数据结构笔记4: 栈和队列

堆栈的定义

  • 堆栈(stack)是一个线性表,其插入和删除操作都在表的同一端进行,这端被称为栈顶(top),另一端被称为栈底(bottom)
  • 后进先出

堆栈的描述

公式化描述

  • 继承线性表
    template<class T>
    class Stack : private LinearList <T> { //线性表的尾部作为栈顶
    public:
        Stack(int MaxStackSize = 10): LinearList<T> (MaxStackSize){} //构造函数
        bool IsEmpty() const{
            return LinearList<T>::IsEmpty();
        } 
        bool IsFull() const{
            return (Length() == GetMaxSize());
        }
        T Top() const{ //提取栈顶元素
            if (IsEmpty()) 
                throw OutOfBounds();
            T x; 
            Find(Length(), x); 
            return x;
        } 
        Stack<T>& Push(const T& x){ //入栈
            Insert(Length(), x); 
            return *this;
        } 
        Stack<T>& Pop(T& x){ //出栈
            Delete(Length(), x); 
            return *this;
        }
    }
  • 自定义的stack类

class Stack{
public:
    Stack(int MaxStackSize = 10);
    ~Stack() {delete [] stack;}
    bool IsEmpty() const {return top == -1;} 
    bool IsFull() const {return top == MaxTop;} 
    T Top() const;
    Stack<T>& Push(const T& x);
    Stack<T>& Pop(T& x);
private:
    int top; //栈顶的位置
    int MaxTop; //栈顶的最大位置(数组大小)
    T *stack; //一维数组实现栈
};

template<class T> 
Stack<T>::Stack(int MaxStackSize) { //构造函数
    MaxTop = MaxStackSize - 1; 
    stack = new T[MaxStackSize]; 
    top = -1; //空栈
}

template<class T>
T Stack<T>::Top() const { //返回栈顶元素
    if (IsEmpty()) 
        throw OutOfBounds();
    return stack[top]; 
}

template<class T>
Stack<T>& Stack<T>::Push(const T& x){ //入栈
    if(IsFull())
        throw OutOfBounds();
    stack[++top]=x; //先加再存
    return *this;
}

template<class T>
Stack<T>& Stack<T>::Pop(T& x){
    if(IsEmpty())
        throw OutOfBounds();
    x=stack[top--]; //先取再减
    return *this;
}
  • 数组描述的缺陷:空间利用率低(要开一个很大的数组)

链表描述

  • 首节点当成栈顶,尾节点当成栈底 ➡️ O(1)
  • 继承链表
    template<class T>
    class LinkedStack : private Chain<T> { //私有继承自链表类,栈顶在链表的首节点
    public:
        bool IsEmpty() const{
            return Chain<T>::IsEmpty();
        }
        bool IsFull() const; 
        T Top() const{
            if (IsEmpty()) 
                throw OutOfBounds(); 
            T x; 
            Find(1, x); 
            return x;
        }
        LinkedStack<T>& Push(const T& x) {
            Insert(0, x); //在链表头前面插入数据
            return *this;
        }
        LinkedStack<T>& Pop(T& x) {
            Delete(1, x); //删除链表头的数据
            return *this;
        }
    }
    
    template<class T>
    bool LinkedStack<T>::IsFull() const {
        try {
            ChainNode<T> *p = new ChainNode<T>; 
            delete p;
            return false;
        }   catch (NoMem) { //如果抛出异常
            return true; //栈已满
        }
    }
  •  自定义的链表实现
    template <class T> class Node {
        friend LinkedStack<T>; //一定要声明友元,否则它无法调用node的私有成员
    private:
        T data;
        Node<T> *link; 
    };
    
    template<class T> class LinkedStack {
    public:
        LinkedStack() {top = 0;} 
        ~LinkedStack();
        bool IsEmpty() const {return top == 0;} 
        bool IsFull() const;
        T Top() const;
        LinkedStack<T>& Push(const T& x); 
        LinkedStack<T>& Pop(T& x);
    private:
        Node<T> *top; //栈顶指针
    };
    
    template<class T> LinkedStack<T>::~LinkedStack() { //析构函数
        Node<T> *next; 
        while (top) {
            next = top->link; 
            delete top;
            top = next;
        } 
    }
    
    template<class T>
    bool LinkedStack<T>::IsFull() const {
        try {
            Node<T> *p = new Node<T>; delete p;
            return false;
        } catch (NoMem) { //如果抛出异常
            return true; //栈已满
        }
    }
    
    template<class T>
    T LinkedStack<T>::Top() const { //获取栈顶元素
        if (IsEmpty()) 
            throw OutOfBounds();
        return top->data; 
    }
    
    template<class T>
    LinkedStack<T>& LinkedStack<T>::Push(const T& x){//入栈
        Node<T> *p = new Node<T>; 
        p->data = x;
        p->link = top; //加在栈顶前面
        top = p; //更新栈顶
        return *this;
    }
    
    template<class T>
    LinkedStack<T>& LinkedStack<T>::Pop(T& x) { //出栈
        if (IsEmpty()) 
            throw OutOfBounds(); 
        x = top->data; //把出栈的元素存在x中
        Node<T> *p = top;
        top = top->link; //更新栈顶
        delete p; //释放空间
        return *this; 
    }
  • 小结:堆栈的两种实现方式比较
    公式化链表
    Create()O(1) / O(MaxSize)O(1)
    Destroy()O(1) / O(MaxSize)O(n)
    IsEmpty()O(1)O(1)
    IsFull()O(1)O(1)
    Top()O(1)O(1)
    Push()O(1)O(1)
    Pop()O(1)O(1)

    其中,堆栈最常用最重要的函数是Push()和Pop()

