本篇博客会讲解力扣“225. 用队列实现栈”的解题思路,这是题目链接。
先来审题:
以下是输出示例:
以下是提示和进阶:
这道题有2种思路,分别使用2个和1个队列来实现栈。
准备工作
先来实现队列。由于本篇博客的重点不是队列应该如何实现,所以这里直接上代码:
// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front; // 队头
QNode* tail; // 队尾
int size; // 有效数据个数
}Queue;
// 初始化队列
void QueueInit(Queue* pq);
// 队尾入队列
void QueuePush(Queue* pq, QDataType data);
// 队头出队列
void QueuePop(Queue* pq);
// 获取队列头部元素
QDataType QueueFront(Queue* pq);
// 获取队列队尾元素
QDataType QueueBack(Queue* pq);
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
// 检测队列是否为空
bool QueueEmpty(Queue* pq);
// 销毁队列
void QueueDestroy(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
// 定义哨兵位
QNode* head = (QNode*)malloc(sizeof(QNode));
if (head == NULL)
{
perror("malloc fail");
return;
}
head->data = 0;
head->next = NULL;
pq->front = head;
pq->tail = head;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType data)
{
assert(pq);
// 创建新结点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = data;
newnode->next = NULL;
// 链接在尾部
pq->tail->next = newnode;
pq->tail = newnode;
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
if (pq->size == 1)
{
// 删除数据
free(pq->tail);
pq->front->next = NULL;
pq->tail = pq->front;
}
else
{
// 至少2个数据
QNode* first = pq->front->next;
QNode* second = first->next;
// 删除数据
free(first);
first = NULL;
// 链接
pq->front->next = second;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
return pq->front->next->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
void QueueDestroy(Queue* pq)
{
QNode* del = pq->front;
while (del)
{
QNode* next = del->next;
free(del);
del = next;
}
pq->front = NULL;
pq->tail = NULL;
pq->size = 0;
}
思路1
我们可以使用2个队列来实现栈。
首先定义结构:
typedef struct {
Queue q1;
Queue q2;
} MyStack;
初始化时,分别对2个队列初始化即可:
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
// 初始化2个队列
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
假设我们让一个队列存数据,另一个队列空着,入数据时,应该往有数据的队列插入数据。
void myStackPush(MyStack* obj, int x) {
// 往有数据的队列中入数据,若2个队列都为空,则随便
if (QueueEmpty(&obj->q1))
QueuePush(&obj->q2, x);
else
QueuePush(&obj->q1, x);
}
问题来了,如何删除数据呢?
首先栈非空才能删除数据,所以需要断言一下。
假设栈非空,如果我们每次都是往有数据的队列插入数据,一定有1个队列为空,另一个队列非空,我们先使用判断语句,锁定队列中哪个为空,则另一个非空。
假设队列中依次插入了[1 2 3 4 5],如果要出队列,根据队列“先进先出”的特性,只能出1,但是作为栈,应该要出5,应该如何操作呢?
我们先出1,插入到空队列中,相当于把1“转移”了。
接着转移2。
继续转移,直到上面的队列只剩1个元素。
删除并返回5即可。
此时,“栈顶”元素5就被删除了。
int myStackPop(MyStack* obj) {
// 非空才能删除
assert(!myStackEmpty(obj));
// 判断队列是否为空
Queue* emptyQueue = &obj->q1;
Queue* nonEmptyQueue = &obj->q2;
if (QueueEmpty(&obj->q2))
{
emptyQueue = &obj->q2;
nonEmptyQueue = &obj->q1;
}
// 把非空队列中的数据插入到空队列中
while (QueueSize(nonEmptyQueue) > 1)
{
// 转移数据
QueuePush(emptyQueue, QueueFront(nonEmptyQueue));
QueuePop(nonEmptyQueue);
}
// 取非空队列最后一个数据并删除
int ret = QueueFront(nonEmptyQueue);
QueuePop(nonEmptyQueue);
return ret;
}
有数据的队列中,队尾的元素就是“栈顶”元素。注意取“栈顶”元素之前要判断非空。
int myStackTop(MyStack* obj) {
// 非空才能取数据
assert(!myStackEmpty(obj));
// 取非空队列的队尾
if (QueueEmpty(&obj->q1))
return QueueBack(&obj->q2);
else
return QueueBack(&obj->q1);
}
2个队列都为空,则栈为空。
bool myStackEmpty(MyStack* obj) {
// 2个队列都为空,则栈为空
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
最后free即可。
void myStackFree(MyStack* obj) {
// 销毁2个队列
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
obj = NULL;
}
完整代码如下:
// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front; // 队头
QNode* tail; // 队尾
int size; // 有效数据个数
}Queue;
// 初始化队列
void QueueInit(Queue* pq);
// 队尾入队列
void QueuePush(Queue* pq, QDataType data);
// 队头出队列
void QueuePop(Queue* pq);
// 获取队列头部元素
QDataType QueueFront(Queue* pq);
// 获取队列队尾元素
QDataType QueueBack(Queue* pq);
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
// 检测队列是否为空
bool QueueEmpty(Queue* pq);
// 销毁队列
void QueueDestroy(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
// 定义哨兵位
QNode* head = (QNode*)malloc(sizeof(QNode));
if (head == NULL)
{
perror("malloc fail");
return;
}
head->data = 0;
head->next = NULL;
pq->front = head;
pq->tail = head;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType data)
{
assert(pq);
// 创建新结点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = data;
newnode->next = NULL;
// 链接在尾部
pq->tail->next = newnode;
pq->tail = newnode;
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
if (pq->size == 1)
{
// 删除数据
free(pq->tail);
pq->front->next = NULL;
pq->tail = pq->front;
}
else
{
// 至少2个数据
QNode* first = pq->front->next;
QNode* second = first->next;
// 删除数据
free(first);
first = NULL;
// 链接
pq->front->next = second;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
return pq->front->next->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
void QueueDestroy(Queue* pq)
{
QNode* del = pq->front;
while (del)
{
QNode* next = del->next;
free(del);
del = next;
}
pq->front = NULL;
pq->tail = NULL;
pq->size = 0;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
// 初始化2个队列
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
void myStackPush(MyStack* obj, int x) {
// 往有数据的队列中入数据,若2个队列都为空,则随便
if (QueueEmpty(&obj->q1))
QueuePush(&obj->q2, x);
else
QueuePush(&obj->q1, x);
}
bool myStackEmpty(MyStack* obj);
int myStackPop(MyStack* obj) {
// 非空才能删除
assert(!myStackEmpty(obj));
// 判断队列是否为空
Queue* emptyQueue = &obj->q1;
Queue* nonEmptyQueue = &obj->q2;
if (QueueEmpty(&obj->q2))
{
emptyQueue = &obj->q2;
nonEmptyQueue = &obj->q1;
}
// 把非空队列中的数据插入到空队列中
while (QueueSize(nonEmptyQueue) > 1)
{
// 转移数据
QueuePush(emptyQueue, QueueFront(nonEmptyQueue));
QueuePop(nonEmptyQueue);
}
// 取非空队列最后一个数据并删除
int ret = QueueFront(nonEmptyQueue);
QueuePop(nonEmptyQueue);
return ret;
}
int myStackTop(MyStack* obj) {
// 非空才能取数据
assert(!myStackEmpty(obj));
// 取非空队列的队尾
if (QueueEmpty(&obj->q1))
return QueueBack(&obj->q2);
else
return QueueBack(&obj->q1);
}
bool myStackEmpty(MyStack* obj) {
// 2个队列都为空,则栈为空
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
// 销毁2个队列
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
obj = NULL;
}
通过喽。
思路2
其实,要实现栈,1个队列足矣。
结构的定义如下:
typedef struct {
Queue q;
} MyStack;
初始化:
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&obj->q);
return obj;
}
直接入数据:
void myStackPush(MyStack* obj, int x) {
QueuePush(&obj->q, x);
}
重点来了,怎么删除数据呢?前面2个队列实现栈的思路中,采取的是2个队列的数据相互转移的思路。如果只有1个队列,就自己给自己转移数据即可。比如:依次插入[1 2 3 4 5],我们要删除“栈顶”的5,就把前n-1个数据从队头转移到队尾,原来队尾的5就会跑到队头,删除它即可。
出1,再插入1(即转移1):
再转移2:
同理转移3、4:
最后删除并返回5:
代码如下:
int myStackPop(MyStack* obj) {
// 检查非空
assert(!myStackEmpty(obj));
// 把队头的size个数据转移到队尾
int count = QueueSize(&obj->q) - 1;
while (count--)
{
QueuePush(&obj->q, QueueFront(&obj->q));
QueuePop(&obj->q);
}
// 此时原来在队尾的数据跑到了队头,删除即可
int ret = QueueFront(&obj->q);
QueuePop(&obj->q);
return ret;
}
“栈顶”元素就是队尾元素:
int myStackTop(MyStack* obj) {
// 检查非空
assert(!myStackEmpty(obj));
return QueueBack(&obj->q);
}
队列为空,则栈为空:
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q);
}
最后释放空间:
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q);
free(obj);
obj = NULL;
}
完整的代码如下:
// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front; // 队头
QNode* tail; // 队尾
int size; // 有效数据个数
}Queue;
// 初始化队列
void QueueInit(Queue* pq);
// 队尾入队列
void QueuePush(Queue* pq, QDataType data);
// 队头出队列
void QueuePop(Queue* pq);
// 获取队列头部元素
QDataType QueueFront(Queue* pq);
// 获取队列队尾元素
QDataType QueueBack(Queue* pq);
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
// 检测队列是否为空
bool QueueEmpty(Queue* pq);
// 销毁队列
void QueueDestroy(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
// 定义哨兵位
QNode* head = (QNode*)malloc(sizeof(QNode));
if (head == NULL)
{
perror("malloc fail");
return;
}
head->data = 0;
head->next = NULL;
pq->front = head;
pq->tail = head;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType data)
{
assert(pq);
// 创建新结点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = data;
newnode->next = NULL;
// 链接在尾部
pq->tail->next = newnode;
pq->tail = newnode;
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
if (pq->size == 1)
{
// 删除数据
free(pq->tail);
pq->front->next = NULL;
pq->tail = pq->front;
}
else
{
// 至少2个数据
QNode* first = pq->front->next;
QNode* second = first->next;
// 删除数据
free(first);
first = NULL;
// 链接
pq->front->next = second;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
return pq->front->next->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
// 判断非空
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
void QueueDestroy(Queue* pq)
{
QNode* del = pq->front;
while (del)
{
QNode* next = del->next;
free(del);
del = next;
}
pq->front = NULL;
pq->tail = NULL;
pq->size = 0;
}
typedef struct {
Queue q;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&obj->q);
return obj;
}
void myStackPush(MyStack* obj, int x) {
QueuePush(&obj->q, x);
}
bool myStackEmpty(MyStack* obj);
int myStackPop(MyStack* obj) {
// 检查非空
assert(!myStackEmpty(obj));
// 把队头的size个数据转移到队尾
int count = QueueSize(&obj->q) - 1;
while (count--)
{
QueuePush(&obj->q, QueueFront(&obj->q));
QueuePop(&obj->q);
}
// 此时原来在队尾的数据跑到了队头,删除即可
int ret = QueueFront(&obj->q);
QueuePop(&obj->q);
return ret;
}
int myStackTop(MyStack* obj) {
// 检查非空
assert(!myStackEmpty(obj));
return QueueBack(&obj->q);
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q);
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q);
free(obj);
obj = NULL;
}
通过!
总结
由于队列“先进先出”的性质,出自己的数据再插入,数据就从队头跑到了队尾,所以可以只使用1个队列实现栈。但是如果使用栈实现队列,就必须使用2个栈才行,因为栈“后进先出”的性质,如果出自己的数据再插入,数据还是在栈顶。
感谢大家的阅读!