栈、队列、堆(LeetCode 课程)

本文详细介绍了如何使用栈、队列和堆来解决LeetCode中的问题,包括用队列实现栈、用栈实现队列、包含最小函数的栈、合法的出栈序列、简单计算器、求数组中的K大数以及寻找中位数等经典算法。通过实例解析,深入理解这些数据结构的操作和应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

预备知识
基本操作:

  • 栈(stack)LIFO(后进先出):S.top():取出栈顶;S.empty()判断栈是否为空;S.push(x)将x添加至栈;S.pop():弹出栈顶;S.size()栈存储元素的个数
  • 队列(queue)FIFO(先进先出):Q.front():返回队列头部元素;Q.back():返回队列尾部元素;Q.empty()判断栈是否为空;Q.push(x)将x添加至栈;Q.pop():弹出栈顶;Q.size()栈存储元素的个数
  • 堆(heap):标准库中的priority queue(类型)默认构造最大堆,最小堆构造std::priority queue<int, std::vector,std::greater> small_heap;最大堆构造std::priority queue<int, std::vector,std::less> big_heap;
    big_heap.empty():判断堆是否为空;big_heap.pop():弹出对顶元素(最大值);big_heap.push(x)添加元素;big_heap.top()返回堆顶元素(最大值);big_heap.size():返回堆中元素个数

一、使用队列实现栈

使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
思路
在这里插入图片描述

#include <iostream>
#include <queue> 

using namespace std;

class MyStack{
	public:
	MyStack(){
		
	}
	void push(int x){
	 	std::queue<int> temp_queue;//申请一个临时队列temp_queue 
	 	temp_queue.push(x);         //将x先push进temp_queue 
	 	while(!_data.empty())       //然后将_data中的元素放进temp_queue 
	 	{
			 temp_queue.push(_data.front());
	 	  	 _data.pop();
	 	}
	 	while(!temp_queue.empty()) //将temp_queue中的元素放进_data中,此时存在队列中的取出顺序就与栈相同了 
	 	{
   	 	    _data.push(temp_queue.front());
	 	    temp_queue.pop();
	 	}
	}
	int pop(){
		int x=_data.front();
		_data.pop();
		return x;
	}
	int top(){
		return _data.front();
	}
	bool empty(){
		return _data.empty();
	}
	private:
	std::queue<int> _data;
};

int main()
{
	MyStack S;
	S.push(1); 
	S.push(2); 
	S.push(4);
	S.push(6); 
	cout<<S.top()<<endl;
	S.pop();
	cout<<S.top()<<endl;
	if(S.empty()) 
	cout<<"This stack is empty!"<<endl;
	else
	cout<<"This stack is not empty!"<<endl;
	return 0;
}

二、使用栈实现队列

使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
思路
在这里插入图片描述

#include <iostream>
#include <stack> 

using namespace std;

class MyQueue{
	public:
	MyQueue(){
		
	}
	void push(int x){
	 	std::stack<int> temp_stack;//临时栈用来调换数据顺序 
	 	while(!_data.empty())      //将_data中的数据放入临时栈中 
	 	{
			 temp_stack.push(_data.top());
	 		 _data.pop();
	 	}
 		temp_stack.push(x);    //将x push进temp_stack 
	 	while(!temp_stack.empty())//将temp_stack放进_data中,数据顺序调换完成 
	 	{
   	 	    _data.push(temp_stack.top());
	 	    temp_stack.pop();
	 	}
	}
	int pop(){
		int x=_data.top();
		_data.pop();
		return x;
	}
	int peek(){
		return _data.top();
	}
	bool empty(){
		return _data.empty();
	}
	private:
	std::stack<int> _data;
};

int main()
{
	MyQueue Q; 
	Q.push(4);
	Q.push(6); 
	cout<<Q.peek()<<endl;
	Q.pop();
	cout<<Q.peek()<<endl;
	Q.pop();
	if(Q.empty()) 
	cout<<"This queue is empty!"<<endl;
	else
	cout<<"This queue is not empty!"<<endl;
	return 0;
}

三、包含min函数的栈

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top() – 获取栈顶元素。
getMin() – 检索栈中的最小元素。
思路:用另一个栈存储各个状态下的最小值

#include <iostream>
#include <stack>
using namespace std;

class MinStack{
public:
	MinStack(){
		
	}
	void push(int x){
		_data.push(x);    //将x存进栈_data中 
		if(_min.empty()){ //如果最小栈_min为空,则直接将x存进最小栈 
			_min.push(x);
		}  
        else{             //如果最小栈不为空,则将x与_min.top()(上一次存储后最小的元素)进行比较 ,将较小的元素存进_min中 
        	if(x > _min.top()){
	        	x = _min.top();
	        }
	        _min.push(x);
        }
	}
	void pop(){
		_data.pop();
		_min.pop();
	}
	int top(){
	    return _data.top();
	}
	int getMin(){
		return _min.top();
	} 
private:
    std::stack<int> _data;  //用来存储数据 
    std::stack<int> _min;  //用来存储不同状态下的最小值 
	 
};

