数据结构学习笔记(二)——栈与队列

10 篇文章 0 订阅
9 篇文章 0 订阅

栈与队列

第二弹~栈与队列来也!

一.栈

栈必须按照“后进先出” 规则进行操作

  • 栈是限定仅在表尾进行插入或删除的线性表
  • 允许进行插入或删除的一端称为栈顶(top),它将随栈中元素增减而浮动
  • 另一端称为栈底(bottom),其指针是固定的,不随栈中元素增减而移动

顺序栈

把自栈底到栈顶的元素按照逻辑次序依次存放在一组地址连续的存储单元里的方式称为栈的顺序存储结构
采用这种存储结构的栈称为顺序栈,同时附设指针 top 指示栈顶元素在数组中的位置。
在这里插入图片描述

结构体定义

#define STACK_INIT_SIZE 100	  //顺序栈的初始分配量
#define STACK_INCREMENT 10    //顺序栈的分配增补量  
typedef struct
{   ElemType *elem;         //栈存储空间基地址
   int top;			 //栈顶指针,非空栈指向栈顶元素
    int stacksize;           //当前分配的存储量
} SqStack;

初始化操作

//时间复杂度为O(1)
void InitStack_Sq(SqStack &S)	
{  
   S.elem=new ElemType[STACK_INIT_SIZE];
   if(!S.elem)//该栈已满  
      Error(“Overflow”);
   S.top=-1;
   S.stacksize=STACK_INIT_SIZE;
}

销毁操作

//时间复杂度为O(1)
void DestroyStack_Sq(SqStack S)
{  delete []S.elem;
   S.top=-1;
   S.stacksize=0;
 }

清空操作:S.top = -1;
求栈长度操作:return (S.top+1);

取栈顶元素

//时间复杂度为O(1)
void GetTop_sq(SqStack S,ElemType &e) 
{ 
    //若顺序栈为空,则退出运行;否则用e返回栈顶元素值
    if(S.top==-1)  
        Error(“stack empty!); 
    e=S.elem[S.top];
} 

入栈操作

① 判断当前存储空间是否已满,如果已满,则需要系统增加空间
先将栈顶指针加1,再将新数据元素入栈顶

//在正常情况下(不发生上溢),算法的时间复杂度为O(1);在非正常情况下(发生上溢),算法的时间复杂度为O(n)。
void Push_Sq(SqStack &S, ElemType e)  
{
    //插入元素e,作为新的栈顶元素
    if(S.top==(S.stacksize-1)) 	
       //若当前存储空间已满,则增加空间
       Increment(L); 
    S.elem[++S.top]=e;			
    // 栈顶指针先加1,再将e压入栈顶
}

void Increment(SqStack &S)  
{
    //为顺序栈S扩充STACK_INCREMENT个数据元素的空间
    newstack=new ElemType[S.stacksize+STACK_INCREMENT];
					  // 增加STACK_INCREMENT个存储分配
    if(!newstack) 
        Error("Overflow!");	  // 存储分配失败
    for(i=0;i<=S.top;i++)
        newstack[i]=S.elem[i];	  // 腾挪原空间中的数据到newstack中
    delete []S.elem;			  // 释放元素所占用的原空间S.elem
    S.elem=newstack;			  // 移交空间首地址
    S.stacksize+=STACK_INCREMENT;	  // 扩充后的顺序栈的最大空间
} 

出栈操作

① 判断当前顺序栈是否为空,如果为空,则给出相应信息并退出运行
② 先用取出栈顶数据元素值,再将栈顶指针减1

//算法的时间复杂度为O(1)
void Pop_Sq(SqStack &S,ElemType &e) 
{
     //若顺序栈S为空,给出相应信息并退出运行;否则用e返回栈顶元素值并修改栈顶指针
    if(S.top==-1)
        Error("Stack Empty!");	// 栈空,给出相应信息并退出运行
    e=S.elem[S.top--]; 
}

共享栈

在这里插入图片描述
相同数据类型的两个栈,栈空间需求相反

链栈

在这里插入图片描述
空栈:
在这里插入图片描述

链栈可以不用头结点,栈顶指针作为链表的头指针

结构体形式

typedef struct StackNode 
{ 
    ElemType  data;//结点数据域
    struct StackNode *next;//结点指针域
}StackNode, *LinkStack;
LinkStack S;   

在这里插入图片描述

