秒懂C++之stack、queue、堆

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

前言

一.stack常用接口

二.stack模拟实现

三.例题

3.1 最小栈

题目解析:

算法解析:

代码:

 3.2 栈的压入、弹出序列

题目解析:

算法解析:

代码:

3.3 逆波兰表达式求值

题目解析:

算法解析:

拓展:

代码:

四.queue常用接口

五.queue模拟实现

六.例题

6.1 二叉树的层序遍历

题目解析:

算法解析:

代码:

七.priority_queue常用接口

八.priority_queue模拟实现

8.1 仿函数

九.例题

9.1 数组中的第K个最大元素

题目解析:

算法解析:

代码:

十. 全部代码


前言

本次学习的容器在功能上基本与前面一致~对接口模拟感兴趣的友友们可以移步前面的文章:

秒懂C++之vector(下)-CSDN博客

秒懂C++之string类(下)-CSDN博客

本篇文章主要以算法题为主~

一.stack常用接口

二.stack模拟实现

namespace lj 
{
	template<class T>
	class stack
	{
		// ...
	private:
		T* _a;
		size_t _top;
		size_t _capacity;
	};
}

这是很传统的模拟实现~

template<class T, class Container = deque<T>>//给适配器搭配缺省参数
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop()
		{
			_con.pop_back();
		}

		const T& top()
		{
			return _con.back();
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

	private:
		Container _con;
	};

这是加入了适配器后了模拟~ 

可以发现我们拿vector容器进行适配,后面的接口模拟只需要调用vector的接口实现就好了。

当然这前提当然是二者合适适配,栈里面有数组栈,也有链式栈。这意味它可以适配list与vector容器。而这里用deque容器作缺省是因为它同时具有前面二者的功能~

三.例题

3.1 最小栈

155. 最小栈 - 力扣(LeetCode)

 

题目解析:

通常想法是设置一个变量记录入栈时最小的那个数,遇到更小的就替换。但缺点是如果我们把3pop出去,那它还得重新再遍历对比一遍,效率太低了。

算法解析:

这里我们换一种思路,搞两个栈,其中一个栈用来记录数据出入,而另一个栈用来记录出入数中最小的存在~

代码:

class MinStack {
public:
    MinStack() {
        //可以用默认构造,不用写~
    }

    void push(int val) {
        _stack.push(val);
        if (_minstack.empty() || _stack.top() <= _minstack.top())//minst只有在空或者入栈的数比之前记录的还小才可以进来
        {
            _minstack.push(val);
        }

    }

    void pop() {
        if (_stack.top() == _minstack.top())//stack出栈的时候还要观察minst里面是否也要跟着出
        {
            _minstack.pop();
        }
        _stack.pop();
    }

    int top() {
        return _stack.top();
    }

    int getMin() {
        return _minstack.top();
    }
    stack<int> _stack;
    stack<int> _minstack;
};

 3.2 栈的压入、弹出序列

栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)

题目解析:

本题核心就在于我们在入栈的同时也可以出栈,这样就能先理解哪些弹出序列是对是错了~

算法解析:

我们思路很简单,就一直对入栈数组进行入栈,直到我们能够在栈顶找到与出栈数组一致的数,那时候就出栈,然后再选定出栈数组的下一个数。就这样以此类推直到出栈数组里面的数可以全部出光那就证明这是一个合格的弹出序列~

代码:

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pushV int整型vector
     * @param popV int整型vector
     * @return bool布尔型
     */
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        // write code here
        stack<int> _st;
        int pushi = 0;
        int popj = 0;
        while (pushi < pushV.size())//只要栈没入完就继续入
        {
            _st.push(pushV[pushi]);
            pushi++;
            while (!_st.empty() && _st.top() == popV[popj])//只要st栈不为空并且和popV值一样就可以出栈,这里得用while是因为可能会连续出栈
            {
                _st.pop();
                popj++;
            }
        }
        return popj == popV.size();
    }
};

3.3 逆波兰表达式求值

150. 逆波兰表达式求值 - 力扣(LeetCode)

题目解析:

本质就是要把后缀表达式转化为中缀表达式进行运算。

算法解析:

后缀表达式如何转中缀表达式如上图所示,在书面上我们可以直接模拟出结果,而在题目中我们可以利用栈来帮助我们实现最终结果。

拓展:

中缀表达式转化为后缀表达式

之所以操作符优先级比栈顶高要入栈是因为还无法确定后面的运算符是否更大,而一旦确定当前操作符比栈顶优先级相等或低就可以立马出栈运算~

ps:如果遇到括号怎么办呢?a+b*c-d/(e+a*b)

我们要知道括号最终的运算结果本身也只是一个数字罢了,我们可以采用递归的策略让其直接返回结果即可,左括号(进入递归,右括号)递归结束返回结果~最后再和上述一致推出中缀表达式即可~

代码:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(auto& e: tokens)//记得&,因为为string类
        {
            if(e=="+"||e=="-"||e=="*"||e=="/")
            {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();

                switch(e[0])//switch条件不支持字符串
                {
                    case '+':
                        st.push(left+right);
                        break;
                    case '-':
                        st.push(left-right);
                        break;
                    case '*':
                        st.push(left*right);
                        break; 
                    case '/':
                        st.push(left/right);
                        break;
                }
            }
            else
            {
                st.push(stoi(e));
            }
        }
        return st.top();
    }
};

四.queue常用接口

五.queue模拟实现

template<class T, class Container = deque<T>>//给适配器搭配缺省参数
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop()
		{
			_con.pop_front();
		}

		const T& front()
		{
			return _con.front();
		}

		const T& back()
		{
			return _con.back();
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

	private:
		Container _con;
	};

我们再来对队列进行适配器的使用~可以发现只需要稍微合理调用适配器的接口函数就可以帮我们实现出模拟的效果了。

注意:这里不能用vector作为适配器,因为它不具备头删的接口~

六.例题

6.1 二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

题目解析:

层序遍历我们一般都是利用队列进行操作,一层出完的同时就会带入下一层

算法解析:

一开始我们可以用一个变量Levsize来记录该层数的节点个数,排出一个节点我们就自减,直到减为0就可以认为当前层数已经全部遍历完毕,最后再把当前层数的节点记录即可。

而我们在排出的过程中又会带入下一层的节点,我们下一层的Levsize更新就是数队列内数据的个数即可,用它们的个数来作为新一层是否遍历完毕的结果~

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        int Levsize = 0;
        if(root)
        {
            q.push(root);
            Levsize =1;
        }
        while(!q.empty())
        {
            vector<int> v;
            while(Levsize--)//代表需要出栈
            {
                TreeNode* node = q.front();//记录节点
                q.pop();

                v.push_back(node->val);//上传该节点数据
                
                if(node->left)
                {
                    q.push(node->left);
                }
                if(node->right)
                {
                    q.push(node->right);
                }
            }
            //该层遍历结束,更新下一层的个数
            Levsize = q.size();
            vv.push_back(v);//把该层数据上传

        }
        return vv;

    }
};

七.priority_queue常用接口

八.priority_queue模拟实现

template<class T, class Container = vector<T>>
	class priority_queue
	{
	public:
		void adjust_up(int child)
		{
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[child] > _con[parent])
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void adjust_down(int parent)
		{
			int child = parent * 2 + 1;

			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child + 1] > _con[child])
				{
					++child;
				}

				if (_con[child] > _con[parent])
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void push(const T& x)
		{
			_con.push_back(x);

			adjust_up(_con.size() - 1);
		}

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			adjust_down(0);
		}

		const T& top()
		{
			return _con[0];
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};

堆的模拟实现合适的适配器是vector,关键在于[ ]的调用~

无论是尾插还是删除本质默认都是为了形成或维护大堆,如果我们想要形成小堆那就得把两个调整法中的大于号改成小于号~

有没有一种办法能在构建对象的时候就能指定想要的运算符从而改变堆的结构呢?——仿函数

8.1 仿函数

namespace lj
{
	template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
}

仿函数本质是对()进行函数重载,我们分别封装出<(less)与>(greater)的类。

九.例题

9.1 数组中的第K个最大元素

215. 数组中的第K个最大元素 - 力扣(LeetCode)

题目解析:

这种我们一眼就知道可以使用堆来解答了~

算法解析:

可以去看之前写的文章哦,那里讲得很清楚~:数据结构——二叉树的基本概念及顺序存储(堆)_头歌数据结构二叉树的顺序存储及基本操作-CSDN博客

代码:

第一种解法:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        //k*logN
        priority_queue<int> pq(nums.begin(),nums.end());//直接通过nums来进行初始化建大堆

        while(--k)//--k,k会走k-1次,把前k-1个pop掉
        {
            pq.pop();
        }
        return pq.top();
    }
};

第二种解法:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        //当k很大时
        priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);//建立k个数的小堆
        //(N-k)*logK
        for(int i = k;i<nums.size();i++)
        {
            if(nums[i]>pq.top())
            {
                pq.pop();
                pq.push(nums[i]);
            }
        }

        return pq.top();
    }
};

十. 全部代码

//stack_queue
#pragma once
namespace lj 
{
	//template<class T>
	//class stack
	//{
	//	// ...
	//private:
	//	T* _a;
	//	size_t _top;
	//	size_t _capacity;
	//};
	//template<class T, class Container = deque<T>>//给适配器搭配缺省参数
	//class stack
	//{
	//public:
	//	void push(const T& x)
	//	{
	//		_con.push_back(x);
	//	}

	//	void pop()
	//	{
	//		_con.pop_back();
	//	}

	//	const T& top()
	//	{
	//		return _con.back();
	//	}

	//	size_t size()
	//	{
	//		return _con.size();
	//	}

	//	bool empty()
	//	{
	//		return _con.empty();
	//	}

	//private:
	//	Container _con;
	//};
	//template<class T, class Container = deque<T>>//给适配器搭配缺省参数
	//class queue
	//{
	//public:
	//	void push(const T& x)
	//	{
	//		_con.push_back(x);
	//	}

	//	void pop()
	//	{
	//		_con.pop_front();
	//	}

	//	const T& front()
	//	{
	//		return _con.front();
	//	}

	//	const T& back()
	//	{
	//		return _con.back();
	//	}

	//	size_t size()
	//	{
	//		return _con.size();
	//	}

	//	bool empty()
	//	{
	//		return _con.empty();
	//	}

	//private:
	//	Container _con;
	//};


