文章目录
1. 二叉树的遍历
看题名毫无疑问这肯定也是二叉树的一道题,但是我既然在栈中刷到,那么意味着用栈也是可以实现的。
(1)中序
方法一: 运用递归实现的。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
// //构造一个栈出来
// int* stack = (int*)malloc(sizeof(int) * 100);
// int top = 0;
// int gettop;
void Inorder(struct TreeNode* T,int* ans,int* size)
{
if(T == NULL)
{
return;
}
Inorder(T->left,ans,size);
ans[(*size)++] = T -> val;
Inorder(T->right,ans,size);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize)
{
int* ans = (int*)malloc(sizeof(int) * 101);
*returnSize = 0;
Inorder(root,ans,returnSize);
return ans;
}
方法二: 迭代
这个就是利用栈来实现的
- 中序遍历是进行左根右的遍历
- 所以我们先将左节点全部入栈,就跟递归那样,肯定是递归进入所有的左节点。
- 然后发现没有左节点了,将当前栈顶的元素出栈,出栈之后将根节点变成右孩子即可开始遍历右节点了
- 整体思想上还是左根右的思想。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* inorderTraversal(struct TreeNode* root, int* returnSize)
{
int* ans = (int*)malloc(sizeof(int) * 101);
*returnSize = 0;
//构造一个栈出来
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 100);
int top = 0;
struct TreeNode* gettop;
while(root != NULL || top != 0)
{
//将树中的左孩子入栈到底
while(root != NULL)
{
stack[++top] = root;
root = root -> left;
}
//出栈
gettop = stack[top--];
ans[(*returnSize)++] = gettop -> val;
root = gettop -> right;
}
return ans;
}
(2)前序
这里就不去实现递归代码了,本章主要运用栈。
- 前序因为是先根,所以当第一次走到该节的点时候。就该将其入ans数组了。
- 根左,根左,根左……………………
- 还得是左孩子走到底时候,栈顶中元素也就是它的父亲节点了。
- 所以再去看它的右孩子就好了
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
int* ans = (int*)malloc(sizeof(int) * 100);
*returnSize = 0;
//栈
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 100);
int top = 0;
struct TreeNode* gettop;
while(root != NULL || top != 0)
{
//先将根节点入栈 并且入ans数组
while(root != NULL)
{
stack[++top] = root;
ans[(*returnSize)++] = root -> val; //根
root = root ->left; //左
}
gettop = stack[top--];
root = gettop -> right; //右
}
return ans;
}
(3后序
后序遍历更加复杂,这里增加了一个prev 用来记录上一个访问过的
同时也增加了对于右孩子的各种判断
- 同样还是先将root 一直迭代到最左边为止,
- 然后去判断它是否有右孩子,如果没有,就证明当前的就是左节点,将其入ans并且用prev记录下来
- 如果没有就去迭代它的右孩子
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* postorderTraversal(struct TreeNode* root, int* returnSize)
{
int* ans = (int*)malloc(sizeof(int) * 100);
*returnSize = 0;
//栈
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 100);
int top = 0;
struct TreeNode* gettop; //prev root 主要用于这个
struct TreeNode* prev = NULL; //记录前一个访问的节点, 左 右 根
while(root != NULL || top != 0)
{
while(root != NULL)
{
stack[++top] = root;
root = root -> left;
}
gettop = stack[top--];
//能够到达这里 它肯定是没有左孩子的,所以判断右孩子即可:
//(1) 判断它是否有 右孩子, ~ 如果没有证明当前节点是左节点
//(2) 右孩子是否 已经访问过 ~ 证明该节点就是根节点了
if(gettop -> right == NULL || gettop -> right == prev)
{
ans[(*returnSize)++] = gettop -> val;
prev = gettop;
root = NULL;
}
else
{
//当前的getop 就是根节点 去迭代它的右孩子
stack[++top] = gettop;
root = gettop -> right;
}
}
return ans;
}
2.用队列实现栈
用队列实现栈。。。。。。。。是的
- 首先你得有一个队列吧。
- 然后栈中放入两个队列
- 对于push操作,只需要将所对应的x插入到不空的队列中去
- 而对于Pop操作的话,这时候就用到那个空的队列了。将有元素的队列中除去最后一个数据,其余的全部导入到那个空的中去。
- 然后对于Q2进行正常的GetTop操作和Pop操作就好了
下面是代码,代码中仅仅是有题目中函数的操作,C语言中没有队列,需要自己实现。
我这里就不放出来了,我用的是循环队列。循环队列我之前在数据结构章节中有实现,同时也有源码。
循环队列链接
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)
{
//那一个队列不为空 往那一个里面去插入数据
if(!QueueEmpty(obj -> Q1))
{
QueuePush(&obj -> Q1,x);
}
else
{
QueuePush(&obj -> Q2,x);
}
}
int myStackPop(MyStack* obj)
{
//假设队列1 为空的
Queue* empty = &obj -> Q1;
Queue* noempty = &obj -> Q2;
//假设错了 该队列不为空 那就交换一下
if(!QueueEmpty(*empty))
{
empty = &obj -> Q2;
noempty = &obj -> Q1;
}
//将该队列中的最后一个前的数据全部导入到空队列中去
while(abs((noempty->front) - (noempty->rear)) > 1)
{
QueuePush(empty,QueueGetTop(*noempty));
QueuePop(noempty);
}
int top = QueueGetTop(*noempty);
QueuePop(noempty);
return top;
}
int myStackTop(MyStack* obj)
{
if(!QueueEmpty(obj -> Q1))
{
return obj -> Q1.data[obj->Q1.rear-1];
}
else
{
return obj -> Q2.data[obj->Q2.rear-1];
}
}
bool myStackEmpty(MyStack* obj)
{
//两个队列都为空
return QueueEmpty(obj -> Q1) == QueueEmpty(obj -> Q2) ? true : false;
}
void myStackFree(MyStack* obj)
{
free(obj);
}
3.用栈实现队列
额……………………
- 它和上题一样,也得使用两个栈出来
- 这两个栈,有着不一样的意义,
- 第一个栈用于存放入队列,第二个栈用来出队列。
- 看下图:最终从S2出栈不久成了 1 2 3 4 了吗?
- 入队列,就是往第一个栈里面放东西,
- 出队列,就需要判断,如果当前的栈为空,那么就将第一个栈导入进去,如果不为空,那么直接访问栈顶元素就好了。
下面是代码,栈在这里的代码也没有放出来,思路就是这么一个思路,自己动手实现一下栈还是可以的。
typedef struct
{
Stack S1;
Stack S2;
}MyQueue;
MyQueue* myQueueCreate()
{
MyQueue* Q = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&Q -> S1);
StackInit(&Q -> S2);
return Q;
}
void myQueuePush(MyQueue* obj, int x)
{
StackPush(&obj -> S1,x);
}
int myQueuePop(MyQueue* obj)
{
int top = myQueuePeek(obj);
StackPop(&obj -> S2);
return top;
}
int myQueuePeek(MyQueue* obj)
{
//如果第二个栈中没有元素
if(StackEmpty(obj -> S2))
{
//将s1中的元素push入s2中去
while(!StackEmpty(obj -> S1))
{
StackPush(&obj -> S2,StackTop(obj -> S1));
StackPop(&obj -> S1);
}
}
int top = StackTop(obj -> S2);
return top;
}
bool myQueueEmpty(MyQueue* obj)
{
return StackEmpty(obj -> S1) && StackEmpty(obj -> S2) ? true : false;
}
void myQueueFree(MyQueue* obj)
{
free(obj);
}
4.棒球比赛
这个题目中用到了一个atoi的函数。
- 构造一个栈,然后对所给的字符进行判断,如果是数字的话就入栈
- 如果不是数字的话,做其相对应的操作即可。
- 最后将栈中所有数字的和加在一起就好了
int calPoints(char** operations, int operationsSize)
{
int* stack = (int*)malloc(sizeof(int) * (operationsSize+1));
int top = 0,gettop,i;
int ans = 0;
for (i = 0; i < operationsSize; i++)
{
if(operations[i][0] == 'C')
{
stack[top--];
}
else if(operations[i][0] == 'D')
{
gettop = stack[top];
stack[++top] = gettop * 2;
}
else if(operations[i][0] == '+')
{
int num1 = stack[top];
int num2 = stack[top-1];
stack[++top] = num1 + num2;
}
else //分数
{
stack[++top] = atoi(operations[i]);
}
}
while(top != 0)
{
gettop = stack[top--];
ans += gettop;
}
return ans;
}
5.比较含退格的字符串
这道题可以使用双指针来解决,但是在本文中,我使用栈来解决
- 构造两个栈出来。
- 分别对两个字符串进行遍历,如果遇到退格符就出栈,
- 没遇到就入栈
- 最后一边出栈一遍比较即可。
bool backspaceCompare(char* s, char* t)
{
int lens = strlen(s),lent = strlen(t);
char* ss = (int*)malloc(sizeof(int) * (lens+1));
char* st = (char*)malloc(sizeof(char) * (lent+1));
int tops = 0,topt = 0;
char gettops,gettopt;
int i;
for (i = 0; i < lens; i++)
{
if(s[i] == '#' && tops == 0)
{
continue;
}
//入栈与出栈
if(s[i] == '#')
{
ss[tops--];
}
else
{
ss[++tops] = s[i];
}
}
for (i = 0; i < lent; i++)
{
if(t[i] == '#' && topt == 0)
{
continue;
}
//入栈与出栈
if(t[i] == '#')
{
st[topt--];
}
else
{
st[++topt] = t[i];
}
}
while(tops != 0 && topt !=0)
{
gettops = ss[tops--];
gettopt = st[topt--];
if(gettops != gettopt)
{
return false;
}
}
return tops == topt ? true : false;
}
6.删除字符串中的所有相邻重复项
这道题双指针和栈都可以解决,下面我用栈解决的。
- 去遍历一遍字符串,如果栈为空,就将当前的元素入栈,
- 如果栈不为空,那么去判断当前元素和栈顶元素是否相同,
- 如果相同,出栈,否则入栈。
- 最后将这个栈返回即可。
char* removeDuplicates(char* s)
{
int i = 0, len = strlen(s);
//栈
char* stack = (char*)malloc(sizeof(char) * (len + 1));
int top = 0;
char gettop;
//先将第一个数据入栈
while (s[i] != '\0')
{
if (top == 0)
{
stack[++top] = s[i++];
continue;
}
gettop = stack[top];
if (gettop == s[i])
{
top--;
}
else
{
stack[++top] = s[i];
}
i++;
}
stack[++top] = '\0';
return (stack+1);
}
7.商品折扣后的最终价格
这道题可以用暴力双for,直接做出,也可以用单调栈来解答,一下是单调栈的方式
- 需要构造一个哈希表,这个哈希表是当前下标,所对应的该打多少折的下标。
- 额。。。。有点抽象,看图。
为什么不直接在钱下面存放需要减去的前,因为有的测试用例,它可能出现两件商品价格一样,那时候打折就会发生冲突。
- 介绍完哈希表后,就要构造一个单调栈出来了,
- 单调栈其实在刷数组的时候用到过,在下面这道题中有过,我前面数组中也有关于单调栈的解析,不过这次是构造一个单调减的栈,只需要改一下符号即可。
已下是代码:
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* finalPrices(int* prices, int pricesSize, int* returnSize)
{
int* stack = (int*)malloc(sizeof(int) * (pricesSize+1));
int top = 0,gettop,i;
*returnSize = 0;
int hash[501] = {0};
int* ans = (int*)malloc(sizeof(int) * pricesSize);
for (i = 0; i < pricesSize; i++)
{
//栈不为空,且外边的数据比栈顶的小
while(top != 0 && prices[i] <= prices[stack[top]])
{
gettop = stack[top--];
hash[gettop] = i; //记录其下标吧
}
stack[++top] = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ",hash[i]);
}
for (i = 0; i < pricesSize; i++)
{
if(hash[i] != 0)
{
ans[(*returnSize)++] = prices[i] - prices[hash[i]];
}
else
{
ans[(*returnSize)++] = prices[i] - 0;
}
}
return ans;
}
8.整理字符串
- 遍历字符串,挨个入栈,入栈的同时去判断题目中所给的那两个条件,
- 去决定入栈还是出栈,最后将该栈返回即可
代码有点长,其中确实有的许多地方可以简写,使其更整洁,但是思路就是这样子。
题解中还有一块代码,非常简洁,思路还是这个思路。
char * makeGood(char * s)
{
int i,len = strlen(s);
char* stack = (char*)malloc(sizeof(char)* (len + 2));
int top = 0;
char gettop;
for (i = 0; i < len; i++)
{
//栈不为空才可以操作。
if(top != 0)
{
gettop = stack[top];
//如果当前是小写字母,后面是大写字母
if((gettop >= 'a' && gettop <= 'z') && (s[i] >= 'A' && s[i] <= 'Z'))
{
char tmp = tolower(s[i]);
if(tmp == gettop)
{
//出栈
top--;
continue;
}
}
if((gettop >= 'A' && gettop <= 'Z') && (s[i] >= 'a' && s[i] <= 'z'))
{
char tmp = toupper(s[i]);
if(tmp == gettop)
{
//出栈
top--;
continue;
}
}
stack[++top] = s[i];
}
else
{
stack[++top] = s[i];
}
}
stack[++top] = '\0';
return (stack+1);
}
char * makeGood(char * s){
int n = strlen(s);
char *buf = malloc(n + 1);
int top = 0;
buf[top] = s[0];
for (int i = 1; s[i]; i++){
if (top >= 0 && (buf[top] - 'A' == s[i] - 'a' || buf[top] - 'a' == s[i] - 'A')){
top--;
}
else{
buf[++top] = s[i];
}
}
buf[++top] = 0;
return buf;
}
9.文件夹操作日志搜集器
还是利用栈实现,不过我下面就不用栈了,因为它只要返回几次,所以利用栈的top即可
int minOperations(char** logs, int logsSize)
{
int top = 0,i;
for (i = 0; i < logsSize; i++)
{
//不出栈,也不入栈
if(strcmp(logs[i],"./") == 0)
{
continue;
}
else if(strcmp(logs[i],"../") == 0)
{
//栈不为空才去 出栈
if(top != 0)
{
top--;
}
}
else
{
top++;
}
}
return top;
}
10.括号的最大深度
因为题目中所给的字符串中括号的格式一定是正确的,那么直接统计出有多少个左括号就好。
- 遇到左括号入栈,更新最值
- 遇到右括号出栈。
int Max(int x,int y)
{
return x > y ? x : y;
}
int maxDepth(char* s)
{
int top = 0,ans = 0,len = strlen(s),i;
for (i = 0; i < len; i++)
{
//左括号入栈,同时更新最多的次数
if(s[i] == '(')
{
top++;
ans = Max(top,ans);
}
else if (s[i] == ')') //出栈
{
top--;
}
}
return ans;
}
11. 无法吃午餐的学生数量
那么长的题目读不懂的话,看一下例子就懂了。
这道题,有说法。
- 先统计出喜欢吃圆和方各自的人数。
- 然后去遍历那一个三明治数组,所对应的各自减去,唯一要注意的就是,那边人数先为空的话,循环就可以结束了,
- 直接看代码可能有些奇妙,
- 想一想,如果队列中始终一有一个人喜欢吃方的,即使他在最后一个,是不是经过许多次的出队列,入队列,他最终都会上去的。
int countStudents(int* students, int studentsSize, int* sandwiches, int sandwichesSize)
{
int s1 = 0;
for (int i = 0; i < studentsSize; i++)
{
s1 += students[i];
}
int s0 = studentsSize - s1;
for (int i = 0; i < sandwichesSize; i++)
{
if (sandwiches[i] == 0 && s0 > 0)
{
s0--;
} else if (sandwiches[i] == 1 && s1 > 0)
{
s1--;
} else
{
break;
}
}
return s0 + s1;
}
12.删除字串后的字符串最小长度
这道题和前面的好多题几乎是一样的,不一样的只是判断条件而已
int minLength(char * s)
{
int i,len = strlen(s);
char* stack = (char*)malloc(sizeof(char) * (len + 1));
int top = 0;
for (i = 0; i < len; i++)
{
//栈不为空去进行判断操作
if(top != 0 && ((stack[top] == 'A' && s[i] == 'B') || (stack[top] == 'C' && s[i] == 'D')))
{
top--;
}
else
{
stack[++top] = s[i];
}
}
return top;
}
13.图书整理
他这道题就是让咱实现一个队列,题目中说两个书车,既然又出现在栈里面,那应该是让咱用两个栈实现一个队列,就像前面的那一道题一样,这里我就直接实现一个队列过了的。
#define MAX_SIZE 250
typedef struct
{
int data[MAX_SIZE];
int front;
int rear;
} CQueue;
CQueue* cQueueCreate()
{
CQueue* Q = (CQueue*)malloc(sizeof(CQueue));
Q -> front = Q -> rear = 0;
return Q;
}
void cQueueAppendTail(CQueue* obj, int value)
{
if((obj -> rear + 1) % MAX_SIZE == obj -> front)
{
printf("队列满了\n");
exit(-1);
}
obj -> data[(obj -> rear)++] = value;
if(obj -> rear >= MAX_SIZE)
{
obj -> rear = 0;
}
}
bool QueueEmpty(CQueue* obj)
{
return obj -> front == obj -> rear ? true : false;
}
int cQueueDeleteHead(CQueue* obj)
{
if(QueueEmpty(obj))
{
return -1;
}
int tmp = obj -> data[(obj -> front)++];
if(obj -> front >= MAX_SIZE)
{
obj -> front = 0;
}
return tmp;
}
void cQueueFree(CQueue* obj)
{
free(obj);
}
/**
* Your CQueue struct will be instantiated and called as such:
* CQueue* obj = cQueueCreate();
* cQueueAppendTail(obj, value);
* int param_2 = cQueueDeleteHead(obj);
* cQueueFree(obj);
*/
14.最小栈
这道题是构造栈,它这个最小栈的意思就是可以用O(1)的方式去直接找到栈中最小的,如果直接遍历得出结果,还叫什么最小栈。
- 需要两个栈,,一个正常存放栈中每一个元素,
- 另一个则存放越来越小的数,
- 如果要删除的数据,和最小栈中栈顶的数据一样,那么就一起出栈
- 否者只出正常栈中的数据
#define MAX_SIZE 10000
typedef struct
{
int data[MAX_SIZE];
int top;
int mindata[MAX_SIZE]; //记录下来
int mintop;
} MinStack;
/** initialize your data structure here. */
MinStack* minStackCreate()
{
MinStack* s = (MinStack*)malloc(sizeof(MinStack));
s -> top = s -> mintop = 0;
return s;
}
void minStackPush(MinStack* obj, int x)
{
if(obj -> top + 1 >= MAX_SIZE)
{
printf("栈满了\n");
exit(-1);
}
obj -> data[++(obj -> top)] = x;
if(obj -> mintop == 0)
{
obj -> mindata[++(obj -> mintop)] = x;
}
else if (obj -> mindata[obj -> mintop] >= x)
{
obj -> mindata[++(obj -> mintop)] = x;
}
}
void minStackPop(MinStack* obj)
{
if(obj -> data[obj -> top] == obj -> mindata[obj -> mintop])
{
obj -> mintop--;;
}
obj -> top--;
}
int minStackTop(MinStack* obj)
{
return obj -> data[obj -> top];
}
int minStackGetMin(MinStack* obj)
{
return obj -> mindata[obj -> mintop];
}
void minStackFree(MinStack* obj)
{
free(obj);
}
/**
* Your MinStack struct will be instantiated and called as such:
* MinStack* obj = minStackCreate();
* minStackPush(obj, x);
* minStackPop(obj);
* int param_3 = minStackTop(obj);
* int param_4 = minStackGetMin(obj);
* minStackFree(obj);
*/
15 .三合一
额。。。。。这道题的意思就是将一个数组,分成三段然后去实现栈。
typedef struct {
int top[3]; //栈顶指针,注意要分成三个
int size; //单个栈的总长度,需要保存下来出入栈时使用
int* data; //只是用一个数组进行数据存储,长度设为三倍的栈总长
} TripleInOne;
TripleInOne* tripleInOneCreate(int stackSize) {
TripleInOne* obj=(TripleInOne*)malloc(sizeof(TripleInOne));
obj->data=(int*)calloc(3*stackSize,sizeof(int));
obj->size=stackSize;
obj->top[0]=-1;
obj->top[1]=-1;
obj->top[2]=-1;
return obj;
}
void tripleInOnePush(TripleInOne* obj, int stackNum, int value) {
//判断当前栈指针是否到达尾部
if(obj->top[stackNum] == obj->size - 1){
}else{
//这里注意,要把指针后移单独拿出来,因为后续入栈定向是使用的代数式进行
obj->top[stackNum]++;
obj->data[obj->top[stackNum] + stackNum * obj->size] = value;
}
}
int tripleInOnePop(TripleInOne* obj, int stackNum) {
if(obj->top[stackNum] == -1){
return -1;
}else{
//这里注意,要把指针前移单独拿出来,因为出栈定向是使用的代数式进行
int value = obj->data[obj->top[stackNum] + stackNum * obj->size];
obj->top[stackNum]--;
return value;
}
}
int tripleInOnePeek(TripleInOne* obj, int stackNum) {
//根据题意当前栈为空时返回-1
if(obj->top[stackNum] == -1){
return -1;
}
//如果栈不为空,则从数组中取出当前栈顶元素返回
return obj->data[obj->top[stackNum] + stackNum * obj->size];
}
bool tripleInOneIsEmpty(TripleInOne* obj, int stackNum) {
//判断当前栈顶指针是否为-1
return obj->top[stackNum] == -1;
}
void tripleInOneFree(TripleInOne* obj) {
free(obj->data);
free(obj);
}
15. 删除最外层的括号
这和上面的括号的最大深度有关联的,括号同时也是规范的。
char* removeOuterParentheses(char* s)
{
int i,len = strlen(s);
char* ans = (char*)malloc(sizeof(char) * (len+1));
int top = 0;
int index = 0;
for (i = 0; i < len; i++)
{
if(s[i] == ')')
{
top--;
}
if(top > 0)
{
ans[index++] = s[i];
}
if(s[i] == '(')
{
top++;
}
}
ans[index] = '\0';
return ans;
}
结尾
上面就是leetcode上所有关于栈的简单题了,有好几个重复的,就没往上放,还有些二叉树的题,也没有放。