堆栈的应用

括号匹配

  • 问题描述:括号必须成对出现,否则报错
  • 应用:C++编译器、数学公式自动求解(句法分析)
  • 算法设计思路:右括号和最近未匹配的左括号匹配
    (1)从左向右解析表达式,遇到左括号就把它push入栈中,遇到右括号就把栈顶的左括号pop出来和它匹配
    (2)如果有右括号但找不到左括号与之匹配,或遍历完了之后左括号的栈不为空,则报错
    #include <iostream.h> 
    #include <string.h> 
    #include <stdio.h> 
    #include "stack.h"
    
    const int MaxLength = 100; //输入的表达式的最大长度
    
    void PrintMatchedPairs(char *expr) { //括号匹配函数
        Stack<int> s(MaxLength); //用堆栈实现
        int j, length = strlen(expr);
        for (int i = 1; i <= length; i++) { //遍历字符串
            if (expr[i - 1] == '(') //遇见左括号就入栈
                s.Push(i);
            else if (expr[i - 1] == ')') //遇见右括号就出栈
                try {
                    s.Pop(j); 
                    cout << j << ' ' << i << endl; //输出哪两个字符是匹配的括号
                } catch (OutOfBounds){ //如果抛出异常,证明有多余的右括号
                    cout << "No match for right parenthesis”<< " at " << i << endl;
                }
        }
        while (!s.IsEmpty()) { //当栈不为空时,即有多余的左括号
            s.Pop(j);
            cout << "No match for left parenthesis at "<< j << endl;
        } 
    }
    
    void main() {
        char expr[MaxLength];
        cout << "Type an expression of length at most "<< MaxLength << endl;
        cin.getline(expr, MaxLength); //获取表达式
        cout <<"The pairs of matching parentheses in”<< endl; 
        puts(expr);
        cout <<"are" << endl;
        PrintMatchedPairs(expr);
    }

