当前位置:   article > 正文

【刷题之路】LeetCode 232. 用栈实现队列_请你仅用两个栈实现先入先出队列c语言

请你仅用两个栈实现先入先出队列c语言

一、题目描述

原题连接: 232. 用栈实现队列
题目描述:
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:

  1. void push(int x) 将元素 x 推到队列的末尾
  2. int pop() 从队列的开头移除并返回元素
  3. int peek() 返回队列开头的元素
  4. boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  1. 你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  2. 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例 1:

输入:
[“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

  1. 1 <= x <= 9
  2. 最多调用 100 次 push、pop、peek 和 empty
  3. 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

进阶:
你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。

二、解题

1、图解主要思路

其实这题和[225. 用队列实现栈]一样,也是属于栈和队列的应用练习题。
所以其实现思路也是相似的,也是通过返回出队元素的方法来模拟出队列的先进先出结构。
其实这一题也是要用到两个栈,但是我们怎样设计思路呢?
其实我们可以对这两个栈的任务进行分工,一个栈专门用来入队,一个栈专门用来出栈:
在这里插入图片描述
那么对于入队操作,我们就可以直接将数据压入到“入队栈”中:
在这里插入图片描述
而对于出队操作,我们就要先检查“出队栈”是否为空,如果为空的话,就要先将“入队栈”中的数据逐一弹出再压入“出队栈”中:
在这里插入图片描述
这其实就等于将“入队栈”中的数据给逆置了,然后我们就可以将“出队栈”的栈顶数据弹栈,最后返回了。
而在后面的出队操作中,如果“出队栈”不为空,我们就可以直接将“出队栈”的栈顶数据弹出并返回了,直到“出队栈”再次为空,那我们就要再次将“入队栈”清空。
不知道大家有没有注意到,其实我们可以把这两个栈形象的看成一个一根U型管的两端:
在这里插入图片描述
我们可以假设“入队栈”的数据是通过下边的弯的管道“流”过去的,这样即完成了“入队栈”的数据的逆序,并且也符合了先进先出。

好了,其实这一道题的主要实现逻辑就是这一点,剩下来的就是结构的问题了。

2、先实现栈

同样的,因为我这里选择的是C语言实现,所以我们要先把栈实现一下,我这里就直接赋值我之前的【数据结构】简单到有摸鱼负罪感的栈的实现中的栈的实现了:

// 重定义数据类型
typedef int DataType;

// 定义栈结构
typedef struct stack {
	DataType* data;
	int top;
	int capacity;
} Stack;

// 栈的初始化
void StackInit(Stack* ps);

// 压栈
void StackPush(Stack* ps, DataType x);
// 弹栈
void StackPop(Stack* ps);
// 返回栈顶数据
DataType StackTop(Stack* ps);
// 返回栈的数据个数
int StackSize(Stack* ps);
// 判断栈是否为空
bool StackEmpty(Stack* ps);
// 栈的销毁
void DestroyStack(Stack* ps);

// 栈的初始化
void StackInit(Stack* ps) {
	assert(ps);
	ps->data = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

// 压栈
void StackPush(Stack* ps, DataType x) {
	assert(ps);
	// 检查是否需要增容
	if (ps->top == ps->capacity) {
		int newCapacity = ps->capacity == 0 ? 10 : ps->capacity * 2;
		DataType* temp = (DataType*)realloc(ps->data, newCapacity * sizeof(DataType));
		if (NULL == temp) {
			perror("ralloc fail!\n");
			exit(-1);
		}
		ps->data = temp;
		ps->capacity = newCapacity;
	}
	ps->data[ps->top] = x;
	ps->top++;
}

// 弹栈
void StackPop(Stack* ps) {
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

// 返回栈顶数据
DataType StackTop(Stack* ps) {
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->data[ps->top - 1];
}

// 返回栈的数据个数
int StackSize(Stack* ps) {
	assert(ps);
	assert(ps->top >= 0);
	return ps->top;
}

// 判断栈是否为空
bool StackEmpty(Stack* ps) {
	assert(ps);
	return ps->top == 0;
}

// 栈的销毁
void DestroyStack(Stack* ps) {
	assert(ps);
	free(ps->data);
	ps->data = NULL;
	ps->top = 0;
	ps->capacity = 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

3、实现各个接口

3.1、初始化接口

在myQueueCreate接口中,我们不仅要将队列开辟好,同时也要将队列中的,栈给初始化了,初始化就直接调用栈中实现的接口即可。

typedef struct {
    Stack headStack; // 队头栈,用于出队
    Stack tailStack; // 队尾栈,用于入队
} MyQueue;

MyQueue* myQueueCreate() {
    MyQueue *queue = (MyQueue*)malloc(sizeof(MyQueue));
    if (NULL == queue) {
        perror("malloc fail!\n");
        exit(-1);
    }
    StackInit(&queue->headStack);
    StackInit(&queue->tailStack);
    return queue;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.2、入队接口

入队没什么好说的,统一将数据压入到“入队栈”中:

void myQueuePush(MyQueue* obj, int x) {
    // 对于入队,我们可以统一把数据压入队尾栈
    StackPush(&obj->tailStack, x);
}
  • 1
  • 2
  • 3
  • 4

3.3、出队接口

出队就要分两种情况,如果“出队栈”不为空,就直接将“出队栈”的栈顶元素弹出即可,如果为空,这要将“入队栈”清空,将数据压入到“出队栈”中。

int myQueuePop(MyQueue* obj) {
    int returnVal = 0;
    if (!StackEmpty(&obj->headStack)) {
        returnVal = StackTop(&obj->headStack);
        StackPop(&obj->headStack);
    } else {
        // 先将队尾栈的数据全部弹出,压到队尾栈
        while (!StackEmpty(&obj->tailStack)) {
            int temp = StackTop(&obj->tailStack);
            StackPop(&obj->tailStack);
            StackPush(&obj->headStack, temp);
        }
        returnVal = StackTop(&obj->headStack);
        StackPop(&obj->headStack);
    }
    return returnVal;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

要注意的是,在真正弹栈之前需要先将栈顶元素的值保存,最后返回。

3.4、取队头接口

取队头其实和出队是一样的逻辑,只不过不用将数据真正弹栈:

int myQueuePeek(MyQueue* obj) {
    // 这个操作其实也和Pop一样,如果队头栈为空,就要先队尾栈的数据全都压入到队头栈,返回栈顶
    if (StackEmpty(&obj->headStack)) {
        // 先将队尾栈的数据全部弹出,压到队尾栈
        while (!StackEmpty(&obj->tailStack)) {
            int temp = StackTop(&obj->tailStack);
            StackPop(&obj->tailStack);
            StackPush(&obj->headStack, temp);
        }
    }
    return StackTop(&obj->headStack);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.5、判空接口

判空的话,我们返回两个栈是否同时为空的判断结果即可:

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->headStack) && StackEmpty(&obj->tailStack);
}
  • 1
  • 2
  • 3

3.6、释放接口

如果队列不为空的话,还要将队列里的两个栈先释放掉,如果为空,我们就直接释放掉队列即可。

void myQueueFree(MyQueue* obj) {
    if (!myQueueEmpty(obj)) {
        // 如果队列不为空,我们就要先将栈销毁
        DestroyStack(&obj->headStack);
        DestroyStack(&obj->tailStack);
    }
    free(obj);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

总结起来,这一题其实和225. 用队列实现栈是一样的,也是逻辑复杂,结构稍复杂一点儿。但也是值得我们多练习几遍,以加深对栈和队列的理解。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/671739
推荐阅读
相关标签
  

闽ICP备14008679号