栈(基于数组&基于链表)与队列(基于数组&基于链表)

时间:2022-11-28 15:39:35

一、栈

    1、栈(stack)是一种线性存储结构

      • 栈中的数据元素遵守先进后出的原则,FILO结构
      • 限定只能在栈顶进行插入与删除操作
      • 栈的操作包括压栈出栈

    2、栈的常用操作

      • 压栈(push)
      • 弹栈或出站(pop)
      • 求栈的大小
      • 判断栈是否为空
      • 获取栈顶元素的值

    3、栈的常见分类

      • 基于数组的栈,以数组为底层数据结构,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向
      • 基于单链表的栈,以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的结点将一直出现在链表的头部

    4、使用标准库的栈时,需要包含对应的头文件,

  • 1 #include<stack>
    2 stack<int>  s;
    3 s.empty();
    4 s.size();
    5 s.top();
    6 s.push();
    7 s.pop();

 

  • 基于数组的栈
    •  1 #include <stack>
       2 #include <iostream>
       3 using namespace std;
       4  
       5 int main()
       6 {
       7     stack<int> mystack;
       8     int sum = 0;
       9     for (int i = 0; i <= 10; i++){
      10         mystack.push(i);
      11     }
      12     cout << "size is " << mystack.size() << endl;
      13     while (!mystack.empty()){
      14         cout << " " << mystack.top();
      15         mystack.pop();
      16     }
      17     cout << endl;
      18     system("pause");
      19     return 0;
      20 }
      21 //size is 11
      22 // 10 9 8 7 6 5 4 3 2 1 0
  • 基于单链表的栈
    •  1 #include <iostream>
       2 using namespace std;
       3 template<class T>class Stack
       4 {
       5 private:
       6     struct Node
       7     {
       8         T data;
       9         Node *next;
      10     };
      11     Node *head;
      12     Node *p;
      13     int length;
      14  
      15 public:
      16     Stack()
      17     {
      18         head = NULL;
      19         length = 0;
      20     }
      21     void push(T n)//入栈
      22     {
      23         Node *q = new Node;
      24         q->data = n;
      25         if (head == NULL)
      26         {
      27             q->next = head;
      28             head = q;
      29             p = q;
      30         }
      31         else
      32         {
      33             q->next = p;
      34             p = q;
      35         }
      36         length++;
      37     }
      38  
      39     T pop()//出栈并且将出栈的元素返回
      40     {
      41         if (length <= 0)
      42         {
      43             abort();
      44         }
      45         Node *q;
      46         int data;
      47         q = p;
      48         data = p->data;
      49         p = p->next;
      50         delete(q);
      51         length--;
      52         return data;
      53     }
      54     int size()//返回元素个数
      55     {
      56         return length;
      57     }
      58     T top()//返回栈顶元素
      59     {
      60         return p->data;
      61     }
      62     bool isEmpty()//判断栈是不是空的
      63     {
      64         if (length == 0)
      65         {
      66             return true;
      67         }
      68         else
      69         {
      70             return false;
      71         }
      72     }
      73     void clear()//清空栈中的所有元素
      74     {
      75         while (length > 0)
      76         {
      77             pop();
      78         }
      79     }
      80 };
      81 int main()
      82 {
      83     Stack<char> s;
      84     s.push('a');
      85     s.push('b');
      86     s.push('c');
      87     while (!s.isEmpty())
      88     {
      89         cout << s.pop() << endl;
      90     }
      91     system("pause");
      92     return 0;
      93 }

       




 