火车车厢重排

  • 问题描述:重新排列杂序入轨的车厢,使得车厢按编号排列出轨
  • 算法思路:通过栈构造缓冲轨
    (1)入轨的车厢,判断按顺序是否该出轨
    (2)如果不该出轨,放入缓冲轨中(越早进入缓冲轨越晚出轨)
    (3)如果该出轨,直接出轨,然后判断缓冲轨中有没有应该出轨的
    ​​​​​​​
    //返回能否将火车车厢重排
    bool Railroad(int p[],int n, int k){ //k条缓冲轨
        LinkedStack<int> *H; //创建用链表实现的堆栈数组来放k条缓冲轨
        H = new LinkedStack<int> [k + 1];
        int NowOut = 1; //记录接下来应该出轨的车厢号
        int minH = n+1; //在所有缓冲轨上最小的车厢号
        int minS; //拥有最小车厢号的缓冲轨
        //重排
        for(int I=1;i<=n;i++)
            if(p[I]==NowOut){ //如果入轨的车厢就是应该出轨的车厢
                cout << “Move car ” << p[i] <<“ from input to output”<< endl;
                NowOut++; //更新应该出轨的车厢号
                while(minH==NowOut){ //如果在缓冲轨中有接下来应该出轨的
                    Output(minH,minS,H,k,n); //从缓冲轨中出轨
                    NowOut++; //更新应该出轨的车厢号
                }
            }
            else{ //如果入轨的车厢不应该出轨,那么让它进入缓冲轨
                if(!Hold(p[I],minH,minS,H,k,n)) //如果进入缓冲轨不成功,报错
                    return false;
            }
        return true;
    } //O(k*n)
    
    //从缓冲轨中出轨
    void Output(int& minH,int& minS,LinkedStack<int> H[],int k,int n){
        int c; //记录车厢号
        H[minS].Pop(c); //缓冲轨栈顶元素出栈
        cout << "Move car " << minH << " from holding track “<< minS << " to output" << endl;
        minH = n + 2; //给缓冲轨中的最小车厢号设置一个不可能的数字,方便更新
        for(int I=1;i<=k;i++) //遍历所有的缓冲轨
            if(!H[I].IsEmpty()&&(c=H[I].Top())<minH){ //只需考虑每个缓冲轨的栈顶元素即可
                minH=c;
                minS=I;
            }
    } //O(k)
    
    //让入轨的车厢进入缓冲轨,如果无法进行该操作,应返回错误
    bool Hold(int c,int& minH,int& minS,LinkedStack<int> H[],int k,int n){
        int BestTrack=0, //可以放进去的最好的缓冲轨号码
            BestTop=n+1, //最好的缓冲轨上栈顶的车厢号
            x; //记录车厢号
        for(int I=1;i<=k;i++) //遍历所有的缓冲轨
            if(!H[I].IsEmpty()){ //该缓冲轨是否为空
                x=H[I].Top(); //该缓冲轨栈顶的车厢号
                if(c<x&&x<BestTop){ //如果目标车厢比该栈顶车厢号小,且该栈顶车厢号比最好的栈顶车厢号小
                    BestTop=x; //更新最好的缓冲轨
                    BestTrack=I;
                }
            }
            else //如果该缓冲轨为空
                if(!BestTrack) //如果还没找到可以放进去的缓冲轨
                    BestTrack=I; //最好的缓冲轨为该空轨
        if(!BestTrack) //如果还没找到可以放进去的缓冲轨
            return false; //返回无法放入缓冲轨,即重排失败
        H[BestTrack].Push(c); //让车厢进入最好的缓冲轨
        cout << "Move car " << c << " from input "<< "to holding track " << BestTrack << endl;
        if (c < minH) {
            minH = c; //更新缓冲轨中的最小车厢号
            minS = BestTrack;
        }
        return true;
    } //O(k)

队列的定义

  • 队列(queue)的插入和删除操作分别在表的不同端进行,队尾添加新元素,队首删除元素
  • 先进先出

队列的描述

公式化描述

template<class T>
class Queue {
public:
    Queue(int MaxQueueSize = 10);
    ~Queue() {delete [] queue;}
    bool IsEmpty() const { //如果队首和队尾位置相同,队列即为空
        return front == rear;
    } 
    bool IsFull() const {
        return (((rear + 1) % MaxSize == front) ? 1 : 0);
    }
    T First() const; //获取队首元素
    T Last() const;  //获取队尾元素
    Queue<T>& Add(const T& x); //在队尾增加元素
    Queue<T>& Delete(T& x); //删除队首元素
private:
    int front; //队首的位置
    int rear; //队尾的位置
    int MaxSize; //队列的最大长度
    T *queue; //一维数组
};

