面试题 03.04. 化栈为队
实现一个MyQueue类,该类用两个栈来实现一个队列。
.
.
示例:
.
MyQueue queue = new MyQueue();
.
queue.push(1); queue.push(2); queue.peek(); // 返回 1 queue.pop(); //
返回 1 queue.empty(); // 返回 false
.
说明:
.
你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size 和 is empty
操作是合法的。 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
.
来源:力扣(LeetCode)《程序员面试金典(第六版)》
难度:简单
链接:https://leetcode-cn.com/problems/implement-queue-using-stacks-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:队列是一端进行删除(即出队),另一端进行插入(即入队)的数据结构。
题目要求2个栈空间,那么我们使一个限制为只能进行pop操作,一个只能进行push操作,模仿队列的入队和出队操作。
pop栈我们命名为frontStack,push栈我们命名为rearStack,接下来的代码实现中有详细的注释。
#define MAXSIZE 30
#define OK 1
#define ERR 0
typedef struct Stack
{
int top;
int data[MAXSIZE];
}Stack, * StackPtr;
typedef struct {
Stack* rearStack;
Stack* frontStack;
} MyQueue, * MyQueuePtr;
bool myQueueEmpty(MyQueue* obj);
/** Initialize your data structure here. */
MyQueue* myQueueCreate() {
//初始化队列空间
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
if (!obj)
goto err1;
memset(obj, 0, sizeof(MyQueue));
//初始化push栈空间
obj->rearStack = (Stack*)malloc(sizeof(Stack));
if (!obj->rearStack)
goto err2;
memset(obj->rearStack, 0, sizeof(Stack));
obj->rearStack->top = -1;
//初始化pop栈空间
obj->frontStack = (Stack*)malloc(sizeof(Stack));
if (!obj->frontStack)
goto err3;
memset(obj->frontStack, 0, sizeof(Stack));
obj->frontStack->top = -1;
return obj;
//集中处理malloc失败的情况。
err3:
if (obj->frontStack)
free(obj->frontStack);
err2:
if (obj->rearStack)
free(obj->rearStack);
err1:
if (obj)
free(obj);
return NULL;
}
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
//判断是否队满
if (obj->rearStack->top == MAXSIZE - 1)
return;
//将pop栈的数据转移到push栈,若没有则直接退出
while (obj->frontStack->top != -1)
{
obj->rearStack->data[++obj->rearStack->top] = obj->frontStack->data[obj->frontStack->top--];
}
//将数据push
obj->rearStack->data[++obj->rearStack->top] = x;
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
//判断队列是否为空
if (myQueueEmpty(obj))
return ERR;
//将push栈的数据转移到pop栈,若没有则直接退出
while (obj->rearStack->top != -1)
{
obj->frontStack->data[++obj->frontStack->top] = obj->rearStack->data[obj->rearStack->top--];
}
//pop出去,并返回。
return obj->frontStack->data[obj->frontStack->top--];
}
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
if (myQueueEmpty(obj))
return ERR;
//判断现在数据在哪个栈空间。
if (obj->frontStack->top == -1)
{
return obj->rearStack->data[0]; //在rear栈中,此时rear指向rear栈的top次,front指向0处
}
else
{
return obj->frontStack->data[obj->frontStack->top]; //front栈中,直接返回即可
}
}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
return (obj->frontStack->top == -1 && obj->rearStack->top == -1) ? true : false;
}
//逐一free内存
void myQueueFree(MyQueue* obj) {
free(obj->frontStack);
free(obj->rearStack);
free(obj);
}
面试题09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )来源:力扣(LeetCode)《剑指offer(第二版)》
难度:简单
链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:我们定义2个栈空间,rear和front分别作为其中的栈顶指针。我们限定rear栈空间只能进行push也就是入队,而front栈只能进行pop也就是出队以此来用两个栈来模仿队列。
下面的代码关键地方都给予了注释。
#define MAXSIZE 300
#define REAR 1
#define FRONT 0
typedef struct CQueue
{
//设置2个数组,即2个栈空间,分别定义2个宏REAR,FRONT现性的指明。
int data[2][MAXSIZE];
int front, rear;
} CQueue;
CQueue* cQueueCreate() {
CQueue* obj = (CQueue*)malloc(sizeof(CQueue));
if (!obj)
return NULL;
memset(obj, 0, sizeof(CQueue));
obj->rear = obj->front = -1; //rear和front分别为一个栈空间的栈顶指针,初始化指向栈底。
return obj;
}
void cQueueAppendTail(CQueue* obj, int value) {
//判断是否满队
if (obj->rear == MAXSIZE - 1 || obj->front == MAXSIZE - 1)
return;
//如果数据在front栈,则转移数据到rear栈中
while (obj->front != -1)
{
obj->data[REAR][++obj->rear] = obj->data[FRONT][obj->front--];
}
obj->data[REAR][++obj->rear] = value;
}
int cQueueDeleteHead(CQueue* obj) {
if (obj->rear == obj->front)
return -1;
//如果数据在rear栈,则转移数据到front栈中
while (obj->rear != -1)
obj->data[FRONT][++obj->front] = obj->data[REAR][obj->rear--];
return obj->data[FRONT][obj->front--];
}
void cQueueFree(CQueue* obj) {
free(obj);
}
1047. 删除字符串中的所有相邻重复项
编辑于:2020年3月17日22:10:39
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:“abbaca” 输出:“ca” 解释: 例如,在 “abbaca” 中,我们可以删除 “bb”
由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa”
可以执行重复项删除操作,所以最后的字符串为 “ca”。扫描二维码关注公众号,回复: 10530733 查看本文章提示:
1 <= S.length <= 20000 S 仅由小写英文字母组成。
来源:力扣(LeetCode)
难度:简单
链接:https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路1我最初的思路非常简单。维护一个栈,不断将数据读入栈中,如果栈中数据有重复的相邻数据则出栈。
感悟:但是写完出来,有点不满意,看见了一个大佬的提醒,顿悟了。感觉优化了下代码。
思路2:我们可以利用遍历下标总是不小于栈底top作为下标的值这个性质将S指向的内存空间作为栈空间。
原因很简单一开始栈空间膨胀和遍历速度是相同的,但是我们不断入栈的途中会有出栈,这就使得栈膨胀的速度永远不可能超过遍历的速度。
所谓遍历就是集合中所有元素都访问一遍。也就是说top只能操作我们访问过的数据,而不能没有操作我们未访问过的数据,因此这并不影响遍历。
//最初的版本
#define MAXSIZE 20000
char* removeDuplicates(char* S) {
//设立栈
int top = -1;
char data[MAXSIZE] = { 0 };
for (int i = 0; S[i]; i++)
{
//判断栈是否满。加1是为了预留'\0'字符
if (top + 1 == MAXSIZE - 1)
return NULL;
data[++top] = S[i]; //字符串s字符逐一入栈
if (top > 0 && data[top] == data[top - 1]) //查找是否有重复,若有出栈
top -= 2;
if(top >= 0)
S[top] = data[top];
}
S[top + 1] = '\0';
return S;
}
//优化后的C代码
char* removeDuplicates(char* S) {
int top = 0;
for (int read = 0; S[read]; read++, top++)
{
S[top] = S[read]; //字符串s字符逐一入栈
if (top > 0 && S[top] == S[top - 1]) //查找是否有重复,若有出栈
top -= 2;
}
S[top] = '\0';
return S;
}