数据结构——栈和队列oj练习
225. 用队列实现栈 - 力扣(LeetCode)
这一题需要我们充分理解队列和栈的特点。
队列:队头出数据,队尾入数据。
栈:栈顶出数据和入数据。
我们可以用两个队列实现栈,在这过程中,我们总要保持其中一个队列为空。如果我们出栈,也就是要将栈顶元素弹出,就相当于对非空队列进行操作,就是要把非空队列的队尾元素弹出队列。但是队列的队尾是不能出数据的,想要让队尾数据出队列,就要让这个数据到达队头,同时我们还要保留其他的数据,就需要用到另一个队列来保存。
所以说,我们要用队列模拟出栈过程,就要把非空队列中的数据不断弹出放到另一个队列中,直到非空队列中的数据个数变成1,保留下这个数据的值,再将这个数据从队列中弹出。
对于取栈顶元素过程,大部分代码可以复用出栈的代码。或者我们可以发现栈顶元素就是非空队列的队尾元素,我们直接取出非空队列的队尾元素即可。
对于入栈过程,对于栈,我们直接将数据放到非空队列的队尾即可。
typedef int Qdatatype;typedef struct QueueNode
{Qdatatype data;struct QueueNode* next;
}QueueNode;//队列的结构定义:
typedef struct Queue
{QueueNode* phead;//队头QueueNode* ptail;//队尾int size;//队列中有效数据个数
}Queue;//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}//销毁
void QueueDesTroy(Queue* pq)
{QueueNode* pcur = pq->phead;while (pcur){QueueNode* pnext = pcur->next;free(pcur);pcur = pnext;}pq->phead = pq->ptail = NULL;pq->size = 0;
}//入队列
//入队列是在队尾入的,所以入队列相当于链表的尾插
void QueuePush(Queue* pq, Qdatatype x)
{assert(pq);//申请新的节点空间QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));newnode->next = NULL;newnode->data = x;//尾插//如果此时队列中一个元素都没有if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else//队列本来就有元素{pq->ptail->next = newnode;pq->ptail = newnode;}(pq->size)++;
}//判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}//出队列
//出队列是在队头出的,相当于链表的头删
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//如果链表中只有一个元素if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else//直接头删{QueueNode* newhead = pq->phead->next;free(pq->phead);pq->phead = newhead;}(pq->size)--;
}//取队头数据
Qdatatype QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
Qdatatype QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}//=========前面为队列的实现===========
//栈的结构
typedef struct
{Queue q1;Queue q2;
} MyStack;//栈的初始化
MyStack* myStackCreate() {MyStack* stack=(MyStack*)malloc(sizeof(MyStack));QueueInit(&(stack->q1));QueueInit(&(stack->q2));return stack;
}
//入栈
void myStackPush(MyStack* obj, int x)
{Queue* empty=&(obj->q1);Queue* nonempty=&(obj->q2);if(QueueEmpty(&(obj->q2))){empty=&(obj->q2);nonempty=&(obj->q1);}QueuePush(nonempty,x);
}
//出栈
int myStackPop(MyStack* obj)
{Queue* empty=&(obj->q1);Queue* nonempty=&(obj->q2);if(QueueEmpty(&(obj->q2))){empty=&(obj->q2);nonempty=&(obj->q1);}while(QueueSize(nonempty)!=1)//让非空队列中的元素不停地出栈直到栈中只有一个元素{//将元素放到原来是空的队列中QueuePush(empty,QueueFront(nonempty));//出队列QueuePop(nonempty);}int ret=QueueFront(nonempty);QueuePop(nonempty);return ret;
}
//取栈顶元素:两种方法
//方法一:
int myStackTop(MyStack* obj)
{Queue* empty=&(obj->q1);Queue* nonempty=&(obj->q2);if(QueueEmpty(&(obj->q2))){empty=&(obj->q2);nonempty=&(obj->q1);}while(QueueSize(nonempty)!=1)//让非空队列中的元素不停地出栈直到栈中只有一个元素{//将元素放到原来是空的队列中QueuePush(empty,QueueFront(nonempty));//出队列QueuePop(nonempty);}int ret=QueueFront(nonempty);QueuePush(empty,ret);QueuePop(nonempty);return ret;
}
//方法二:
int myStackTop(MyStack* obj)
{Queue* empty=&(obj->q1);Queue* nonempty=&(obj->q2);if(QueueEmpty(&(obj->q2))){empty=&(obj->q2);nonempty=&(obj->q1);}return QueueBack(nonempty);
}//判断栈是否为空
bool myStackEmpty(MyStack* obj) {return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));
}
//销毁
void myStackFree(MyStack* obj)
{QueueDesTroy(&(obj->q1));QueueDesTroy(&(obj->q2));free(obj);obj=NULL;
}
232. 用栈实现队列 - 力扣(LeetCode)
我们是可以用两个栈实现队列的结构的。具体实现方法如下:
我们定义两个栈,一个栈A是专门用来插入数据的,另一个栈B是专门用来出数据的。当我们要插入数据的时候,直接往A中插入即可,当我们要删除数据的时候,要先检查B是否为空,如果B为空,就讲A中的数据全部放入B中,如果B不为空,就直接对B进行出栈操作。
typedef int stdatatype;//定义栈的结构:
typedef struct Stack
{stdatatype* arr;int top;//指向栈顶的后一个位置,也可以表示有效数据个数int capacity;//栈中的最大容量
}ST;
//初始化
void STInit(ST* ps)
{assert(ps);//防止后续空指针解引用ps->arr = NULL;ps->top = 0;//如果使top=0,那么top指向栈顶元素的后一个位置,//如果想让top指向栈顶元素,就要让top初始化为-1ps->capacity = 0;
}//销毁
void STDesTroy(ST* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->capacity = ps->top = 0;
}//入栈——栈顶
void STPush(ST* ps, stdatatype x)
{assert(ps);//先判断是否需要扩容if (ps->top == ps->capacity){//需要扩容int newcapacity = ps->capacity > 0 ? 2 * ps->capacity : 4;stdatatype*tmp = (stdatatype*)realloc(ps->arr, newcapacity * sizeof(stdatatype));ps->arr = tmp;ps->capacity = newcapacity;}ps->arr[(ps->top)++] = x;
}//栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//出栈———栈顶
void STPop(ST* ps)
{assert(ps);//出栈之前先判空assert(ps->top);ps->top--;
}//取栈顶元素
stdatatype STTop(ST* ps)
{assert(ps);//去栈顶元素之前先判空assert(ps->top);return ps->arr[ps->top - 1];
}//获取栈中有效元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}//================以上全是栈的实现=============
typedef struct
{ST pushst;ST popst;
} MyQueue;MyQueue* myQueueCreate()
{MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));STInit(&(obj->pushst));STInit(&(obj->popst));return obj;
}
void myQueuePush(MyQueue* obj, int x)
{STPush(&(obj->pushst),x);
}int myQueuePop(MyQueue* obj)
{if(!STEmpty(&(obj->popst))){int ret=STTop(&(obj->popst));STPop(&(obj->popst));return ret;}else{while(!STEmpty(&(obj->pushst))){STPush(&(obj->popst),STTop(&(obj->pushst)));STPop(&(obj->pushst));}int ret=STTop(&(obj->popst));STPop(&(obj->popst));return ret;}
}int myQueuePeek(MyQueue* obj)
{if(!STEmpty(&(obj->popst))){int ret=STTop(&(obj->popst));return ret;}else{while(!STEmpty(&(obj->pushst))){STPush(&(obj->popst),STTop(&(obj->pushst)));STPop(&(obj->pushst));}int ret=STTop(&(obj->popst));return ret;}}bool myQueueEmpty(MyQueue* obj)
{return STEmpty(&(obj->pushst)) && STEmpty(&(obj->popst));
}void myQueueFree(MyQueue* obj)
{STDesTroy(&(obj->pushst));STDesTroy(&(obj->popst));free(obj);
}
622. 设计循环队列 - 力扣(LeetCode)
//我们用数组的结构设计循环队列
//这一题要善于运用模除的运算从而达到利用旧空间的效果
typedef struct {int front;int rear;int*arr;int capacity;
} MyCircularQueue;bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->rear==obj->front;
}bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->rear+1)%(obj->capacity)==obj->front;
}MyCircularQueue* myCircularQueueCreate(int k)
{MyCircularQueue*cirque=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));cirque -> front=0;//指向 队头元素(即下一个要出队的元素)。cirque -> rear= 0;//rear指向的是队尾元素的下一个位置cirque -> arr=(int*)malloc(sizeof(int)*(k+1));//多开辟一个空间以区分队列满和空的状态cirque->capacity=k+1;return cirque;
}
//尾插
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{if(!myCircularQueueIsFull(obj)){obj->arr[(obj->rear)++]=value;obj->rear=(obj->rear)%(obj->capacity);return true;}return false;
}
//头删
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{if(myCircularQueueIsEmpty(obj)){return false;}obj->front= ( obj->front+1)%(obj->capacity);return true;
}int myCircularQueueFront(MyCircularQueue* obj)
{if(myCircularQueueIsEmpty(obj)){return -1;}return obj->arr[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj)
{if(myCircularQueueIsEmpty(obj)){return -1;}return obj->arr[(obj->rear-1+obj->capacity)%(obj->capacity)];
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->arr);free(obj);
}
解析:图中我们解释了如何插入和删除元素
设计循环双端队列
双端队列(Deque,Double-Ended Queue)是一种支持 队头(front)和队尾(rear)两端高效插入和删除 的数据结构。
上一题我们实现了循环队列的实现,有了上一题的基础,我们就能利用循环队列来实现双端队列:
//双端队列中:
//front 直接指向当前队头元素(即下一个从队头出队的元素)。
//rear 指向队尾的下一个空位(即下一个插入队尾的位置)。
//在队头插入数据时,要先改变front指向;同理,在队尾插入元素以后,要改变rear的指向
//然而,插入元素后不能同时让front和rear同时递增或同时递减
//我们就使在队头插入数据后,front--,队尾插入数据后rear++typedef struct {int front;int rear;int capacity;int*arr;
} MyCircularDeque;MyCircularDeque* myCircularDequeCreate(int k)
{MyCircularDeque* obj=(MyCircularDeque*)malloc(sizeof(MyCircularDeque));obj->front=obj->rear=0;obj->arr=(int*)malloc(sizeof(int)*(k+1));obj->capacity=k+1;return obj;
}bool myCircularDequeIsEmpty(MyCircularDeque* obj)
{return obj->rear==obj->front;
}bool myCircularDequeIsFull(MyCircularDeque* obj)
{return (obj->rear+1)%(obj->capacity)==obj->front;
}
//头插
bool myCircularDequeInsertFront(MyCircularDeque* obj, int value)
{if(myCircularDequeIsFull(obj)){return false;}//front指向的是队头元素,所以我们再进行头插时,需要先改变队头指向obj->front=(obj->front-1+obj->capacity)%(obj->capacity);obj->arr[obj->front]=value;return true;
}bool myCircularDequeInsertLast(MyCircularDeque* obj, int value)
{if(myCircularDequeIsFull(obj)){return false;}obj->arr[obj->rear]=value;//改变rear指向obj->rear=(obj->rear+1)%(obj->capacity);return true;
}bool myCircularDequeDeleteFront(MyCircularDeque* obj)
{if(myCircularDequeIsEmpty(obj)){return false;}//插入元素是让front--,那么删除元素就要让front++obj->front=(obj->front+1)%(obj->capacity);return true;
}bool myCircularDequeDeleteLast(MyCircularDeque* obj)
{if(myCircularDequeIsEmpty(obj)){return false;}obj->rear=(obj->rear-1+obj->capacity)%(obj->capacity);return true;
}int myCircularDequeGetFront(MyCircularDeque* obj)
{if(myCircularDequeIsEmpty(obj)){return -1;}return obj->arr[obj->front];
}int myCircularDequeGetRear(MyCircularDeque* obj)
{if(myCircularDequeIsEmpty(obj)){return -1;}return obj->arr[(obj->rear-1+obj->capacity)%(obj->capacity)];
}void myCircularDequeFree(MyCircularDeque* obj)
{free(obj->arr);obj->arr=NULL;obj->front=obj->rear=obj->capacity=0;free(obj);obj=NULL;
}