template<class T>
Queue<T>::Queue(int MaxQueueSize){ //构造函数
    MaxSize = MaxQueueSize + 1; //留出一个额外空间
    queue = new T[MaxSize];
    front = rear = 0; 
}

template<class T>
T Queue<T>::First() const{
    if (IsEmpty()) 
        throw OutOfBounds();
    return queue[(front + 1) % MaxSize]; 
}

template<class T>
T Queue<T>::Last() const{
    if (IsEmpty()) 
        throw OutOfBounds();
    return queue[rear]; 
}

template<class T>
Queue<T>& Queue<T>::Add(const T& x) {
    if (IsFull()) 
        throw NoMem(); 
    rear = (rear + 1) % MaxSize; 
    queue[rear] = x;
    return *this;
}

template<class T>
Queue<T>& Queue<T>::Delete(T& x){
    if (IsEmpty()) 
        throw OutOfBounds(); 
    front = (front + 1) % MaxSize;
    x = queue[front];
    return *this;
}

链表描述

两种指针方向

  • front ➡️ rear:入队和出队都是O(1)
  • rear ➡️ front:入队是O(1),出队是O(n),因为出队要更新front指针,但要找到front指针的前一个位置,需要遍历队列
template<class T> class LinkedQueue {
public:
    LinkedQueue() {front = rear = 0;} //构造函数初始化队列
    ~LinkedQueue();
    bool IsEmpty() const{
        return ((front) ? false : true);
    } 
    bool IsFull() const;
    T First() const;  
    T Last() const; 
    LinkedQueue<T>& Add(const T& x);
    LinkedQueue<T>& Delete(T& x); 
private:
    Node<T> *front; //头指针
    Node<T> *rear; //尾指针
};

template<class T> LinkedQueue<T>::~LinkedQueue(){ //析构函数
    Node<T> *next; 
    while (front) {
    next = front->link; 
    delete front;
    front = next;
    }
}

template<class T>
bool LinkedQueue<T>::IsFull() const {
    Node<T> *p;
    try {
        p = new Node<T>;
        delete p;
        return false;
    } catch (NoMem) { //若抛出异常,则队列已满
        return true;
    }
}

template<class T>
T LinkedQueue<T>::First() const{
    if (IsEmpty()) 
        throw OutOfBounds();
    return front->data; 
}

template<class T>
T LinkedQueue<T>::Last() const{
    if (IsEmpty()) 
        throw OutOfBounds();
    return rear->data; 
}

template<class T>
LinkedQueue<T>& LinkedQueue<T>::Add(const T& x){ //增加新元素
    Node<T> *p = new Node<T>; //新建节点并赋值
    p->data = x;
    p->link = 0; 
    if (front) //如果队列已被创建
        rear->link = p;
    else 
        front = p;
    rear = p; //更新尾节点
    return *this;
}

template<class T>
LinkedQueue<T>& LinkedQueue<T>::Delete(T& x){
    if (IsEmpty()) 
        throw OutOfBounds(); 
    x = front->data; //把要删除的元素存储在x中
    Node<T> *p = front;
    front = front->link; //更新首节点 
    delete p; //释放原队首的空间
    return *this;
}

小结:堆栈的两种实现方式比较

公式化链表
Create()O(1) / O(MaxSize)O(1)
Destroy()O(1) / O(MaxSize)O(n)
IsEmpty()O(1)O(1)
IsFull()O(1)O(1)
Top()O(1)O(1)
Push()O(1)O(1)
Pop()O(1)O(1)

队列的应用

火车车厢重排

