数据结构第三章——栈和队列

本文详细介绍了数据结构中的栈和队列,包括它们的定义、性质、操作实现以及在进制转换、括号匹配检验和表达式求值等实际问题中的应用。详细讨论了顺序栈和链栈的表示与操作,如栈的类型定义、入栈、出栈、栈的初始化和销毁等,并给出了相应的算法补充。同时,还阐述了队列的实现,如循环队列和链队的操作,以及队列在解决假上溢问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

“要像星星一样发光。”


目录

3.1 栈和队列

3.2 案例引入

3.3 栈的表示和操作的实现

3.3.1 栈的类型定义

3.3.2 顺序栈的表示与实现 

【代码部分】

1  定义

2 初始化

3 入栈

4 出栈

【算法补充】

1 顺序栈判断是否为空

2 求顺序栈长度

3 清空顺序栈

4 销毁顺序栈

3.3.2 链栈的表示与实现 

1 定义

2 初始化

3 入栈

4 出栈

5 取栈顶元素 

【算法补充】

1 判断链栈是否为空 

3.4 栈与递归

3.5 队列的表示和操作的实现

 3.5.1  队列(Queue)

【代码部分】

1 顺序队列

1)循环队列——定义

2)循环队列——初始化

2)循环队列——求队列长度

3)循环队列——入队

4)循环队列——出队

5)循环队列——取队头元素

2 链队

1)类型定义

2)初始化

3)入队

4)出队

5)取队头元素

6)销毁队列(补充)

3.6 案例分析与实现




3.1 栈和队列

定义:栈和队列是插入和删除只能在表的“端点”进行的线性表

  • 栈——后进先出
  • 队列——先进先出

3.2 案例引入

1 进制转换

【例】把十进制数159转换成八进制数。

159_{(10)}=237_{(8)}

转换法则:除以d倒取余

n=(n div d)*d+n mod d 

(div为整除运算,mod为求余运算)

 2 括号匹配的检验

【例】检验 ( ( )]  )是否匹配

3 表达式求值

3.3 栈的表示和操作的实现

3.3.1 栈的类型定义

ADT Stack {

数据对象:D={ ai | ai∈ElemSet, i=1,2,…,n,n≥0 }

数据关系:R1={ < ai-1 , ai > | ai-1,ai∈D, i=2,…,n } (约定an端为栈顶,a1端为栈底)

}ADT Stack

1 基本操作:

  • InitStack(&S)  初始化操作
  • DestroyStack(&S)  销毁栈操作
  • StackEmpty(S)  判断S是否为空栈
  • StackLength(S)  求栈的长度
  • GetTop(S,&e)  取栈顶元素
  • ClearStack(&S)  栈置空操作
  • Push(&S,e)  入栈操作
  • Pop(&S,&e)  出栈操作

3.3.2 顺序栈的表示与实现 

  • top指针,指示栈顶元素在顺序栈中的位置。
  • base指针,指示栈底元素在顺序栈中的位置。
  • stacksize,表示栈可使用的最大容量。

(top指针指示栈顶元素之上的下标位置)

  • 空栈:base == top
  • 栈满:top-base == stacksize
  •  stacksize = (  )

(栈满的处理方法:

  1. 报错,返回操作系统。
  2. 分配更大的空间。)

顺序存储方式特点:简单、方便、容易溢出。

  • 上溢:栈满,又要压入元素。
  • 下溢:栈空,还要弹出元素。

(上溢是一种错误,无法运行;而下溢是一种结束条件)

【代码部分】

1  定义

#define MAXSIZE 100
typedef struct{
       SElemType  *base;  //栈底指针
       SElemType  *top;   //栈顶指针
       int stacksize;     //栈可用最大容量
}SqStack;

2 初始化

Status InitStack(SqStack &S){         //构造一个空栈
    S.base = new SElemType[MAXSIZE];
    if (!S.base)exit (OVERFLOW);      //存储分配失败
    S.top = S.base;                   //栈顶指针等于栈底指针
    S.stacksize = MAXSIZE;
    return OK;
}

3 入栈

【算法步骤】

  1. 判断是否栈满,若满则出错(上溢)
  2. 将元素e压入栈顶
  3. 栈顶指针加1
  Status Push (SqStack &S,SElemType e){
         if(S.top - S.base == S.stacksize)  //栈满
              return ERROR;
              *S.top++=e;
              return OK;
}

S.top++=e; \rightarrow *S.top=e;  S.top++;

4 出栈

【算法步骤】

  1. 判断是否栈空,若空则出错(下溢)
  2. 获取栈顶元素e
  3. 栈顶指针减一
Status Pop(SqStack &S,SElemType &e){ 
// 若栈不空,则删除S的栈顶元素,用e返回其职,并返回OK;否则返回ERROR
    if (S.top == S.base)      //等价于if(StackEmpty(S))
          return ERROR;
     e= *--S.top;
     return OK;
}

   e= *--S.top;  =   - - S.top;e = *--S.top; 


【算法补充】

1 顺序栈判断是否为空

Status StackEmpty(SqStack S){        //栈空返回TRUE
       if (S.top == S.base)
           return TRUE;
       else
           return FALSE;
}

2 求顺序栈长度

 int StackLength( SqStack S )
{
      return S.top - S.base;
}

3 清空顺序栈

Status ClearStack( SqStack S ){
         if( S.base )S.top = S.base;
         return OK;
}

4 销毁顺序栈