int main()
{
	MinStack S;
	S.push(1); 
	S.push(-2); 
	S.push(4);
	S.push(-6); 
	cout<<S.top()<<endl;
	S.pop();
	cout<<"The min number is";
	cout<<S.getMin()<<endl;
	
	return 0;
}

代码精炼:

class MinStack{
public:
	MinStack(){
		
	}
	void push(int x){
		_data.push(x);   
		if(_min.empty() || x<=_min.top()){ //如果x小于等于最小栈的栈顶或者最小栈为空则将x存进最小栈 
			_min.push(x);
		}  
	}
	void pop(){
		if(_data.top()==getMin()){ //弹出元素时,如果 _min中的元素等于将要弹出的元素则一同弹出 
			_min.pop();
		} 
		_data.pop();	
	}
	int top(){
	    return _data.top();
	}
	int getMin(){
		return _min.top();
	} 
private:
    std::stack<int> _data;  //用来存储数据 
    std::stack<int> _min;  //用来存储不同状态下的最小值 
	 
};

四、合法的出栈序列

在这里插入图片描述
思路
在这里插入图片描述

#include <iostream>
#include <stack>
#include <queue> 

using namespace std;

bool is_valid_order(std::queue<int> &Q){
	std::stack<int> S;
	int n = Q.size();
	for(int i=1;i<=n;i++){ //按1-n将元素push进S中 
		S.push(i);
		while(!S.empty()&&Q.front()==S.top()){ //如果栈与队列元素相同则将栈与队列中的元素弹出 
		//还要判断S是否为空;用while,if只会判断以此,如果有多个相等的元素则会出现错误 
			Q.pop();
			S.pop();
		}
	}
	if(S.empty()){
		return true;
	}
	    return false;
}
int main()
{
	std::queue<int> QQ;
	QQ.push(3);
	QQ.push(1);
	QQ.push(2);
	QQ.push(4);
	bool a=is_valid_order(QQ);
	if(a){
	  cout<<"This order is valid"<<endl; 
	} 
	else
	  cout<<"This order is not valid"<<endl; 
	return 0;
}

五、简单计算器

实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。
思路
在这里插入图片描述
代码:

#include <iostream>
#include <stack>
#include <string> 
using namespace std;

class solution
{
   public:
   int calculator(string s)
   {
	   	static const int STATE_BEGIN=0;   //利用向量机的思想,设置三种状态,分别是开始,数值,和符号 
	   	static const int NUMBER_STATE=1;
	   	static const int OPERATION_STATE=2;
	   	stack<int> number_stack;        //申请一个栈用来存储数值 
	   	stack<char> operator_stack;     //用来存储符号 
	   	int number=0;                   //用来处理数字,将字符型的数字转化成整型 
	   	int STATE = STATE_BEGIN;        //将状态设置成初始状态 
	   	int compute_flag = 0;           //将计算符号设置为零,不进行计算 
	   	
		for(int i=0; i < s.size();i++) //循环遍历字符串所有字符 
		{
			if(s[i]==' '){              //如果为空格则继续遍历,知道为数字或符号 
				continue;
			}
			switch(STATE)               //查看状态 
			{
				case STATE_BEGIN:       //如果是初始状态则判断当前字符是数值还是操作符 
				     if(s[i]>='0'&&s[i]<='9'){//如果是数字则将状态置为数字状态,下面进行数字相关的处理 
						STATE = NUMBER_STATE;
					}
					else{                     
				     	STATE = OPERATION_STATE; //如果不是数字则置为符号状态,下面进行符号的相关处理 
				    }
				    i--;                //i回一位,因为对当前位进行判断的后i会进行一次自加移动到下一位,因而要对当位置的内容进行操作则i往前回一位 
					break;              //推出case,继续switch 
				case NUMBER_STATE:      //如果发现当前字符是数字则将字符转换为整型 
				    if(s[i]>='0'&&s[i]<='9'){
				   	   number = number*10 + s[i] - '0';
				    }
				    else{             //当数字处理完后,将转化完成的数字存进数字栈中 
				    	number_stack.push(number);
				    	if(compute_flag == 1){//如果可以计算则进行计算 
				        	compute(number_stack,operator_stack);
				        }
				        number = 0; //数字入栈后number置为0 
				        i--;        //i回一位 
				        STATE = OPERATION_STATE; //状态置为符号 
				    }
                   break;
				case OPERATION_STATE:
				    if(s[i] == '+'|| s[i] == '-'){
				    	operator_stack.push(s[i]);//如果是符号则将符号push进符号栈中 ,并且计算标志置为1 
				    	compute_flag = 1;
				    }
				    else if(s[i]=='('){  
					//不要写成赋值等号了!! 
				    	STATE = NUMBER_STATE; //遇到左括号进行数值处理,且不能进行计算 
				    	compute_flag = 0;
				    }
				    else if(s[i]>='0'&&s[i]<='9'){
				    	STATE = NUMBER_STATE; //遇到数字进行数值处理,i退回一位 
				    	i--;
				    } 
				    else if(s[i] ==')'){ //遇到右括号则直接进行计算 
				    	compute(number_stack,operator_stack);
				    }
				break;
			}
		}
		if(number!=0) {
			number_stack.push(number);   //最后数字不为零则将其放进数字栈进行相关计算 
			compute(number_stack, operator_stack);
		}
		if(number==0 && number_stack.empty()) //数字为零,且数字栈为空,返回零 
		 //不能判断符号栈,符号比数字少,因此总返回0.要细心啊啊啊!!!! 
		    return 0;
	    return number_stack.top();   //返回数字栈顶部的数字 
  }
   
