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;
}