Status DestroyStack( SqStack &S ){
  if(S.base){
       delete S.base;
       S.stacksize = 0;
       S.base = S.top = NULL;
  }
  return OK;
}

3.3.2 链栈的表示与实现 

  • 链栈是运算受限的单链表。
  • 链表的头指针就是栈顶
  • 不需要头结点
  • 基本不存在栈满的情况
  • 空栈相当于头指针指向空
  • 插入和删除仅在栈顶处执行

1 定义

typedf struct StackNode
{
    ElemType   data;
    struct StackNode  *next;
}StackNode,*LinkStack;

2 初始化

void InitStack(LinkStack &S){    //构造一个空栈,栈顶指针为空
       S=NULL;
       return OK;
}

3 入栈

【算法步骤】

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

4 出栈

【算法步骤】

  1. 判断栈是否为空,若空则返回ERROR。
  2. 将栈顶元素赋值给e。
  3. 临时保存栈顶元素的空间,以备释放。
  4. 修改栈顶指针,指向新的栈顶元素。
  5. 释放原栈顶元素的空间。
Status Pop (LinkStack &S,SElemType &e){
   if (S ==NULL) return ERROR;      //栈空
   e = S-> data;                    //将栈顶元素赋值给e
   p = S;                           //用p临时保存栈顶元素空间,以备释放
   S = S-> next;                    //修改栈顶指针
   delete p;                        //释放预原栈顶元素的空间
   return OK;
}

5 取栈顶元素 

   

【算法补充】

1 判断链栈是否为空 

Status StackEmpty(LinkStack S){
    if (S == NULL)return TRUE;
    eles return FALSE;
}

3.4 栈与递归

递归:若一个函数、过程直接或间接地调用自己,则称这个过程是递归的过程。

  •  例:阶乘函数、2阶Fibonaci数列、二叉树、广义表、迷宫问题、Hanoi塔问题

递归问题——分治法(将一个问题分解称几个相对简单的子问题来求解)

void  p (参数表){

       if  (递归结束条件)可直接求解 ;       //基本项

       else p(较小的参数);                        //归纳项

}

 例如:

long Fact (long n){
   if (n == 0) return 1;      //基本项
   else return n*Fact (n-1);  //归纳项
}

递归的优缺点

  • 优点:结构清晰,程序易读。
  • 缺点:每次调用都要生成工作记录,保存状态信息,入栈;返回时要出栈,恢复状态信息。时间开销大。

3.5 队列的表示和操作的实现

 3.5.1  队列(Queue)

1 定义         只能在表尾(Rear)进行插入运算,在表头(Front)进行删除运算的线性表(头删尾插)。 

2 逻辑结构  一对一关系

3 存储结构  顺序队,链队

4 运算规则  先进先出(FIFO)

【代码部分】

定义:

ADT Queue{
      数据对象
      数据关系
      基本操作:
              InitQueue
              DestroyQueue
              ClearQueue
              QueueLength
              GetHead
              EnQueue
              DeQueue
}ADT Queue 

1 顺序队列

#define MAXQSIZE 100     //最大队列长度
Typedef struct{
   QElemType *base;      //初始化的动态分配存储空间
   int front;            //头指针
   int rear;             //尾指针
}SqQueue;
初始(空队)front = rear = 0
入队base [rear] = x  ;  rear++ ; 
出队x=base[front]  ;  front++ ;
空队标志front == rear
  • rear = MAXQSIZE,溢出
  • front =0,rear = MAXQSIZE 真溢出
  •  front ≠0,rear = MAXQSIZE  假溢出

 解决假上溢——循环队列

  • 实现方法:利用模(mod,  %)取余运算
  • 插入元素:Q.base[Q.rear]=x  ;  Q.base[Q.rear+1)%MAXQSIZE;
  • 删除元素:x = Q.base[s.front]   ;  Q.front = (Q.front+1)% MAXQSIZE

解决队列队满时判断方法——少用一个元素空间

  • 队空:front == rear
  • 队满:(rear+1)%MAXQSIZE == front

1)循环队列——定义

   

2)循环队列——初始化

  

2)循环队列——求队列长度

  

3)循环队列——入队

  

4)循环队列——出队

  

5)循环队列——取队头元素

  

2 链队

 若用户无法估计所用队列的长度,则宜采用链队列。

1)类型定义

#define MAXQSIZE 100      //最大队列长度
typedef struct Qnode {
     QElemType  data;
     stuct Qnode  *next;
}QNode, *QueuePtr;

typedaf struct{
     QueuePtr front;      //队头指针
     QueuePtr  rear;      //队尾指针
}LinkQueue;

2)初始化

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

3)入队

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

4)出队

p=Q.front->next;

e=p->data;

Q.front->next = p->next;

Status DeQueue(LinkQueue &Q ,QElemType &e)
{//
    if(Q.front == Q.rear)return ERROR;      //若队空,返回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;
}

5)取队头元素

e=Q.front -> next ->data;

SElemType GetHead(LinkQueue Q)
{//返回Q的对头元素,不修改头指针
     if(Q.front == Q.rear) return ERROR;   //队列非空
     e=Q.front -> next ->data;
     return OK;
}

6)销毁队列(补充)

Status DestroyQueue (LinkQueue &Q){
    while(Q.front):
         p=Q.front->next ; free(Q.front); Q.front = p;
    }
    return OK;
}

3.6 案例分析与实现

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值