	template<class T, class Container = vector<T>,class Compare = lj::less<int>>//类模板加入仿函数
	class priority_queue
	{
	public:
		Compare _com;//定义所需要运算符的类对象
		void adjust_up(int child)
		{
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void adjust_down(int parent)
		{
			int child = parent * 2 + 1;

			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _com(_con[child], _con[child + 1]))
				{
					++child;
				}

				if (_com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void push(const T& x)
		{
			_con.push_back(x);

			adjust_up(_con.size() - 1);
		}

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			adjust_down(0);
		}

		const T& top()
		{
			return _con[0];
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};

	void test()
	{
		/*stack<int, vector<int>> st;
		st.push(1);
		st.push(1);
		st.push(1);
		st.push(1);
		while (!st.empty())
		{
			cout << st.top() << " ";
			st.pop();
		}
		cout << endl;*/
		/*queue<int> q;
		q.push(1);
		q.push(1);
		q.push(1);
		q.push(1);
		while (!q.empty())
		{
			cout << q.front() << " ";
			q.pop();
		}
		cout << endl;*/

		//priority_queue<int> pq;//默认适配器vector,默认运算符小于号——形成大堆
		//pq.push(1);
		//pq.push(2);
		//pq.push(2);
		//pq.push(3);
		//pq.push(6);
		//while (!pq.empty())
		//{
		//	cout << pq.top() << " ";
		//	pq.pop();
		//}
		//cout << endl;
		priority_queue<int,vector<int>,greater<int>> pq1;//适配器vector,运算符大于号——形成小堆
		pq1.push(6);
		pq1.push(5);
		pq1.push(4);
		pq1.push(3);
		pq1.push(2);
		while (!pq1.empty())
		{
			cout << pq1.top() << " ";
			pq1.pop();
		}
		cout << endl;
	}
}
//test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>
#include <list>
#include <queue>
#include <stack>


using namespace std;



namespace lj
{
	template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
}

#include "stack_queue.h"
int main()
{
	lj::test();

	return 0;
}




//
//class MinStack {
//public:
//    MinStack() {
//        //可以用默认构造,不用写~
//    }
//
//    void push(int val) {
//        _stack.push(val);
//        if (_minstack.empty() || _stack.top() <= _minstack.top())//minst只有在空或者入栈的数比之前记录的还小才可以进来
//        {
//            _minstack.push(val);
//        }
//
//    }
//
//    void pop() {
//        if (_stack.top() == _minstack.top())//stack出栈的时候还要观察minst里面是否也要跟着出
//        {
//            _minstack.pop();
//        }
//        _stack.pop();
//    }
//
//    int top() {
//        return _stack.top();
//    }
//
//    int getMin() {
//        return _minstack.top();
//    }
//    stack<int> _stack;
//    stack<int> _minstack;
//};
//
//class Solution {
//public:
//    /**
//     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
//     *
//     *
//     * @param pushV int整型vector
//     * @param popV int整型vector
//     * @return bool布尔型
//     */
//    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
//        // write code here
//        stack<int> _st;
//        int pushi = 0;
//        int popj = 0;
//        while (pushi < pushV.size())//只要栈没入完就继续入
//        {
//            _st.push(pushV[pushi]);
//            pushi++;
//            while (!_st.empty() && _st.top() == popV[popj])//只要st栈不为空并且和popV值一样就可以出栈,这里得用while是因为可能会连续出栈
//            {
//                _st.pop();
//                popj++;
//            }
//        }
//        return popj == popV.size();
//    }
//};
//
//class Solution {
//public:
//    int evalRPN(vector<string>& tokens) {
//        stack<int> st;
//        for (auto& e : tokens)//记得&,因为为string类
//        {
//            if (e == "+" || e == "-" || e == "*" || e == "/")
//            {
//                int right = st.top();
//                st.pop();
//                int left = st.top();
//                st.pop();
//
//                switch (e[0])//switch条件不支持字符串
//                {
//                case '+':
//                    st.push(left + right);
//                    break;
//                case '-':
//                    st.push(left - right);
//                    break;
//                case '*':
//                    st.push(left * right);
//                    break;
//                case '/':
//                    st.push(left / right);
//                    break;
//                }
//            }
//            else
//            {
//                st.push(stoi(e));
//            }
//        }
//        return st.top();
//    }
//};
//
//
///**
// * Definition for a binary tree node.
// * struct TreeNode {
// *     int val;
// *     TreeNode *left;
// *     TreeNode *right;
// *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
// *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
// *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
// * };
// */
//class Solution {
//public:
//    vector<vector<int>> levelOrder(TreeNode* root) {
//        vector<vector<int>> vv;
//        queue<TreeNode*> q;
//        int Levsize = 0;
//        if (root)
//        {
//            q.push(root);
//            Levsize = 1;
//        }
//        while (!q.empty())
//        {
//            vector<int> v;
//            while (Levsize--)//代表需要出栈
//            {
//                TreeNode* node = q.front();//记录节点
//                q.pop();
//
//                v.push_back(node->val);//上传该节点数据
//
//                if (node->left)
//                {
//                    q.push(node->left);
//                }
//                if (node->right)
//                {
//                    q.push(node->right);
//                }
//            }
//            //该层遍历结束,更新下一层的个数
//            Levsize = q.size();
//            vv.push_back(v);//把该层数据上传
//
//        }
//        return vv;
//
//    }
//};
//
//class Solution {
//public:
//    int findKthLargest(vector<int>& nums, int k) {
//        //当k很大时
//        priority_queue<int, vector<int>, greater<int>> pq(nums.begin(), nums.begin() + k);//建立k个数的小堆
//
//        for (int i = k; i < nums.size(); i++)
//        {
//            if (nums[i] > pq.top())
//            {
//                pq.pop();
//                pq.push(nums[i]);
//            }
//        }
//
//        return pq.top();
//    }
//};
//class Solution {
//public:
//    int findKthLargest(vector<int>& nums, int k) {
//        //k*logN
//        priority_queue<int> pq(nums.begin(), nums.end());//直接通过nums来进行初始化建大堆
//
//        while (--k)//--k,k会走k-1次,把前k-1个pop掉
//        {
//            pq.pop();
//        }
//        return pq.top();
//    }
//};

  • 52
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值