数据结构(C语言版)学习笔记(第三章:栈和队列)

本文详细介绍了栈和队列这两种数据结构,包括它们的基本概念、顺序存储和链式存储的实现。栈遵循后进先出的原则,提供入栈、出栈、读取栈顶元素的操作;队列则遵循先进先出的原则,支持入队和出队操作。文中还讨论了顺序栈和链栈、顺序循环队列和链式队列的具体实现细节。

栈(Stack)

栈的基本概念

  • 栈:只允许在一端进行插入或删除的线性表
  • 后进先出(Last in First out,LIFO
  • 栈的结构:
    • 栈顶(Top):允许进行插入和删除的一端
    • 栈底(Bottom):固定的,不允许进行插入和删除的一端
  • 因为栈本质上也是线性表,所以有顺序储存和链式储存两种储存方式

顺序栈的定义与基本操作

顺序栈的定义

typedef int ElemType;
#define MaxSize 10      //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize]; //静态数组存放栈中元素
    int top;    //栈顶指针,指向栈顶元素
}SeqStack;

初始化顺序栈以及栈的判空

//初始化栈
void InitStack(SeqStack *S){
    S->top = -1;
}

//判空
bool StackEmpty(SeqStack *S){
    if (S->top == -1) {
        return true;    //栈空
    }
    return false;   //不空
}

入栈操作

//入栈操作
bool Push(SeqStack *S, ElemType e){
    if (S->top == MaxSize-1) {
        return false;           //栈满报错
    }
    S->data[++S->top] = e;      //先将指向栈顶的指针+1,再入栈
    return true;
}

出栈操作

//出栈操作,返回元素e
bool Pop(SeqStack *S,ElemType *e){
    if (S->top == -1) {
        return false;	//栈空,报错
    }
    
    *e = S->data[S->top--];     //先出栈,再将栈顶指针-1
    return true;
}

读取栈顶元素

//读取栈顶元素
bool GetTop(SeqStack *S,ElemType *e){
    if (S->top == -1) {
        return false;
    }
    
    *e = S->data[S->top];     //获取栈顶元素
    return true;
}
  • 读取栈顶元素与出栈操作惟一的区别就在于:出栈需要将指针-1,而读取栈顶元素不需要

链栈的定义与基本操作

  • 基本概念:栈的链式存储,采用单链表实现,并规定所有操作都是在单链表的表头进行

链栈的定义

typedef int ElemType;

typedef struct LinkNode{
    ElemType data;          //数据域
    struct LinkNode *next;  //指针域
}*LStack;   //栈类型定义

初始化链栈

//初始化链栈(带头结点)
LStack InitStack(void){
    LStack L = (LNode *)malloc(sizeof(LNode *));
    L->next = NULL;     //头结点的后继开始空
    
    return L;
}

int main(int argc, const char * argv[]) {
    LStack L = NULL;    //初始化链栈(不带头结点)
   
    return 0;
}

  • 一般推荐使用不带头结点的方式初始化

入栈操作

//入栈操作(带头结点)
bool Push(LStack L,ElemType e){
    LNode *p = (LNode *)malloc(sizeof(LNode));    //创建新结点
    if (p == NULL) {
        return false;   //申请新结点失败
    }
    p->data = e;    //将数据赋值给新节点的数据域中
    p->next = L->next;    //新结点指向栈顶结点
    L->next = p;
    return true;
}

出栈操作

//出栈操作(带头结点)
bool Pop(LStack L,ElemType *e){
    if (StackEmpty(L)) {
        return false;       //如果链栈为空,则报错
    }
    LNode *p = L->next;     //p代表即将出栈的结点
    *e = p->data;           //获取到出栈结点的元素值
    L->next = p->next;
    free(p);                //将p删除
    
    return true;
}

队列(Queue)