bool RailRoad(int p[],int n,int k){
    LinkedQueue<int> *H; //创建链表数组做缓冲轨
    H = new LinkedQueue<int> [k];
    k--; //第k个缓冲轨不用
    int NowOut=1; //应该出轨的车厢号
    int minH=n+1; //所有缓冲轨上最小的车厢号
    int minQ; //拥有最小车厢号的缓冲轨
    for(int i=1;i<=n;i++){ //遍历所有入轨的车厢
        if(p[i]==NowOut){ //如果正好要出轨
            cout << "Move car”<<p[i]<<" from input to output" << endl;
            NowOut++; //更新下一个要出轨的车厢号
            while(minH==NowOut){ //如果缓冲轨上有要出轨的车厢
                Output(minH,minQ,H,k,n); //从缓冲轨上出轨
                NowOut++;
            }
        }
        else{ //如果入轨的车厢不应该出轨,则将其放入缓冲轨
            if(!Hold(p[i],minH,minQ,H,k,n)) //如果放入失败,则重排失败
                return false;
        }
    }
    return true;
}

//从缓冲轨中出轨
void Output(int& minH,int& minQ,queue<int> H[],int k,int n){
    int c; //记录车厢号
    H[minQ].Delete(c); //出轨
    minH=n+2; //准备重新寻找缓冲轨上最小车厢号
    for(int i=1;i<=k;i++){ //遍历所有缓冲轨
        if (!H[i].IsEmpty()&&(c = H[i].First()) < minH) { 
            minH = c; //更新最小车厢号的信息
            minQ = I;
        }
    }
}

//从入轨中放入缓冲轨,如果放入失败要返回错误
bool Hold(int c,int& minH,int& minQ,queue<int> H[],int k,int n){
    int BestTrack=0,BestLast=0,x;
    for(int i=1;i<=k;i++) //遍历所有缓冲轨,寻找最好的缓冲轨
        if(!H[I].IsEmpty()){ //缓冲轨是否为空
            x=H[I].last();
            if (c > x && x > BestLast) { //注意条件的设置,队列是队首先出
                BestLast = x; 
                BestTrack = I;
            }
        }
        else if (!BestTrack) //如果还没找到最好的缓冲轨
            BestTrack = I;
    if (!BestTrack) //如果所有缓冲轨都不能放入
        return false; 
    H[BestTrack].Add(c); //放入缓冲轨
    if (c < minH) { //更新缓冲轨上的最小车厢号
        minH = c; 
        minQ = BestTrack;
    }
    return true;
}

作业4

Josephus问题

/*
8
5
5 2 8 7 1 4 6 3
5 2 8 7 1 4 6 3
*/

template <typename T>
class linearList{
public:
    linearList(int n);
    linearList<T>& append(T x);
    linearList<T>& deleted(int k);
    void josephus(int m);
private:
    int length;
    T *p;
};
 
template <typename T>
linearList<T>::linearList(int n){
    length=0;
    p=new T[n];
}
 
template <typename T>
linearList<T>& linearList<T>::append(T x){
    int i=0;
    for(;i<length;i++);
    p[i]=x;
    length++;
    return *this;
}
 
template <typename T>
linearList<T>& linearList<T>::deleted(int k){
    for(int i=k+1;i<length;i++)
        p[i-1]=p[i];
    length--;
    return *this;
}
 
template <typename T>
void linearList<T>::josephus(int m){
    int mark=0;
    while(length!=1){
        for(int i=1;i<m;i++){
            mark++;
            if(mark==length)
                mark=0;
            else if(mark>length)
                mark=1;
        }
        cout<<p[mark]<<" ";
        deleted(mark);
    }
    cout<<p[0]<<endl;
}
 
template <typename T>
class chain{
    struct node{
        T data;
        node *next;
    };
public:
    node *head; //头节点
    chain(); //构造函数
    void append(T t); //将数据拼接在链表最后
    bool empty(); //判断链表是否为空
    void deleted(node *t); //删除元素
    void josephus(int n,int m);
};
 
template <typename T>
chain<T>::chain(){
    head=NULL;
}
 
template <typename T>
void chain<T>::append(T t){
    node *p=new node;
    p->data=t;
    p->next=NULL;
    if(head==NULL){ //如果链表还未被创建
        head=p;
        head->next=head; //头尾相连
        return;
    }
    node *tail=head;
    while(tail->next!=head) //找到尾节点所在
        tail=tail->next;
    tail->next=p; //将新节点接在最后面
    p->next=head; //头尾相连
}
 
template <typename T>
bool chain<T>::empty(){
    if(head)
        return false;
    else
        return true;
}
 
