数据结构(栈和队列)

是限定仅在表尾进行插入和删除操作的线性表。表尾端称为栈顶,表头端称为栈底。不含元素的空表称为空栈。
栈是后进先出的线性表。

队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。队列是先进先出的线性表。

1. 栈

(1) 顺序栈的表示和实现

顺序栈是指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。另设指针base指示栈底元素在顺序栈中的位置。

当top和base的值相等时,表示空栈。

1. 顺序栈的存储结构

#define MAXSIZE 100 /* 存储空间初始分配量 */ 
typedef int Status;
 typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */ 
/* 顺序栈结构 */
typedef struct{        
    SElemType *base;//栈底指针
    SElemType *top;//栈顶指针
    int stacksize;//栈可用的最大容量
}

base指针的值为NULL,则表明栈结构不存在。
栈非空时,top始终指向栈顶元素的上一个位置。

2. 顺序栈的初始化

  • 为顺序栈动态分配一个最大容量为MAXSIZE的数组空间,使base指向这段空间的基地址,即栈底。
  • 栈顶指针top初始值为base。
  • stacksize置为栈的最大容量MAXSIZE。

Status InitStack(SqStack &S)
{
    S.base = new SElemType[MAXSIZE]; //为顺序栈动态分配一个最大容量为MAXSIZE的数组空间
    if(!S.base) exit(OVERFLOW);//存储分配失败
    S.top = S.base;//top初始为base,空栈
    S.stacksize = MAXSIZE;// stacksize置为栈的最大容量MAXSIZE
    return OK;
}

3. 顺序栈的入栈

  • 判断栈是否为满,若满则返回ERROR。
  • 将新的元素压入栈顶,栈顶指针加1。

Status Push(SqStack &S,SElemType e)
{
    //  插入元素e为新的栈顶元素
    if(S.top - S.base == S.stacksize) return ERROR;//栈满
    *S.top ++ = e; //元素e压入栈顶,栈顶指针加1
    return OK;
}

4.出栈

  • 判断栈是否为空,若空则返回ERROR。
  • 栈顶指针减1,栈顶元素出栈。

Status Pop(SqStack &S,SElemType &e)
{
    //删除S的栈顶元素,用e 返回其值
    if(S.top == S.base) return ERROR ; // 栈空
    e = *--S.top; //栈顶指针减1,将栈顶元素赋值给e
    return OK;
}

5. 取栈顶元素

当栈非空时,此操作返回当前栈顶元素的值,栈顶保持不变。

Status GetTop(SqStack S)
{
    if (S.top ! = S.base)  //栈非空
        return *(S.top -1);//返回栈顶元素的值,栈顶指针不变。
    
}

(2) 链栈的表示和实现

链栈是指采用链式存储结构实现的栈。


1. 链栈的存储结构


typedef struct StackNode
{
        SElemType data;
        struct StackNode *next;

}StackNode,*LinkStackPtr;

2. 链栈的初始化

链栈的初始化操作就是构造一个空栈,因为没必要设头结点,所以直接将栈顶指针置空即可。

Status InitStack(LinkStack &S)
{//构建一个空栈S,栈顶指针置空
    S = NULL:
    return OK;
}

3. 链栈的入栈

  • 为栈元素e分配空间,用指针p指向。
  • 将新结点数据域置为e。
  • 将新结点插入栈顶
  • 修改栈顶指针为p。
Status Push(LinkStack &S,SElemType e)
{
    p = new StackNode; //生成新结点
    p->data  = e;//将新结点数据域置为e
    p->next = S;//将新结点插入栈顶
    S = p;//修改栈顶指针为p
    return OK;
}

4. 链栈的出栈

  • 判断栈是否为空,若空则返回ERROR。
  • 将栈顶元素赋值给e。
  • 临时保存栈顶元素的空间,以备释放。
  • 修改栈顶指针,指向新的栈顶元素。
  • 释放元栈顶元素