链栈进栈

//算法的时间复杂度为O(1)
void Push(LinkStack &S,SElemType &e)
{
    //栈顶插入元素e
    p =new StackNode;   //生成新结点
    p->data=e;          //将新结点数据置为e
    p->next=S;          //将新结点插入栈顶
    S=p;                //修改栈顶指针S值为p
}

链栈出栈

//算法的时间复杂度为O(1)
void Pop(LinkStack &S,SElemType &e) 
{
    // 删除栈顶元素,用e返回
    if(S==NULL) 
        return Error(“Stack Empty!);   //栈空
    e=S->data;          //将栈顶元素赋给e
    p=S;              //临时保存栈顶元素空间,以备释放                      
    S=S->next;             //修改栈顶指针
    delete p;
}

常见例题:

  • 回文字符串
  • 汉诺塔问题
  • 八皇后问题和迷宫问题

二.队列

队列必须按照“先进先出” 规则进行操作

  • 队列是限定只允许在表的一端进行插入,而在表另一端进行删除的线性表
  • 允许插入的一端称为队尾,队尾将随着队列中元素的增加而浮动
  • 允许删除的一端称为队头,队头将随着队列中元素的减少而浮动
    在这里插入图片描述

顺序队列

在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放队列头到队列尾的数据元素之外,还需要附设两个指针front和rear分别指示队列头元素和队列尾元素的位置
在这里插入图片描述

由于队列头和尾都是动态的,因此需设置队头和队尾两个指针,且在初始化空队列时,令 front = rear = 0

当入队时,尾指针加1;当出队时,头指针加1

在非空队列中,头指针front指向队头元素,尾指针rear指向队尾元素的下一个位置

因为顺序队列有“假溢出”现象,因此产生了循环队列

循环队列

在这里插入图片描述

设存储循环队列的数组长度为Queue_Size,则:

循环队列长度 QueueLength = (rear - front + Queue_Size ) % Queue_Size

队尾指针修改:rear = (rear + 1) % Queue_Size

队头指针修改:front = (front + 1) % Queue_Size

循环队列空的条件:front == rear

循环队列满的条件:(rear + 1) % Queue_Size == front

例:
在这里插入图片描述

结构体形式

#define QUEUE_MAX_SIZE 100;	  // 循环队列大小
typedef struct {
    ElemType	*elem;	 // 存储空间基地址
    int  	front;	    // 头指针,队列非空指向队头元素
    int  	rear;		   
    // 尾指针,队列非空指向队尾元素下一位置
} SqQueue; 

初始化

//时间复杂度为O(1)
void InitQueue_Sq(SqQueue &Q) 
{
    //构造一个空循环队列Q
    Q.elem=new ElemType[QUEUE_MAX_SIZE];
    if(!Q.elem)  Error(" Overflow!"); // 存储分配失败
    Q.front=Q.rear=0;
} 

销毁操作

//时间复杂度为O(1)
void DestroyQueue_Sq(SqQueue &Q) 
{
    //释放循环队列Q所占用的存储空间
    delete []Q.elem;
    Q.front=Q.rear=0;
}

清空操作

//时间复杂度为O(1)
void ClearQueue_Sq(SqStack &Q)  
{
    //重置循环队列Q为空队列
    Q.front=Q.rear=0;
} 

求队列长操作

//时间复杂度为O(1)
int QueueLength_Sq(SqQueue Q) 
{
    //返回循环队列Q的数据元素个数
    length=(Q.rear-Q.front+QUEUE_MAX_SIZE)%QUEUE_MAX_SIZE;
    return length;
} 

取队列头元素值操作

//时间复杂度为O(1)
void GetHead_Sq(SqQueue Q,ElemType &e) 
{
     //若循环队列Q为空,则给出相应信息并退出运行;否则用返回队头元素值
    if(Q.front==Q.rear)  Error("Queue Empty!"); 
    e=Q.elem[Q.front]; 
}

入队操作

① 判断当前存储空间是否已满,如果已满,则给出相应信息并退出运行
② 先将数据元素插入队尾,再修改队尾指针

//时间复杂度为O(1)
void EnQueue_Sq(SqQueue &Q,ElemType e) 
{
     //若循环队列Q已满,则给出相应信息退出运行;否则将元素e插入队尾,并修改队尾指针
    if(((Q.rear+1)%QUEUE_MAX_SIZE)==Q.front)
        Error("Queue Overflow!")	
    Q.elem[Q.rear]=e; 
    Q.rear=(Q.rear+1)%Queue_Size;
}