   void compute(stack<int> &number_stack, stack<char> &operator_stack){ //计算函数 
	   	if(number_stack.size()<2){ //如果数字栈小于两个数则不用进行计算 
		   	return;
	    }
	    int num2 = number_stack.top(); //令num2等于数字站顶部的数 
	    number_stack.pop();             //弹出顶部数字 
	    int num1 = number_stack.top(); 
	    number_stack.pop();
	    if(operator_stack.top()=='+'){ //对num1、num2 进行计算 
	    	number_stack.push(num1+num2);
	    }
	    else if(operator_stack.top()=='-'){
	    	number_stack.push(num1-num2);
	    }
	    operator_stack.pop();
   } 
};

int main()
{
	solution solve;
	string s = "1-(2+1)";
	int a=solve.calculator(s);
	cout<<a<<endl;
	return 0;
}

六、求数组中的K大数

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
思路
在这里插入图片描述

#include <iostream>
#include <vector> 
#include <queue>
using namespace std;

class Solution{
	public:
	 int findKthlargest(vector<int>& nums,int K){
 		priority_queue<int, vector<int>, greater<int> > Q;//最小堆 
 		for(int i=0; i < nums.size();i++){//遍历所有数字将前K大的数字入堆 
			if(Q.size() < K){             //如果堆中数字小于K个则直接入堆 
				Q.push(nums[i]);
			} 
			else if(Q.top()<nums[i]){     //否则判断堆顶数字是否小于新的数字,如果是则弹出堆顶,新的数字入堆 
				Q.pop();
				Q.push(nums[i]);
			}	
	    }
	    return Q.top();
 	}
};
int main()
{
	vector<int> nums;
	nums.push_back(3);
	nums.push_back(2);
	nums.push_back(1);
	nums.push_back(5);
	nums.push_back(6);
	nums.push_back(4);
	Solution solve;
	cout<<"The second largest number is:";
	cout<<solve.findKthlargest(nums, 2)<<endl; 
	return 0;
}

七、寻找中位数

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,[2,3,4] 的中位数是 3 [2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
思路
在这里插入图片描述
添加元素时:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

class MedianFinder{
	public:
	priority_queue<double, vector<double>, greater<double> > small_queue;//最小堆,用来存储较大的一半元素 
	priority_queue<double, vector<double>, less<double> > big_queue;//最大堆,用来存储较小的一半元素 
	MedianFinder(){//初始化	
	}
	void addNum(int num){//添加数字 
		if(big_queue.empty()){
			big_queue.push(num);
			return;
		}
		if(big_queue.size()==small_queue.size()){//情况1:如果最大堆与最小堆元素个数一样 
			if(num < big_queue.top()){           //如果num小于最大堆则直接存进最大堆 
				big_queue.push(num);
			}
			else{                                //否则存进最小堆 
				small_queue.push(num);
			}
		}
		else if(big_queue.size() > small_queue.size()){//情况2:如果最大堆的元素多一个 
			if(num < big_queue.top()){               //num小于最大堆堆顶,则要讲最大堆堆顶放进最小堆再弹出堆顶存入num 
				small_queue.push(big_queue.top());
				big_queue.pop();
				big_queue.push(num); 
			} 
			else{                                    //否则直接将num存进最小堆 
				small_queue.push(num);
			}
		}
		else if(big_queue.size() < small_queue.size()){//情况3:最小堆元素多一个 
			if(num < small_queue.top()){                 //num小于最小堆堆顶,则直接存进最大堆 
				big_queue.push(num);
			}
			else{                                        //否则将最小堆堆顶存进最大堆后弹出,再将num存进
				big_queue.push(small_queue.top());
				small_queue.pop();
				small_queue.push(num);
			}
		}
	}
	double findMedian(){
		if(big_queue.size()==small_queue.size()){  //最大堆与最小堆元素一样多取二者堆顶平均数 
			return (big_queue.top()+small_queue.top())/2;
		}
		else if(big_queue.size()>small_queue.size()){ //否则 返回元素较多的一个堆的堆顶 
			return big_queue.top();
		}
		return small_queue.top();
	}
}; 
int main()
{
	MedianFinder M;
	int test[] = {6,11,1,7,99,4,33};
	for(int i=0;i<7;i++){
		M.addNum(test[i]);
		cout<<M.findMedian()<<endl;
	} 
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值