队列的基本概念

  • 队列:只允许在一端进行插入另一端进行删除线性表
  • 先进先出(First in First out,FIFO
  • 队列的结构:
    • 对头(Front):允许进行删除的一端
    • 队尾(Rear):允许进行插入的一端
  • 因为队列本质上也是线性表,所以有顺序储存和链式储存两种储存方式。

顺序循环队列的定义与基本操作(队列的顺序存储)

顺序循环队列的定义

typedef int ElemType;
#define MaxSize 10  //队列中元素的最大个数

typedef struct{
    ElemType data[MaxSize]; //用静态数组存放队列元素
    int front,rear;     //队头指针与队尾指针
}SeqQueue;

初始化顺序循环队列以及队列的判空

//初始化队列
SeqQueue InitQueue(void){
    SeqQueue Q;
    Q.front = 0;
    Q.rear = 0;
    
    return Q;
}

//判空
bool QueueEmpty(SeqQueue *Q){
    if (Q->front == Q->rear ) { //队空条件
        return true;
    }else{
        return false;
    }
}

入队操作

//入队操作
bool EnQueue(SeqQueue *Q,ElemType e){
    //判断是否已满
    if ((Q->rear+1)%MaxSize == Q->front) {
        return false;   //队满报错
    }
    
    Q->data[Q->rear] = e;     //新元素入队
    Q->rear = (Q->rear+1)%MaxSize;  //队尾指向下一个空间
    
    return true;
}
  • 这里要注意的是:当队尾元素到data[MaxSize]时,可能因为对头元素出队导致空间其实并没有被填满,这时就需要对队尾进行取余%操作使其跳到第一个位置,这样的话在逻辑上看就是一个循环的结构(这也就是为什么此结构要叫做循环顺序队列
  • 因为我们是通过比较队头和队尾是否在同一个位置上来判断队列是否为空的,所以我们可以通过舍弃数组上的一个储存单元的方式辅助判断,当对位指向最后一个单元时就认为此队列已经被填满了。

出队操作

//出队操作
bool DeQueue(SeqQueue *Q,ElemType *e){
    //判断是否为空
    if (QueueEmpty(Q)) {
        return false;
    }
    
    *e = Q->data[Q->front]; //储存元素
    Q->front = (Q->front+1)%MaxSize;    //对头指向下一个储存单元
    
    return true;
}

链式队列的定义与基本操作

链式队列的定义

typedef int ElemType;

typedef struct LinkNode{    //链式队列结点
    ElemType data;
    struct LinkNode *next;
}LinkNode;

typedef struct LinkQueue{   //链式队列
    LinkNode *front,*rear;  //队列的头和尾指针
}LinkQueue;

初始化链式队列以及队列的判空

//初始化链式队列(带头结点)
void InitLinkQueue(LinkQueue *Q){
    LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode)); //创建一个新结点作为队列的头结点
    p->next = NULL;     //初始化头结点
    
    Q->front = p;
    Q->rear = p;    //将队列的头和尾指针指向头结点

}

//判空
bool QueueEmpty(LinkQueue Q){
    if (Q.front == Q.rear) {
        return true;
    }
    return false;
}

入队操作

//入队操作
bool EnQueue(LinkQueue *Q,ElemType e){
    LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode)); //创建新结点
    if (p == NULL) {
        return false;   //未能成功创建结点
    }
    p->data = e;    //将数据储存在新结点中
    p->next = NULL;
    Q->rear->next = p;  //将新结点插入到尾结点后
    Q->rear = p;    //让新插入的结点作为尾结点
    return true;
}

出队操作

//出队操作
bool DeQueue(LinkQueue *Q,ElemType *e){
    //判断是否为空
    if (QueueEmpty(*Q)) {
        return false;
    }
    
    LinkNode *p = Q->front->next;   //获取到要出队的结点
    *e = p->data;       //获取结点数据
    Q->front->next = p->next;       //令头结点的后继=出队结点的后继
    free(p);    //释放出队接点
    
    if (Q->front->next == NULL) {   //如果头结点后为空,将尾结点设为头结点
        Q->rear = Q->front;
    }
    
    return true;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值