栈、队列-题

堆、栈、栈帧、栈区

添加基础知识描述

  1. 内存里的堆、栈:(操作系统)虚拟进程地址空间分段,1段内存区域的划分,函数调用,每个函数里的局部变量存在栈帧里,malloc的数据在堆上开空间。
  2. 数据结构里的栈、堆(二叉树),存储和管理数据,解决一些问题。
    以上2个堆、栈的名字一样,但是是2个学科里的不同问题
  3. 区分:栈、栈帧、栈区?
    栈: 一般指的是数据结构中栈结构—后进先出的数据结构
    栈区:一块特殊的内存空间,该内存空间中存储的是与函数调用相关的信息—线程:
    **栈帧:**一个特殊的数据结构–该结构组织的是与函数调用相关信息—每次函数调用时,都会对应一个栈帧,当函数调用结束后栈帧就被回收,而栈帧存储在栈区。
    在这里插入图片描述

栈的应用:表达式求解:中缀运算: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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值