朝花夕拾 · 数据结构 | 队列与栈篇

一.队列

1.队列的顺序存储

1.队列的定义
队列(Qucue)简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。这和我们日常生活中的排队是一致的,最早排队的也是最早离队的,其操作的特性是先进先出(FirstInFirst Out,FIFO),如图所示。

队头(Front):允许删除的一端,也称队首。
队尾(Rear):允许插入的一端。
空队列:不含任何元素的空表。

顺序队列的两种定义:

1.

typedef int Data_t;

typedef struct node
{
    Data_t data;
    struct node *pnext;
}Node_t;

typedef struct lqueue
{
    Node_t *phead;
    Node_t *ptail;
    int clen;
}LQue_t;

2.

typedef struct{
Data_t data[MaxSize];    //用数组存放队列元素
int front,rear;            //队首指针和队尾指针
}SqQueue;

以第一种定义为例,以下为相关CRUD功能实现

LQue_t * create_link_queue()
{
    LQue_t *pque = malloc(sizeof(LQue_t)); 
    if (NULL == pque)
    {
        printf("malloc error\n");
        return NULL;
    }  
    pque->phead = NULL;
    pque->ptail = NULL;
    pque->clen = 0;

    return pque;
}
int is_empty(LQue_t *plink)
{
    return NULL == plink->phead;
}
int insert_link_queue(LQue_t* pque,int data)
{
    Node_t *p = malloc(sizeof(Node_t));
    p->data = data;
    p->pnext = NULL;
    if(p == NULL)
    {
        printf("malloc error\n");
        return -1;
    }
    if(pque->phead == NULL)
    {
        pque->phead = p;
        pque->ptail = p;
    }
    else
    {
        pque->ptail->pnext = p;
        pque->ptail = p;
    }
    pque->clen++;
      return 0;
}
void show_link_queue(LQue_t* pque)
{
    if(is_empty(pque))
    {
        return;
    }
    Node_t *ptmp = pque->phead;
    while(ptmp)
    {
        printf("%d ",ptmp->data);
        ptmp = ptmp->pnext;
    }
    printf("\n");
}
int pop_link_queue(LQue_t* pque,Data_t *pdata)
{
    if(is_empty(pque))
    {
        return -1;
    }
    Node_t *pnode = pque->phead;
    pque->phead = pnode->pnext;
    *pdata = pnode->data;
    free(pnode);
    pque->clen--;
    if(pque->clen == 0)
    {
        pque->ptail = NULL;
    }
    return 0;
}
int get_link_queue_head(LQue_t *pque,Data_t *pdata)
{
    if(is_empty(pque))
    {
        return -1;
    }
    *pdata = pque->phead->data;;
    return 0;
}
void destory_link_queue(LQue_t *pque)
{
//    if(is_empty(pque))
//   {
//        return ;
//    }
//    while(pque->phead)
//   {
//    Node_t *pfree = pque->phead;
//    pque->phead = pfree->pnext;
//   free(pfree);
//   pque->clen--;
//   }
//   pque->clen = 0;
//   free(pque);
    while (!is_empty_link_queue(pque))
    {
        pop_link_queue(pque, NULL);
    }
    free(pque);
}

2.链式队列

3.循环队列

由于上述顺序队列存在“假溢出”的问题,这里引出循环队列的概念。将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。当队首指针Q.front=MaxSize-1后,再前进一个位置就自动到0,这可以利用除法取模运算(%)来实现。

为了区分是队空还是队满的情况,有三种处理方式:

1)牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的做法,约定以“队首指针在队尾指针的下一位置作为队满的标志”,如图3.7(d2)所示。队满条件: (Q.rear+1)%MaxSize==Q.front。队空条件:Q.front==Q.rear。队列中元素的个数:(Q.rear-Q.front+MaxSize)%MaxSize

2)类型中增设size数据成员,表示元素个数。若删除成功,则size减1,若插入成功,则 size 加 1,队空时 Q.size==0;队满时 Q.size==MaxSize,两种情况都有Q.front==Q.rear.

3)类型中增设tag数据成员,以区分是队满还是队空。删除成功置tag=0,若导致Q.front==Q.rear,则为队空;插入成功置tag=1,若导致Q.front==Q.rear,则为队满。

循环链表定义

typedef struct seq_que
{
    Data_t* base;
    int head;
    int tail;
}SQue_t; 

基本功能:

