文章目录
堆、栈、栈帧、栈区
- 内存里的堆、栈:(操作系统)虚拟进程地址空间分段,1段内存区域的划分,函数调用,每个函数里的局部变量存在栈帧里,malloc的数据在堆上开空间。
- 数据结构里的栈、堆(二叉树),存储和管理数据,解决一些问题。
以上2个堆、栈的名字一样,但是是2个学科里的不同问题 - 区分:栈、栈帧、栈区?
栈: 一般指的是数据结构中栈结构—后进先出的数据结构
栈区:一块特殊的内存空间,该内存空间中存储的是与函数调用相关的信息—线程:
**栈帧:**一个特殊的数据结构–该结构组织的是与函数调用相关信息—每次函数调用时,都会对应一个栈帧,当函数调用结束后栈帧就被回收,而栈帧存储在栈区。
栈的应用:表达式求解:中缀运算:4 +2×3 -10/5,2个栈。数字依次入栈,+优先级低于×,×入栈,—优先级低于×,×出栈,开始计算栈内数字:2×3=6,6入栈,-和+优先级一样,但+先出现,6+4=10,10入栈,剩余都的入栈,开始计算.次方的优先级高于*,()的优先级高于任意运算符,
347
优先级队列=披着队列外衣的堆、对部分频率进行排序
添加链接描述
239 滑动窗口最大值
150
36939-合法括号序列判断
stack<char> sc;
for (auto ele : A)
{
switch (ele) {
case '(':
sc.push(ele);
break;
case ')':
if (sc.empty() )
return false; 多出右半边
sc.pop();
break;
default:
return false;遇到了非括号字符
}
}return sc.empty() ;多出左半边
1047
20.有效的括号
typedef char STDateType;
typedef struct Stack
{
STDateType* _a;
int _top; //栈顶下标
int _capacity;
}Stack;
//初始化和销毁
void StackInit(Stack* pst);
void StackDestory(Stack* pst);
//入栈
void StackPush(Stack* pst, STDateType x);
//出栈
void StackPop(Stack* pst);
//获取数据个数
int StackSize(Stack* pst);
//返回1是空,返回0是非空
int StackEmpty(Stack* pst);
//获取栈顶的数据
STDateType StackTop(Stack* pst);
//初始化
void StackInit(Stack* pst)
{
assert(pst);
pst->_a = (STDateType*)malloc(sizeof(STDateType)*4);
pst->_top = 0;
pst->_capacity = 4;
}
//销毁
void StackDestory(Stack* pst)
{
assert(pst);
free(pst->_a);
pst->_a = NULL;
pst->_top = pst->_capacity = 0;
}
//入栈
void StackPush(Stack* pst, STDateType x)
{
assert(pst);
//空间不够则增容
if (pst->_top == pst->_capacity)
{
pst->_capacity *= 2;
STDateType* tmp = (STDateType*)realloc(pst->_a, sizeof(STDateType)*pst->_capacity);
if (tmp == NULL)
{
printf("内存不足\n");
exit(-1);
}
else
{
pst->_a = tmp;
}
}
pst->_a[pst->_top] = x;//栈顶=放入数据的下一个位置
pst->_top++;
}
//出栈
void StackPop(Stack* pst)
{
assert(pst);
assert(pst->_top > 0);
--pst->_top;
}
//获取数据个数
int StackSize(Stack* pst)
{
assert(pst);
return pst->_top;
}
//返回1是空,返回0是非空
int StackEmpty(Stack* pst)
{
assert(pst);
return pst->_top == 0 ? 1 : 0;
}
//获取栈顶的数据
STDateType StackTop(Stack* pst)
{
assert(pst);
assert(pst->_top > 0);
return pst->_a[pst->_top - 1];//栈顶=放入数据的下一个位置
}
bool isValid(char * s){
Stack st;
StackInit(&st);
while(*s != '\0')
{//接口型的已经包了头文件
//前括号那就入栈,后括号那就拿栈顶的值和此时相比较(这样他们两个才是相邻的)
if(*s == '[' || *s == '(' || *s == '{')
{
StackPush(&st,*s);
s++;
}
else
{
//表示此时前括号栈里没有了
if(StackEmpty(&st))
return false; //有后括号,无前括号
//取栈顶
char top = StackTop(&st);
if(*s == ']' && top != '[')
return false;//匹配错误
if(*s == ')' && top != '(')
return false;
if(*s == '}' && top != '{')
return false;
StackPop(&st);//匹配正确
s++;
}
}
//这里的while循环结束有两种情况:1:s遍历完了, 注意:[[[] 情况。2:都匹配上了,里面不再有任何东西了
//这里判断的是[[[] 在栈里面还有,但是你的s已经遇见了'\0'遍历完了,//后括号小于前括号数,前括号没走完,在栈里
int ret = StackEmpty(&st);
// ret=1=空,所有的括号都匹配上了,1=true,但是ret加上下面这个ret==0这个判断条件更加容易理解,如果他是0表示[在栈里面还有,但是此时s已经遍历完了,所以他不为空,那么也是一种错误的情况,//栈=空,即所有入进去的前括号都出去了
if(ret == 0)
return false;
//栈使用完后对其进行释放,否则内存的泄漏
StackDestory(&st);
return ret;
}
225.用队列实现栈的后进先出特点
使用队列实现栈的下列操作:
push(x) --元素×入栈
pop() --移除栈顶元素
top() --获取栈顶元素
emptv() --返回栈是否为空
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;
typedef struct QueueNode{
struct QueueNode*_next;
QDataType _data;//单链表 定义2个
} QueueNode;
typedef struct Queue{
QueueNode*_head;
QueueNode*_tail;
} 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);
int QueueEmpty(Queue* pq);
void QueueDestory(Queue* pq);
void QueueInit(Queue* pq)//把指针放在结构体里,传结构体的地址,不需要2级指针
{
assert(pq);
pq->_head = pq->_tail = NULL;
}
void QueuePush(Queue* pq, QDataType x){
assert(pq); QueueNode*newnode=(QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{ printf("内存不足\n"); exit(-1); }
newnode->_data = x; newnode->_next = NULL;
if (pq->_head == NULL){//队列=空
pq->_head = pq->_tail = newnode;
}
else{
pq->_tail->_next = newnode;//指向新结点
pq->_tail = newnode;//新结点=尾
}}
void QueuePop(Queue* pq){
assert(pq); assert(pq->_head);
QueueNode*next = pq->_head->_next;//先保留头的next,再feee头
free(pq->_head); pq->_head = next;
if (pq->_head == NULL){
pq->_tail = NULL;//所有都给删完了,不管这个尾,他就会成为野指针
}}
QDataType QueueFront(Queue* pq){
assert(pq); assert(pq->_head);
return pq->_head->_data;
}
QDataType QueueBack(Queue* pq){
assert(pq); assert(pq->_tail);
return pq->_tail->_data;
}
int QueueSize(Queue* pq)//遍历这个结构
{
assert(pq);
QueueNode*cur = pq->_head; int size = 0;
while (cur){
++size; cur=cur->_next;
}return size;
}
int QueueEmpty(Queue* pq){//返回1是空
assert(pq);
return pq->_head==NULL?1:0;
}
void QueueDestory(Queue* pq){
assert(pq);
QueueNode*cur = pq->_head; while (cur){
QueueNode*next = cur->_next; free(cur); cur = next;
}pq->_head = pq->_tail = NULL;
}
typedef struct{
Queue _q1;
Queue _q2;
} MyStack;//定义2个队列,始终保持1个空、1个有数据,以此实现倒数据
//先定义两个队列,队列先进入的数据都移到那个空的队列中去,然后再把最后一个入队列的元素pop出来. 继续入队列添加元素,在有元素的队列进行操作,
/** Initialize your data structure here. */
MyStack* myStackCreate() {//返回栈的结构体
MyStack*st=(MyStack*)malloc(sizeof(MyStack));//malloc只有free之后会销毁,生命周期受人的控制;如果改为:MyStack st,局部变量,则出了这个函数即使用不了,
QueueInit(&st->_q1);QueueInit(&st->_q2);//初始化
return st;//返回结构体,如果是&st,则应是MyStack**
}
/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->_q1))
{
QueuePush(&obj->_q1,x);
}else {QueuePush(&obj->_q2,x);}
}//往非空的队列里入数据,都是空,则随便入1个队列
/** Removes the element on top of the stack(栈顶) and returns that element. */
//只有弹数据时需要借助另外1个队列去倒数据,数据倒1圈时,顺序不变
int myStackPop(MyStack* obj) {
Queue*empty=&obj->_q1;
Queue*nonempty=&obj->_q2;//默认q1空,q2非空
if(QueueEmpty(&obj->_q2))
{empty=&obj->_q2;nonempty=&obj->_q1;}//默认错误
while(QueueSize(nonempty)>1){ //保证非空的队列只留下最后一个数据
QueuePush(empty,QueueFront(nonempty));//把非空队列的队头数据倒入到空队列里
QueuePop(nonempty);
}int top=QueueFront(nonempty);
QueuePop(nonempty);//弹出最后1个数据
return top;}
/** Get the top element. */
int myStackTop(MyStack* obj) {//获取栈顶的数据并不需要把它pop一下,只需要你的尾的next的date显示
if(!QueueEmpty(&obj->_q1)){//非空时,取队尾数据
return QueueBack(&obj->_q1);
}else {
return QueueBack(&obj->_q2);}
}
/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->_q1)&&QueueEmpty(&obj->_q2);
}
void myStackFree(MyStack* obj) {
QueueDestory(&obj->_q1);
QueueDestory(&obj->_q2);
free(obj);
}
/**
* Your MyStack struct will be instantiated and called as such:
* MyStack* obj = myStackCreate();
* myStackPush(obj, x);
* int param_2 = myStackPop(obj);
* int param_3 = myStackTop(obj);
* bool param_4 = myStackEmpty(obj);
* myStackFree(obj);
*/
232.用栈实现队列
typedef int STDataType;//声明
typedef struct Stack{
STDataType*_a; int _top;//栈顶下标
int _capacity;
}Stack;
void StackInit(Stack*pst);//传指针,形参的改变影响实参
void StackDestory(Stack*pst);
void StackPush(Stack* pst, STDataType x);
void StackPop(Stack* pst);
int StackSize(Stack* pst);//等价int StackSize(Stack pst)结构体中传值需要拷贝
int StackEmpty(Stack* pst);//返回1=空
STDataType StackTop(Stack* pst);
void StackInit(Stack*pst) //传指针,形参的改变影响实参;定义
{
assert(pst);
/*pst->_a = NULL; pst->_top = 0; pst->_capacity= 0;*/
pst->_a = malloc(sizeof(STDataType)*4); pst->_top = 0; pst->_capacity = 4;
}
void StackDestory(Stack*pst){
assert(pst); free(pst->_a); pst->_a = NULL; pst->_top = pst->_capacity = 0;
}
void StackPush(Stack* pst, STDataType x){
assert(pst); if (pst->_top == pst->_capacity){ pst->_capacity * 2;
STDataType*tmp = (STDataType*)realloc(pst->_a, sizeof(STDataType)*pst->_capacity);
if (tmp == NULL){ printf("内存不足\n"); exit(-1); }//开出空间失败
else { pst->_a = tmp; }
}pst->_a[pst->_top] = x; pst->_top++;
}
void StackPop(Stack* pst){
assert(pst); assert(pst->_top > 0); --pst->_top;
}
int StackSize(Stack* pst)//等价int StackSize(Stack pst)结构体中传值需要拷贝
{
assert(pst); return pst->_top;
}
int StackEmpty(Stack* pst)//返回1=空,pst->_top 栈顶指向最后数据的下1个位置,不要直接访问结构体
{
assert(pst); return pst->_top == 0 ? 1 : 0;
}
STDataType StackTop(Stack* pst)
{
assert(pst);
return pst->_a[pst->_top-1];
}
typedef struct {
Stack _pushST;Stack _popST;
} MyQueue;//数据倒1圈时,数据顺序变化,2个栈都有数据,固定为1个出栈,1个入栈
/** Initialize your data structure here. */
MyQueue* myQueueCreate() {
MyQueue*q=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&q->_pushST);StackInit(&q->_popST);
return q;
}
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->_pushST,x);
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
int front=myQueuePeek(obj);
StackPop(&obj->_popST);//删除队头数据
return front;
}
/** Get the front element.*/
int myQueuePeek(MyQueue* obj) {
if (!StackEmpty(&obj->_popST)) //如果是非空,弹出队头数据
return StackTop(&obj->_popST);
else //把obj->_pushST倒入obj->_popST
{
while(!StackEmpty(&obj->_pushST)){
StackPush(&obj->_popST,StackTop(&obj->_pushST));
StackPop(&obj->_pushST);
}
return StackTop(&obj->_popST);//得到队头数字
}}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->_pushST) && StackEmpty(&obj->_popST);
}
void myQueueFree(MyQueue* obj) {
StackDestory(&obj->_popST);
StackDestory(&obj->_pushST); free(obj);}
622–设计循环队列
逻辑结构:圈;底层是数组还是链表都可以。建议用数组
把最开始的结点里面的数据干掉,然后换上新的数据(可不是干掉结点,只是干掉原结点里面的数据),所开的空间大小是固定
满了: (rear+1)%(k+1)=front,(1+1)%(4+1)=2,计算使用数组下标,空的位置一直在变化。
//在myCircularQueueCreate中所使用的k是一个局部变量,出了这个局部变量是拿不到的,所以也必须传入结构体中
typedef struct {
int* _a;
int _front;
int _rear;
int _k;
} MyCircularQueue;
//队列实际个数=固定,存放k个数据,创造k+1个空间,rear+1 = front:满了
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
q->_a = (int*)malloc(sizeof(int)*(k + 1));//多开一个空间
q->_front = 0;
q->_rear = 0;
q->_k = k;
return q;
}
在使用前加声明
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//插入数据
if (myCircularQueueIsFull(obj))
return false;
obj->_a[obj->_rear] = value;//非满则插入数据,rear永远指向要插入的数据
obj->_rear++;
//但是有可能你此时的rear已经走到了你所开辟的最后一个空间的位置处,你在++此时就会越过数组
obj->_rear %= (obj->_k + 1);//即使你现在rear并不是最后一个位置,你%完以后,是不会有影响的
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
return false;
++obj->_front;//删除数据//删不删除front位置的元素都可以,但是也有可能front加着越过数组的长度
obj->_front %= (obj->_k + 1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {//返回队头数据
if (myCircularQueueIsEmpty(obj))
return -1;
else
return obj->_a[obj->_front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{ int tail = obj->_rear - 1;
//rear的数组下角标=0时,上一个位置是在数组的最后一个下角标的位置
if (tail == -1)
tail = obj->_k;
return obj->_a[tail];
}
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->_front == obj->_rear; //真返回的是1
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->_rear + 1) % (obj->_k + 1) == obj->_front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
//先释放最里面一层的,在释放最外面的
free(obj->_a);
free(obj);
}
现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设队头不存放数据)
(rear - front + N) % N
画图:1:非满;2满了
栈的压入、弹出序列
添加链接描述
入栈:pushV, 出栈:popV;遍历入栈序列,只要popv的第一个数据和当前pushv的数据不相等,一直入栈;只要st栈顶值=popv的出栈序列数据,出栈。如果符合要求,最后栈结构是空
bool IsPopOrder(vector<int> pushV, vector<int> popV) {
if (pushV.size() == 0 || popV.size() == 0 || pushV.size() != popV.size())
{ return false; }
stack<int> st; //辅助栈,入栈出栈模拟
int i = 0; int j = 0;
for(; i < pushV.size(); i++)
{ st.push(pushV[i]);
while(!st.empty() && st.top() == popV[j])
{ st.pop(); //去掉栈顶,比较下一个
j++;
}
}
return st.empty();
}
包含min函数的栈
添加链接描述
每次更新的时候,都对min变量进行更新。如果想拿出第二小,第三小的值怎么拿? //用上面的办法就不行了 //为了满足通用,使用一个辅助栈,内部保存元素的个数和数据栈完全一样,辅助栈内部元素可能会出现“必要性”重复),为了实现算法, 所以就不从0开始实现stack
private: stack<int> data_stack; //数据栈
stack<int> min_stack;//辅助栈 最小值
public:
void push(int value)
{
data_stack.push(value);
if(min_stack.size() == 0 || value < min_stack.top())
{ min_stack.push(value); }
else
{ //min_stack.size() != 0 && value >= min_stack.top()
min_stack.push(min_stack.top()); }
}
void pop()
{ if(data_stack.size() == 0 || min_stack.size() == 0)
{ return; }
data_stack.pop();
min_stack.pop();
}
int top() {
return data_stack.top();
}
int min()
{ return min_stack.top(); }辅助栈的栈顶值:当前时刻到数据栈位置时的min