出队操作

① 判断循环队列是否为空:如果为空,则给出相应信息并退出运行
② 先取出队头数据元素值,再修改队头指针

//时间复杂度为O(1)
void DeQueue_Sq(SqQueue &Q,ElemType &e) {
// 若循环队列Q为空,则给出相应信息并退出运行;否则用e返回队头元素,并修改队头指针
    if(Q.front==Q.rear)  Error("Queue Empty!");
    e=Q.elem[Q.front];
    Q.front=(Q.front+1)%QUEUE_MAX_SIZE;
}

链队列

用链表表示的队列称为链队列。
通常用单链表表示,并设置队头指针指向其头结点,队尾指针指向其最后一个结点。
在这里插入图片描述
空链队列:

front 和 rear 都指向头结点

在这里插入图片描述

结构体形式

typedef struct QNode  
{ 
    ElemType	data ;		// 数据域
    struct QNode	*next ;		// 指针域
}QNode,*QueuePtr; 

typedef struct  { 
    QueuePtr  front;		// 头指针,指向链队列头结点
    QueuePtr  rear;		// 尾指针,指向链队列最后结点
} LinkQueue;

初始化操作

void InitQueue_L(LinkQueue &Q) 
{
    //构造一个空链队列Q
    Q.front=Q.rear=new QNode;
    Q.front->next=NULL;	
} 

销毁操作

//时间复杂度为O(n)
void DestroyQueue_L(LinkQueue &Q) 
{
    // 释放链队列Q所占用的存储空间
    while(Q.front) 
    {
        Q.rear=Q.front->next;
        delete Q.front;
        Q.front=Q.rear;
    }
} 

清空操作

//时间复杂度为O(n)
void ClearQueue_L(LinkQueue &Q)  
{
    //清空链队列L,释放所有结点空间
    p=Q.front->next;
    while(p)  
    {
        q=p;
        p=p->next;
        delete q;    
     }
    Q.front->next=NULL;  
    Q.rear=Q.front;
}  

求队列长

//时间复杂度为O(n)
int QueueLength_L(LinkQueue Q) {// 返回链队列Q的长度
    p=Q.front;		// 设置指针,初始指向链队列头结点
    length=0;			// 设置计数器length初始为0
    while(p->next)  {
        length++;
        p=p->next;    }
    return length;
}

取队列头元素操作

//时间复杂度为O(1)
void GetHead_L(LinkQueue Q,ElemType &e) 
{
    //若链队列Q为空,则给出相应信息并退出运行;否则用e返回队头结点数据域的值
    if(Q.front->next==NULL)  Error("Queue Empty!");
    e=Q.front->next->data; 
} 

入队操作

链队列的入队列操作只需要处理队尾结点,而不需要考虑其他结点
在这里插入图片描述

void EnQueue_L(LinkQueue &Q,ElemType e)
 {
    //插入一个数据域值为e的结点到链队列Q中,成为新的队尾结点
    QueuePtr p=new QNode;
    p->data=e;  // 生成数据域值为e的新结点
    p->next=NULL;	 
    Q.rear->next=p;		// 将新结点插到队尾
    Q.rear=p;		// 修改队尾指针,令其指向新结点
}

出队操作

链队列的出队操作只需要处理队头结点,而不需要考虑其他结点。链队列的出队操作需要考虑两种情况:
① 当队列长度大于1时,用e返回链队列队头结点数据域的值,并释放其存储空间
在这里插入图片描述
② 当队列长度等于1时,即删除的既是队头结点又是队尾结点,还需要修改队尾指针
在这里插入图片描述

void DeQueue_L(LinkQueue &Q,ElemType &e) 
{
    // 若链队列Q为空,则退出运行;否则用e返回队头结点数据域的值
    if(Q.front->next==NULL)  Error("Queue Empty!");
    QNode* p=Q.front->next; // 指针p指向链队列Q队头结点
    e=p->data;
    Q.front->next=p->next;		// 修改队头指针
    if(Q.rear==p)  
       Q.rear=Q.front;	// 若队列长度等于1,则修改尾指针
    delete p;		// 释放队头结点所占的存储空间
}

下一弹:数组与广义表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小菲601

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值