二、队列

    1、队列与栈一样是一种线性存储结构

      • 队列中的数据元素遵循先进先出的原则即FIFO原则
      • 在队尾添加元素,在队头删除元素
      • 队的操作包括入队和出队

    2、队列的操作

      • 入队(push)
      • 出队(pop)
      • 求队列中元素的个数
      • 判断队列是否为空
      • 获取队首元素

    3、队列的分类

      • 基于数组的循环队列(循环队列)
      • 基于链表的队列(链队列)

    4、C++队列queue模板类定义在<queue>头文件中,queue模板类需要两个模板参数,一个是元素类型,一个是容器类型。元素类型是必要的,容器类型是可选的,默认为deque类型。C++队列Queue是一种容器适配器,它给予程序员一种先进先出(FIFO)的数据结构。

  • 如何判断队列是空队列还是已满呢
      • 栈空:队首标志=队尾标志,表示栈空
      • 栈满:队尾+1=队首时,表示栈满
  • 使用标准库的队列时,应包含相关头文件,
    • #include<queue>
      queue<int> q;
      q.empty();
      q.size();
      q.push();
      q.pop();
      q.front();
      q.back();

       


    • 基于数组的循环队列
      • 以数组作为底层数据结构时,一般将队列实现为循环队列。这是因为队列在顺序存储上的不足:每次从数组头部删除元素(出队)后,需要将头部以后的所有元素往前移动一个位置,这是一个时间复杂度为O(n)的操作。
      • 循环队列可以将数组看成一个首位相连的圆环,删除元素时将队首标志往后移动,添加元素时若数组尾部已经没有空间,则考虑数组头部的空间是否空闲,如果是则在数组头部进行插入。
      •  1 #include <queue>
         2 #include <iostream>
         3 using namespace std;
         4  
         5 int main(){
         6     queue<int> q;
         7     for (int i = 0; i < 10; i++){
         8         q.push(i);
         9     }
        10     if (!q.empty()){
        11         cout << "队列q非空!" << endl;
        12         cout << "q中有" << q.size() << "个元素" << endl;
        13     }
        14     cout << "队头元素为:" << q.front() << endl;
        15     cout << "队尾元素为:" << q.back() << endl;
        16     for (int j = 0; j < 10; j++){
        17         int tmp = q.front();
        18         cout << tmp << " ";
        19         q.pop();
        20     }
        21     cout << endl;
        22     if (!q.empty()){
        23         cout << "队列非空!" << endl;
        24     }
        25     system("pause");
        26     return 0;
        27 }
      • 循环队列基于数组的C++实现代码如下:
      •   1 #include <iostream>
          2 #include <queue>
          3 #include <string>
          4 using namespace std;
          5  
          6 template <typename T>
          7 class LoopQueue
          8 {
          9 public:
         10     LoopQueue(int c = 10);
         11     ~LoopQueue();
         12     bool isEmpty();        //队列的判空
         13     int size();            //队列的大小
         14     bool push(T t);        //入队列
         15     bool pop();            //出队列
         16     T front();            //队首元素
         17  
         18 private:
         19     int capacity;
         20     int begin;
         21     int end;
         22     T*  queue;
         23 };
         24  
         25  
         26 template<typename T>
         27 LoopQueue<T>::LoopQueue(int c = 10)
         28     :capacity(c), begin(0), end(0), queue(nullptr)
         29 {
         30     queue = new T[capacity];
         31 };
         32  
         33 template<typename T>
         34 LoopQueue<T>::~LoopQueue()
         35 {
         36     delete[]queue;
         37 }
         38  
         39 template <typename T>
         40 bool LoopQueue<T>::isEmpty()                   //判断循环队列是否为空
         41 {
         42     if (begin == end)
         43         return true;
         44     return false;
         45 };
         46  
         47 template<typename T>
         48 int LoopQueue<T>::size()
         49 {
         50     return (end - begin + capacity) % capacity; //计算循环队列的长度
         51 };
         52  
         53 template<typename T>
         54 bool LoopQueue<T>::push(T t)
         55 {
         56     if (end + 1 % capacity == begin)            //判断队列是否已满
         57     {
         58         return false;
         59     }
         60     queue[end] = t;
         61     end = (end + 1) % capacity;
         62     return true;
         63 };
         64  
         65 template <typename T>
         66 bool LoopQueue<T>::pop()                        //判断队列是否为空
         67 {
         68     if (end == begin) 
         69     {
         70         return false;
         71     }
         72     begin = (begin + 1) % capacity;
         73     return true;
         74 };
         75  
         76 template <typename T>
         77 T LoopQueue<T>::front()
         78 {
         79     if (end == begin)
         80     {
         81         return false;
         82     }
         83     return queue[begin];
         84 };
         85  
         86 int main()
         87 {
         88     LoopQueue<string> queue(6);
         89     queue.push("one");
         90     queue.push("two");
         91     queue.push("three");
         92     queue.push("four");
         93     queue.push("five");
         94     cout << "队列长度" << queue.size() << endl;
         95     while (!queue.isEmpty())
         96     {
         97         cout << queue.front() << endl;
         98         queue.pop();
         99     }
        100     getchar();
        101     //system("pause");
        102     return 0;
        103 }

    • 基于链表的队列(链队列)
      • 链队列是基于链表实现的队列,它不存在数组的O(n)的元素移动问题和内存空间浪费问题。我们所要确定的就是链表哪头做队首,哪头做队尾。显然我们应该将链表头做队首,链表尾做队尾。存储一个指向队尾的指针,方便从链表尾插入元素,使用带头结点的链表,方便从链表头删除元素。
      • 链式队列的C++实现
        •   1 #include <iostream>
            2 #include <cstdlib>
            3 using namespace std;
            4  
            5 struct QNode    //定义队列结点的数据结构
            6 {
            7     QNode *next; //指针域,指向下一个结点
            8     double data;    //数据域,存储队列信息
            9 };
           10  
           11 struct LinkQueue    //定义队列的数据结构
           12 {
           13     QNode *front;      //队首指针,指向QNode类型的指针
           14     QNode *rear;       //队尾指针
           15 };
           16  
           17 void InitQueue(LinkQueue &Q)     //构造一个空的队列
           18 {
           19     QNode *q;
           20     q = new QNode;    //申请一个结点的空间
           21     q->next = NULL;   //当作头结点
           22     //队首与队尾指针都指向这个结点,指针域为NULL
           23     Q.front = q;
           24     Q.rear = q;
           25 }
           26  
           27 int IsEmpty(LinkQueue &Q)    //判断队列是否为空
           28 {
           29     if (Q.rear == Q.front)
           30         return 0;
           31     else
           32         return 1;
           33 }
           34  
           35 void EnQueue(LinkQueue &Q, double e)     //从队列尾部插入元素
           36 {
           37     QNode *p;    //新创建一个结点
           38     p = new QNode;
           39     p->next = NULL;
           40     p->data = e;  //输入数据信息
           41     //将新结点插入队列尾部
           42     Q.rear->next = p;
           43     Q.rear = p;       //设置新的尾结点
           44 }
           45  
           46 void DeQueue(LinkQueue &Q, double &e)   //从队列首部删除一个结点
           47 {
           48     QNode *p;
           49     p = Q.front->next;
           50     e = p->data;    //保存要出队列的数据
           51     Q.front->next = p->next;       //将下一个结点当作头结点后面链接的第一个结点
           52     if (Q.rear == p)    //如果要删除的元素即为尾结点,则将头指针赋予尾指针,一同指向头结点,表示队列为空
           53         Q.rear = Q.front;
           54     delete p;
           55 }
           56  
           57 void DestoryQueue(LinkQueue &Q)       //销毁一个队列
           58 {
           59     while (Q.front)
           60     {
           61         Q.rear = Q.front;    //从头节点开始,一个一个删除队列结点,释放空间
           62         delete Q.front;
           63         Q.front = Q.rear;
           64     }
           65 }
           66 int main()
           67 {
           68     LinkQueue *Q;  //定义一个队列Q
           69     Q = new LinkQueue;
           70     InitQueue(*Q);
           71     cout << "开始往队列里输入数据,以-1作为结束符" << endl;
           72     cout << "请输入一个数:" << endl;
           73     double a, x;
           74     cin >> a;
           75     while (a != -1)
           76     {
           77         EnQueue(*Q, a);
           78         cout << "请输入一个数:" << endl;
           79         cin >> a;
           80     }
           81     //输出队列元素,队首->队尾
           82     QNode *p;
           83     p = Q->front->next;
           84     if (p == NULL)     //如果为空表,直接退出
           85     {
           86         cout << "队列为空!" << endl;
           87         return 0;
           88     }
           89     cout << "队列数据依次为:" << endl;
           90     while (p != NULL)
           91     {
           92         cout << p->data << " ";
           93         p = p->next;
           94     }
           95     cout << endl;
           96     //删除队列元素
           97     while (!IsEmpty(*Q))
           98     {
           99         DeQueue(*Q, x);
          100         cout << x << " ";
          101     }
          102     //释放内存空间
          103     delete Q->front;
          104     delete Q;
          105     system("pause");
          106     return 0;
          107 }

           

        • 用单链表表示的链式队列特别适合于数据元素变化较大的情况,而且不存在溢出的情况。
        • 循环队列中判断队空的方法是front==rear,队满的方法是判断front=(real+1)%MAXSIZE。(为什么不用一个length表示队长,当length=MAXSIZE时表示队满,原因就是,在频繁的队列操作中多出一个变量会大量的增加执行时间,所以不如浪费一个数组空间划算。)