Status Pop(LinkStack &S,SElemType &e)
{
      //删除S的栈顶元素,用e返回其值。
      if(S==NULL) return ERROR;
      e = S -> data;
      p = S;
      S = S->next;
      delete p;
      return OK;
 }

5. 取栈顶元素

SElemType GetTop (LinkStack S)
{
    if(S != NULL) //栈非空
        return S->data;//返回栈顶元素,栈指针不变。
}

(3) 栈与递归

Hanoi塔问题

  1. 每次只能移动一个圆盘。
    2.圆盘可以插在A、B和C中的任一塔座上。
  2. 任何时刻都不能将一个较大的圆盘压在较小的圆盘之上。


Hanoi塔问题的递归算法

  • 如果 n=1,则直接将编号为1的圆盘从A移到C,递归结束。
  • 否则:

1.递归,将A上编号为1至n-1的圆盘移到B,C做辅助塔。

  1. 直接将编号为n的圆盘从A移到C。
  2. 递归,将B上编号为1至n-1的圆盘移动到C,A做辅助塔。
void Hanoi(int n,char A,,char B,char C)
{
    if (n == 1) move(A,1,C); //将编号为1的圆盘从A移到C
    else
    {
          Hanoi(n-1,A,C,B); //将A上编号为1至n-1的圆盘移到B,C做辅助
          move(A,n,C);//将编号为 n的圆盘从A移到C
          Hanoi(n-1,B,A,C);//将B上编号为1至n-1的圆盘移到C,A做辅助
    }
}

/* 将搬动操作定义为move(A,n,C),是指将编号为n的圆盘从A移动到C,同时设置一个初始值为0的全局变量m,对搬动进行计数。*/
int m;
void move(char A,int n,char C)
{
    count<<++m<<","<<n<<","<<A<<","<<C<<endl;
}

2. 队列

(1) 循环队列-队列的顺序表示和实现

1. 队列的顺序存储结构

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
 typedef int Status;
 typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
 /* 循环队列的顺序存储结构 */