SQue_t* create_seq_queue()
{
    SQue_t* psq = malloc(sizeof(SQue_t));
    if (NULL == psq)
    {
        printf("malloc error\n");
        return NULL;
    }
    psq->base = malloc(sizeof(Data_t)*SEQ_MAX_LEN);
    if (NULL == psq)
    {
        printf("malloc error\n");
        return NULL;
    }
    psq->head = 0;
    psq->tail = 0;
    return psq;
}
int push_seq_queue(SQue_t *psq,Data_t data)
{
    if(is_full_seq_queue(psq))
    {
        return -1;
    }
    psq->base[psq->tail] = data;
    psq->tail = (psq->tail+1)%SEQ_MAX_LEN;
    return 0;
}
int is_full_seq_queue(SQue_t* psq)
{
    return (psq->tail+1)%SEQ_MAX_LEN == psq->head;
}
int is_empty_seq_queue(SQue_t *psq)
{

    return psq->tail == psq->head;
}
void show_seq_queue(SQue_t*psq)
{
    if(is_empty_seq_queue(psq))
    {
        return ;
    }
    int tmp = psq->head;
    while(tmp<psq->tail)
    {
        printf("%d ",psq->base[tmp]);
        tmp++;
    }
    printf("\n");
}
int pop_seq_queue(SQue_t *psq,Data_t *pdata)
{

    if(is_empty_seq_queue(psq))
    {
        return -1;
    }
    if(pdata = NULL)
    {
        return -1;
    }
    *pdata = psq->base[psq->head];
    psq->head = (psq->head+1)%SEQ_MAX_LEN;
    return 0;
}
void destory(SQue_t *psq)
{
    free(psq->base);
    psq->base = NULL;
    free(psq);
}

4.队列的应用

1.队列在层次遍历中的应用

2.队列在计算机系统中的应用

二.栈

1.链式栈

栈结构:只允许从一端进行插入和删除数据的线性存储结构,成为栈结构。

数据插入和删除的这端成为栈顶,另一端成为栈底;

数据的插入称为入栈/压栈,数据的删除称为出栈/弹栈。

顺序栈:
满增栈、空增栈、满减栈、空减栈
满栈、空栈:根据所在位置是否存有元素确定
栈顶所在位置一直存有元素称为满栈,先移动栈顶再存数据。
栈顶所在位置一直没有元素称为空栈,先存数据再移动栈顶。
增栈、减栈:根据栈的生长方向确定。
入栈数据时,栈顶向内存高地址移动,称为增栈,入栈数据时,栈顶向内存低地址移动,称为减栈。

与内存中栈的区别:

前者是线性表的一种实现形式,是一种逻辑数据结构,由开发人员自己管理(开辟和释放内存空间);后者是由操作系统管理,存放局部变量和形参等数据,二者都是先进后出结构。

链式栈的结构定义:

typedef int Data_t;

typedef struct node
{
    Data_t data;
    struct node *pnext;
}Node_t;

typedef struct stack
{
    Node_t *ptop;
    int clen;
}Stack_t;

基本功能实现:

#include<stdio.h>
#include"stack.h"
#include<stdlib.h>


Stack_t *create_stack()
{
    Stack_t *pstack = malloc(sizeof(Stack_t));
    if(NULL == pstack)
    {
        printf("malloc error\n");
        return NULL;
    }
    pstack->clen = 0;
    pstack->ptop = NULL;
    return pstack;
}
int push_stack(Stack_t *pstack, Data_t data)
{
    Node_t *pnode = malloc(sizeof(Node_t));
    if(NULL == pnode)
    {
        printf("malloc error\n");
        return -1;
    }
    pnode->data = data;
    pnode->pnext = pstack->ptop;
    pstack->ptop = pnode;
    pstack->clen++;
    return 0;
}
int pop_stack(Stack_t *pstack, Data_t *pdata)
{
    if(is_empty_stack(pstack))
    {
        return -1;
    }
    Node_t *pfree = pstack->ptop;
    if(pdata)
    *pdata = pfree->data;
    pstack->ptop = pfree->pnext;
    free(pfree);
    pstack->clen--;
    return 0;
}
int is_empty_stack(Stack_t *pstack)
{
    return pstack->clen == 0;
}
void clear_stack(Stack_t *pstack)
{
    while(!is_empty_stack(pstack))
    {
        pop_stack(pstack,NULL);
    }
}
void show_stack(Stack_t *pstack)
{
    Node_t *ptmp = pstack->ptop;
    while(ptmp)
    {
        printf("%d ",ptmp->data);
        ptmp = ptmp->pnext;
    }
    printf("\n");
}
int get_stack_top(Stack_t *pstack,Data_t *pdata)
{
    if(is_empty_stack(pstack))
    {
        return -1;
    }
    *pdata = pstack->ptop->data;
    return 0;
}
void destroy_stack(Stack_t *pstack)
{
    clear_stack(pstack);
    free(pstack);
}

2.栈的应用

解决回溯问题;
撤销功能、网页撤销功能缓存;
判断回文字符串,判断成对出现的符号有没有丢失;

栈在括号匹配中的应用:

栈在递归中的应用:

栈在表达式中的应用:

中缀表达式转后缀表达式:

注意,先出栈的为右操作数,后出为左操作数,操作符每次出栈时要出两个操作数。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值