《数据结构》复习之线性表(栈和队列)

  • 栈和堆的基本概念
    • 1栈的特点
    • 2队列的特点
  • 数据结构和算法
    • 1栈的数据结构和算法
    • 2队列的数据结构和算法
  • 相关应用
    • 1栈的应用输出八进制数
    • 2队列的应用猴子选大王
  • 总结

1.栈和堆的基本概念

  栈和队列都是操作受限制线性表。由于都是线性表,后面也会介绍它们的实现都有顺序和链式两种结构;由于操作受到限制,因此它们各自有各自的特点。

1.1栈的特点

  栈是一种只能在一端进行插入或删除操作的线性表,这一端被称为栈顶,相对地,把另一端称为栈底。它的主要特点就是先进先出(FILO—First-In/Last-Out),如图所示。它的原理好比开进一个死胡同的多辆汽车,最先开进去的汽车只能最后出来。

1.2队列的特点

  队列式一钟允许在表的一端进行插入,而在表的另一端进行删除的线性表,它的特点概括起来就是先进先出(FIFO—first in first out)。就如它的名字一样,原理好比排队,最先进入队列的人总是第一个出队。

2.数据结构和算法

2.1栈的数据结构和算法

  栈的数据结构主要有顺序栈和链栈。最常用的应该是顺序栈。
  顺序栈可以简单的用数组表示。定义如下

typedef struct
{
    int data[maxSize];
    int top;
}SqStack;

  对于顺序栈St的两个状态和两个操作。
  栈空St.top==-1
  栈满St.top==maxSize-1
  进栈++St.top;St.data[St.top]=x;
  出栈x=St.data[St.top];--St.top;
  当然上面只是举例,不同的情况可以做相应的改变。其实更为常见的情况应该是对于不同类型的数据,new相应的数组,然后用指针来指示栈顶。
  栈的另一种实现链栈,链栈的结构与链表是一样的。进栈相当于将元素插入到头结点之后(头插法),而出栈相当于将头结点之后的那个元素删除。这里不再赘述。

2.2队列的数据结构和算法

  队列和栈一样,有顺序队列和链队。
  顺序队列我感觉用处不大,毕竟太浪费空间。更加有用的应该是循环队列(当然循环队列也有数组实现和链表实现,我们这里主要讲数组的实现)。
  循环队列示意图如下。
  《数据结构》复习之线性表(栈和队列)_第1张图片
  就像右图所示它其实就是一个数组,但入栈和出栈的方式让指示器(front和rear)一直在这个数组范围内移动(从0递增7再变为0递增,一直循环下去),这样就不会像顺序队列一样空间被浪费。
  循环队列中,由于入队时尾指针向前追赶头指针;出队时头指针向前追赶尾指针,造成队空和队满时头尾指针均相等。因此,无法通过条件front==rear来判别队列是”空”还是”满”。
  因此我们设置
  队空状态:rear==front
  队满状态:(rear+1)%maxSize==front(其实并未满,损失了一个空间)
  进队操作:rear=(rear+1)%maxSzie;data[rear]=x;
  出队操作:front=(front+1)%maxSize;x=data[front];
  这里我们先移动指针再存取元素,当然也可以先存取元素在移动指针。
  
  队列的另一种实现是链队。结构和链表一样。只是有两个指针指向链头(链表中的第一个元素)和链尾(链表中的最后一个元素),如下图所示。队空时两个指针均为null,进队时链尾指针操作,相当于从链表的最后插入元素(要单独考虑没有元素的时候),出队就是删除第一个元素(要单独考虑只有一个元素的时候和没有元素的时候)。由于时间原因,具体过程不再赘述。
  

3.相关应用

3.1栈的应用(输出八进制数)

  栈的应用有很多,包括大到语言的函数调用和递归等实现,小到表达式求解等应用。
  归纳一下,如果一个问题中出现这种情况:
  即在解决问题的过程中出现了一个状态,但凭借现有条件不能判断当前的状态是否可以解决,需要记下等待以后出现可以解决当前状态的条件后返回来解决。这种问题需要用到栈来解决,栈具有记忆功能。
  这里用一个比较简单的程序八进制输出(输入一个十进制数,输出其八进制数)来演示,用到栈是C++ STL中stack。
  

#include<iostream>
#include<stdio.h>
#include<stack>
#include<stdlib.h>
using namespace std;

int main()
{
    stack <int> s;
    int num;
    while(scanf("%d",&num)!=EOF)
    {
        while(num)
        {
            s.push((num%8));
            num=num/8;
        }
        while(!s.empty())
        {
            cout<<s.top();
            s.pop();
        }
        cout<<endl;
    }
    return 0;
}

3.2队列的应用(猴子选大王)

  n只猴子围坐成一个圈,按顺时针方向从1到n编号。然后从1号猴子开始沿顺时针方向从1开始报数,报到m的猴子出局,再从刚出局猴子的下一个位置重新开始报数,如此重复,直至剩下一个猴子,它就是大王。设计并编写程序,实现如下功能:
  (1) 要求由用户输入开始时的猴子数n、报数的最后一个数m。
  (2) 给出当选猴王的初始编号。
  这道题(第一次碰到这题是一场在保研的机试上,当时竟然不会做)放在这边不知道是不是合适,因为它并没有用到队列的出队入队,但它很好的解释了循环队列的思想。
  代码如下:

#include <iostream>
using namespace std;


int main()
{
    int n=0, m=0;
    cin >> n >> m;
    int x = n;   //x记录猴子的个数
    int *A = new int[n];
    int i = 0;
    for (i = 0; i<n;i++)   //设置好编号
    {
        A[i] = i + 1;
    }
    i = 0;
    int count = 0;   //计数器,从1-m
    while (x > 1)   //是否只剩下一只猴子
    {
        if (A[i]==-1)    //判断当前的猴子是否淘汰出局
        {
            i = (i + 1) % n;   //已经淘汰出局直接走向下一只
            continue;
        }
        else
        {
            if (count==m-1)   //若正好报道m,(由于从0开始所以要减1)
            {
                A[i] = -1;   //该猴子淘汰
                i = (i + 1) % n;   //到下一个猴子
                count = 0;  //计数器置为0
                x--;  //猴子数量减少一只
            }
            else   //报道的不是m,计数器+1,猴子到下一只
            {
                count++;  
                i = (i + 1) % n;
            }
        }
    }
    for (i = 0; i < n;i++)   //遍历,不是-1的猴子就是最后留下来的猴子
    {
        if (A[i]!=-1)
        {
            cout << A[i] << endl;
        }
    }
    return 0;
}

4.总结

  栈和队列还是比较简单的,特别是在实现上(这也让我在写的时候不是很情愿,因为感觉太简单,没有必要要写,而且实现不管是java还是C++都有相应的系统库)。但应用范围很广,比如毕设做的Java虚拟机,栈可是其中很重要的数据结构。最重要的是理解栈和队列的思想,并学会如何应用。

你可能感兴趣的:(数据结构,栈,线性表)