typedef struct{    
    QElemType *base /*存储空间的基地址*/
    int front;        /* 头指针 */
    int rear;        /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */

  1. 附设两个整型变量front和rear指针分别指示队列头元素及队列尾元素的位置。
  2. 初始化创建空队列时,令front = rear = 0
  3. 每当插入新的队列尾元素时,尾指针rear增1,每当删除队列头元素时,头指针front增1.
  4. 在非空栈中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置。

循环队列,解决假溢出问题

队空队满的判断:

  1. 少用一个元素空间。即队列空间大小为m时,由m-1个元素就认为是队满。
  2. 当头、尾指针相等时,队空。当尾指针在循环意义上加1后是等于头指针,则认为队满。
    队空条件:Q.front == Q.rear
    队满条件:(Q.rear+1)%MAXQSIZE == Q.front

2. 循环队列的初始化

  • 为队列分配一个最大容量的MAXSIZE的数组空间,base指向数组空间的首地址。
  • 头指针和尾指针为零,表示队列为空。

/* 初始化一个空队列Q */
Status InitQueue(SqQueue *Q)
{
        Q.base = new QElemType[MAXQSIZE]; //为队列分配一个最大容量为MAXQSIZE的数组空间
        if (!Q.base) exit(OVERFLOW);
        Q->front =Q->rear=0;
        return  OK;
}

3. 求循环队列的长度

/* 返回Q的元素个数,也就是队列的当前长度 */
int QueueLength(SqQueue Q)
{
    return  (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}

4. 循环队列的入队
  • 判断队列是否满,若满则返回ERROR。
  • 将新元素插入队尾。
  • 队尾指针加1。
Status EnQueue(SqQueue &Q,QElemType e){ 
    if ((Q.rear+1)%MAXSIZE == Q.front)  /* 队列满的判断 */        
        return ERROR;   
    Q.base[Q.rear]=e;           /* 将元素e赋值给队尾 */ 
    Q->rear=(Q.rear+1)%MAXSIZE;/* rear指针向后移一位置, */                                  
    return  OK;
}

5. 循环队列的出队

  • 判断队列是否为空,若为空则返回ERROR。
  • 保存队列头元素。
  • 对头指针加1。
Status DeQueue(SqQueue &Q,QElemType &e){    
    if (Q.front == Q.rear)          /* 队列空的判断 */            
        return ERROR;   
    e=Q.base[Q.front];              /* 将队头元素赋值给e */
    Q.front=(Q.front+1)%MAXSIZE;    /* front指针向后移一位置,若到最后则转到数组头部 */ 
    return  OK;
}

6. 取循环队列的对头元素

SElemType GetHead(SqQueue Q)
{
    if(Q.front != Q.rear)//队列非空
        return Q.base[Q.front];//返回队头元素的值,队头指针不变。
}

(2) 链队-队列的链式表示和实现

链队采用链式存储结构实现的队列。为了方便操作,给链队添加一个头结点,并令头指针始终指向头结点。


1. 队列的链式存储结构

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */ 
typedef int Status;  
typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ 
typedef struct QNode     /* 结点结构 */
{   
    QElemType data;   
    struct QNode *next;
}QNode,*QueuePtr; 
typedef struct          /* 队列的链表结构 */
{   
    QueuePtr front,rear; /* 队头、队尾指针 */
}LinkQueue;
 

2. 链队的初始化

  • 生成一个新结点作为头结点,对头和队尾指针指向此结点。
  • 头结点的指针域置空。
Status InitQueue(LinkQueue &Q)
{
    //构造一个空队
    Q.front = Q.rear = new QNode;
    Q.front->next = NULL;
    return OK;
}

3. 链队的入队

  • 为元素分配新的结点空间,用指针p指向。
  • 将新结点数据域设置为e。
  • 将新结点插入到队尾。
  • 修改队尾指针为p。
Status EnQueue(LinkQueue &Q,QElemType e)
{   
    p =  new QNode;
    p->data=e;  
    p->next=NULL;   
    Q.rear->next=p;
    Q.rear = p          
    return OK;
}

4. 链队的出队

  • 判断队列是否为空,若空则返回ERROR。
  • 临时保存队列的头元素,以备释放空间。
  • 修改队头指针,指向下一个结点。
  • 判断出队元素是否为最后一个元素,若是,则将队尾指针重新赋值,指向队头结点。
  • 释放元队头元素空间。
Status DeQueue(LinkQueue &Q,QElemType &e)
{   
    QueuePtr p; 
    if(Q.front==Q.rear)     
        return ERROR;   
    p=Q.front->next;        //p指向队头元素   
    e=p->data;          //e保存队头元素的值     
    Q.front->next=p.next;   //修改头指针
    if(Q.rear==p)   //最后一个元素被删,队尾指针指向队头指针   
        Q.rear=Q.front; 
    delete p;   
    return OK;
}
 

4. 取链队的队头元素

SElemType GetHead(LinkQueue Q)
{
    if(Q.front != Q.rear) 
        return Q.front->next->data;
}

(3) 循环队列和链队列的比较

  1. 时间上,基本操作都是常数时间,即O(1),不过,循环队列是事先申请好空间,使用期间不释放,而对于链队列,每次申请和释放结点会存在一些时间开销,如果入队出队频繁,则两者还是略有差异。
  2. 空间上,循环队列必须有固定的长度,所以就有了存储元素个数和空间浪费的问题。而链队列不存在这样的问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链队列更加灵活。
  3. 总之,在可以确定队列长度最大值的情况下,建议用循环队列,如果无法预估队列的长度,则用链队列。

插入元素时间复杂度为O(1),删除时是O(n),因为后面的所有元素要向前移;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容