数据结构与算法再探(二)栈与队列的应用

目录

栈应用举例

 std::stack的基本操作:

队列实现栈 

c++版单队列方式

python3

应用实例(一):括号匹配

C++栈

C++非栈方式

python实现

实例(二):后缀表达式求值

c++实现

python实现

队列的应用

队:

 std::queue 基本操作

栈实现队列

队列应用举例:

1、约瑟夫问题

数组实现:

队列实现:

双向链表

2、单调队列-滑动窗口里的最大值

C++

python3

总结


栈应用举例

栈是操作受限的线性表,典型生活中的用例:压入子弹。std::stack 类是一种容器适配器,底层实现默认是deque,它给予栈的功能,FILO(先进后出)。该类模板用做为底层容器的包装器,只提供特定函数集合。栈从被称作栈顶的容器尾部弹元素。

 std::stack的基本操作:

C++ stack的基本操作
s.pop() 修改器 删除栈顶元素,但不返回该元素的值
swap() 交换元素
s.push(item) 创建一个新元素压入栈顶,该元素通过拷贝或移动item而来,或者由args构造
s.emplace(args)
s.top() 元素访问 返回栈顶元素,但是不将元素弹出栈
empty()、size() 容量 empty检查容器是否为空,size返回元素数
栈默认基于deque实现,也可以在list或vector之上实现
#include 
#include 
using namespace std;
int main() {
  stack s;

  // push() 压入元素
  s.push(1); 
  s.push(2);
  s.push(3);
//容量
  cout << "s.size() = " << s.size() << endl; 
  cout << "s.empty() = " << s.empty() << endl;
  if (!s.empty())
    cout << "栈顶元素s.top() = " << s.top() << endl;
//元素访问
  s.pop();  // 弹出栈顶元素
  cout << "s.size() = " << s.size() << " s.empty() = " << s.empty() << endl;
  if (!s.empty())
    cout << "s.top() = " << s.top() << endl;
   //推入新元素到 stack 顶。原位构造元素,即不进行移动或复制操作
  s.emplace(3);
  stack s1;//空栈
  s1.swap(s);  // s和s1进行交换

  cout << "s.size() = " << s.size() << "  s.empty() = " << s.empty() << endl;
  if (!s.empty())
    cout << "栈顶元素,即s.top() = " << s.top() << endl;
  else
    cout << "栈空不能使用top访问栈顶元素" << endl;

  cout << "s1.size() = " << s1.size() << "  s1.empty() = " << s1.empty() << endl;
  if (!s1.empty())
    cout << "栈顶元素,即s1.top() = " << s1.top() << endl;
  return 0;
}

队列实现栈 

c++版单队列方式

class MyStack {
    queue qunIn; //单队列方式
public:
    MyStack() {
        
    }
    