template <typename T>
void chain<T>::deleted(node* t){
    node* p=head;
    while(p->next->data!=t->data)
        p=p->next;
    p->next=t->next;
}
 
template <typename T>
void chain<T>::josephus(int n,int m){
    node *p=head;
    while(n!=1){ //当链表中的元素大于一个时
        for(int i=1;i<m;i++)
            p=p->next; //找到第m个元素
        node *t=p;
        p=p->next; //指针继续寻找
        cout<<t->data<<" "; //输出第m个元素
        deleted(t); //删除该元素
        n--;
    }
    cout<<p->data<<endl; //输出最后一个元素
}
 
int main(){
    int n,m;
    cin>>n>>m;
    if(n>=3&&n<=100&&m>=1&&m<=n);
    else{
        cout<<"WRONG!";
        return 0;
    }
    linearList<int> l(n);
    for(int i=1;i<=n;i++)
        l.append(i);
    l.josephus(m);
    chain<int> c;
    for(int i=1;i<=n;i++)
        c.append(i);
    c.josephus(n,m);
    cout<<endl;
    return 0;
}

删除栈中所有等于x的数据项,保持其他数据项顺序不变

/*
a
b a t a a e c
c e t b
*/
 
template<typename T>
class Stack{ //虚基类栈定义
public:
    virtual bool empty() const=0; //检查栈是否为空
    virtual int getSize() const=0; //返回栈的大小
    virtual T& getTop()=0; //返回栈顶指针
    virtual void pop()=0; //出栈
    virtual void push(const T& t)=0; //进栈
};
 
template<typename T>
class linkedStack:public Stack<T>{ //用链表实现栈
    struct node{ //节点定义
        T data;
        node *next;
    };
public:
    linkedStack(int capacity=10){ //构造函数
        top=NULL;
        size=0;
    }
    bool empty() const{
        return size==0;
    }
    int getSize() const{
        return size;
    }
    T& getTop(){
        if(size==0)
            cout<<"The stack is empty!"<<endl;
        return top->data;
    }
    void pop(){
        if(size==0){
            cout<<"The stack is empty!"<<endl;
            return ;
        }
        cout<<top->data;
        node *p=top->next;
        top=p;
        size--;
    }
    void push(const T& t){
        node *p=new node;
        p->data=t;
        p->next=top;
        top=p; //更新栈顶指针
        size++;
    }
    node* find(int& k,T t){ //从第k个元素开始找数据为t的元素,返回其节点
        node *p=top;
        for(int i=1;i<k;i++)
            p=p->next; //找到第k个元素
        while(p){ //当p不为NULL时
            if(p->data==t){
                k--; //调整k的值,以便下一次寻找
                return p;
            }
            p=p->next;
            k++;
        }
        return p;
    }
    void deleted(node *t){ //删除节点
        if(t==NULL)
            return ;
        if(t==top){ //删除栈顶,直接pop即可
            pop();
            return ;
        }
        node *p=top;
        while(p->next!=t)
            p=p->next; //找到节点的前一个
        p->next=t->next;
        size--;
    }
private:
    node *top; //栈顶指针
    int size; //栈的大小
};
 
//删除栈中所有等于x的数据项,保持其他数据项顺序不变
template<typename T>
void delete_all(linkedStack<T>&s,const T&x){
    for(int i=1;i<=s.getSize();i++)
        if(s.find(i,x)) //从第i个元素开始寻找,如果有等于x的数据项
            s.deleted(s.find(i,x)); //删除该数据项
        else //返回节点为NULL,即栈中没有等于x的数据项
            break; //跳出循环
}
 
int main(){
    linkedStack<char> l;
    char c;
    cout<<"Please input data of the stack!"<<endl;
    for(int i=1;i<=7;i++){ //获取数据
        cin>>c;
        l.push(c);
    }
    cout<<"Please input the element you want to delete: ";
    cin>>c;
    delete_all(l, c);
    while(l.getSize()){ //依此出栈
        l.pop();
        if(l.getSize())
            cout<<" ";
    }
    cout<<endl;
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值