    void push(int x) {  //模拟入栈
        int n=qunIn.size();
        qunIn.push(x);
        for(int i=0;i

python3

class MyStack:

    def __init__(self):
        self.queue=deque()

    def push(self, x: int) -> None:
        n=len(self.queue)
        self.queue.append(x)
        for _ in range(n):
            self.queue.append(self.queue.popleft())

    def pop(self) -> int:
        return self.queue.popleft()

    def top(self) -> int:
        return self.queue[0]

    def empty(self) -> bool:
        return not self.queue

应用实例(一):括号匹配

输入给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效

C++栈

bool isValid(string s) {
        stack sta;
        if (s.size() % 2 == 1) {
            return false;
        }
        for (auto ch : s) {
            if (sta.empty() || ch == '{' || ch == '[' || ch == '(') {
                sta.push(ch);
            } else if (ch == ']' && !sta.empty() && sta.top() == '[')
                sta.pop();
            else if (ch == '}' && !sta.empty() && sta.top() == '{')
                sta.pop();
            else if (ch == ')' && !sta.empty() && sta.top() == '(')
                sta.pop();
            else
                sta.push(ch);
        }
        return sta.empty();
    }

C++非栈方式

bool isValid(string s) {
    int len=s.length()/2;
	std::string::size_type n;
	for(int i=0;i

python实现

class Solution:
    def isValid(self, s: str) -> bool:
        if len(s)%2:
            return False
        mp={')':'(',']':'[','}':'{'}
        st=[]
        for c in s:
            if c not in mp: #c is 左括号
                st.append(c) #
            elif not st or st.pop()!=mp[c]:
                return False
        return not st #匹配结束左括号要匹配完

实例(二):后缀表达式求值

c++实现

int evalRPN(vector &tokens)
{
    std::stack st;
    for (auto i : tokens)
    {
        if (i.size() > 1 || isdigit(i[0]))
        {
            st.emplace(stoi(i));
        }
        else
        {
            try
            {
                if (st.empty())
                {
                    throw std::out_of_range("Stack is empty, cannot pop");
                }
                int n2 = st.top();
                st.pop();
                if (st.empty())
                {
                    throw std::out_of_range("Stack is empty, cannot pop");
                }
                int n1 = st.top();
                st.pop();
                if (i == "+")
                    n1 = n1 + n2;
                else if (i == "-")
                    n1 = n1 - n2;
                else if (i == "*")
                    n1 = n1 * n2;
                else if (i == "/")
                    n1 = n1 / n2;
                st.emplace(n1);
            }
            catch (const std::exception &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
    }
    return st.top();
}

python实现

#计算后缀表达式的值
# 后缀表达式(也称为逆波兰表达式)是一种算术表达式,其中操作符位于其操作数之后。
from typing import List
class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack=[]
        calc={'+':lambda x,y:x+y,
              '-':lambda x,y:x-y,
              '*':lambda x,y:x*y,
              '/':lambda x,y:int(x/y)}
        for i in tokens:
            if i in calc:
                try:
                    n2,n1=stack.pop(),stack.pop()
                    stack.append(calc[i](n1,n2))
                except ZeroDivisionError:
                    print('division by zero')
                    return None
                except IndexError:
                    print('not right evalRPN ')
                    return None
            else:
                stack.append(int(i))
        return stack[0]

so =Solution()
print(so.evalRPN(['0','/','/']))

队列的应用

队:

一种先进先出的数据结构,std::queue也是一种容器适配器,遵循先进先出FIFO原则,其中从容器一端插入元素,另一端提取元素。

priority_queue是C++标准库中的一个容器适配器,用于实现优先队列的数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级进行排序,每次取出的元素都是优先级最高的。它的底层实现通常使用堆(heap)数据结构。它提供常数时间的(默认)最大元素查找,对数代价的插入与提取。可以通过用户提供的 Compare 更改顺序,例如,用 std::greater 将导致最小元素作为top()出现。默认使用std::less比较使得最大元素出现在堆顶。priority_queue 的作用类似于管理某些随机访问容器中的堆,其优势是不可能意外使堆失效。

C++的队列适配器queue和priority_queue适配器定义在queue头文件中,queue默认基于deque实现,也可以用list或vector实现,priority_queue默认基于vector实现,也可以用deque实现
q.pop() 移除quene的首元素或priority_queue的最高优先级的元素
q.front()

返回首元素或尾元素,但是不删除此元素,q.back只适用于quene

q.back()
q.top() 返回最高优先级元素不删除该元素只适用于priority_queue
q.push(item) 在queue末尾或priority_queue中恰当位置创建一个值为item的元素或者由args构造
q.emplace(args)

 std::queue 基本操作

#include 
#include
using namespace std;
int main()
{
	queue q;	//默认使用 deque
	//queue> qVector;	//指定使用 vector
    q.push(1);
	q.push(2);
	q.push(3);
	cout << q.size() << endl;  //元素个数  size() 3
	cout << q.front() << endl; //队头元素 front() 1
	cout << q.back() << endl;  //队尾元素 队尾元素back() 3
    q.pop();                   // 出队pop()   
	cout << q.size() << endl;  //2
	cout << q.front() << endl; //2
	while (!q.empty())         //判空 empty()
    {
		cout << q.front() << " "; //2 3
		q.pop();
	}
	cout << q.size() << endl; //0
	queue q1;
	q1.push(4);
	q1.push(5);
	q1.push(6);
 	cout <<"before swap q size is "<< q.size() <<" before swap q1 size is "< pq;	//直接构造一个空对象,默认为大堆
	priority_queue, std::greater> pq1; // 构建小堆
	cout<<"----------priority_queue----"< pq2;	//直接构造一个空对象,默认为大堆
	cout<

栈实现队列

class MyQueue {
    private:
    stack staIn;
    stack staout;
public:
    MyQueue() {
    }
    
    void push(int x) { //入栈模拟入队
        staIn.push(x);
    }
    
    int pop() {
        if(staout.empty()){  //出队是入栈不能有元素,保证出栈是先入队元素
            while (!staIn.empty())
            {
                staout.push(staIn.top());
                staIn.pop();
            }
        }
        int res=staout.top();
        staout.pop();
        return res;
    }
    
    int peek() {
        int res=this->pop();
        staout.push(res);
        return res;
    }
    
    bool empty() {
        return staIn.empty()&&staout.empty();
    }
};

队列应用举例:

1、约瑟夫问题

N个人围成一圈从第一个人开始报数,数到M的人出圈;再由下一个人开始报数,数到M的人出圈

数组实现:
#include 
#include 
using namespace std;
 int main(){
 	int n,m,flag=0;
	 cin>>n>>m;
 	int count=0,len=1,arr[n+1]={0};
 	for(int i=1;i<=n;i++){
 		arr[i]=i;
	 }
	while(flag!=n)//一共n个人 
	{
		if(arr[len]!=0){//如果数组不为0,就计数已经输出的人将其值赋值为了0 
			count++;				
		}
		if(count==m)//计数值为m输出该点的值并将其赋值为0 
		{
			count=0;
			cout<
队列实现:
#include 
using namespace std;
int main()
{
    queue que;
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
        que.push(i);
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m - 1; ++j)
        {//将队头的人出队后,再入队到队尾
            que.push(que.front()); 
            que.pop();
        }
        cout << que.front() << ' ';//此时队头是要出列的人 
        que.pop();
    }
    return 0;
}
双向链表
#include
using namespace std;
int main()
{
    list li;
    int n, m, ct = 0;
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
        li.push_back(i);
    list::iterator it = li.begin();
    while(li.empty() == false)
    {
        ct++;
        if(ct == m)
        {
            cout << *it << ' ';
            it = li.erase(it);
            ct = 0;
        }
        else
        	it++;
        if(it == li.end())
            it = li.begin();
    }
    return 0;
}

2、单调队列-滑动窗口里的最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3   输出:[3,3,5,5,6,7]

C++

vector maxSlidingWindow(vector& nums, int k) {
        deque res;
        vector result;
        for(int i=0;i=k){   //队首出队 
                res.pop_front();
            }
            if(i>=k-1){  //单调性最值在队首
                result.push_back(nums[res.front()]);
            }
            
        }
        return result;
    }

python3

def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        ans=[]
        q=deque()
        for i, x in enumerate(nums):
            while q and nums[q[-1]]<=x:
                q.pop()  #维护单调性
            q.append(i) #入队
            if i-q[0]>=k:  #元素即将多于k,队首离队
                q.popleft()
            if i>=k-1:  #取最大值队首
                ans.append(nums[q[0]])
        return ans

总结

栈常用于 1)函数调用管理:程序的函数调用使用栈来管理局部变量和返回地址。2)表达式求值:在计算机中,栈用于表达式的求值和语法解析(如中缀表达式转后缀表达式)。3)回溯算法:如深度优先搜索(DFS)等算法中,栈用于存储路径信息。4)撤销操作:在文本编辑器等应用中,栈可以用于实现撤销功能。

队列常用于:1)任务调度:操作系统中的任务调度使用队列来管理进程或线程的执行顺序。2)数据流管理:在网络数据包处理、IO缓冲等场景中,队列用于管理数据流。3)广度优先搜索(BFS)在图的遍历中,队列用于存储待访问的节点。4)打印任务管理:打印机的任务管理使用队列来处理打印请求。

你可能感兴趣的:(数据结构与算法,数据结构,算法)