03-栈和队列 (数据结构和算法)

四 、栈和队列

栈和队列也是一种线性结构。它们也都用于存储逻辑关系为 "一对一" 的数据,但由于它们比较特殊,因此将其单独作为一章,做重点讲解。

4.1 栈的基本概念

栈是一种基于先进后出(FILO)或者后进先出(LIFO的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。

我们称数据进入到栈的动作为压栈(入栈),数据从栈中出去的动作为弹栈(出栈)

  • 顺序栈:使用数组实现

  • 链式栈:使用链表实现

4.2 顺序栈

在实现顺序栈之前,我们先来看一看对于顺序栈的操作:

顺序栈可以使用一维数组实现,base指针指向栈底(数组的第0个元素),top指针是动态的,每次都指向栈顶元素(最后一个放入栈中的元素),因此,我们将base指针称之为:栈底指针,将top指针称之为栈顶指针

1、顺序栈的描述结构体

#define STACK_INIT_LEN 100
#define STACKINCREMENT 10

typedef struct SqStack{
    ElemType *top; //栈顶指针
    ElemType *base;
    uint stackLen;
}SqStack;

2、顺序栈的初始化

/*
 * @brief 初始化一个顺序栈
 * @param 初始顺序栈的长度
 * @return 返回初始化后的栈的指针
 * */
SqStack *stack_init(uint size)
{
    //创建一个栈
    SqStack *s = (SqStack *)malloc(sizeof(SqStack));

    //为栈分配空间
    s->base = (ElemType *)malloc(size*sizeof(ElemType));
    s->top = s->base;
    s->stackLen = size;
    s->len = 0;
    return s;
}

3、对顺序栈进行扩容

/*
 * @brief 对栈进行扩容
 * @param s 需要扩容的栈指针
 * @return 成功返回TRUE, 失败返回FALSE
 */
int expand(SqStack *s){
    printf("[%s %d] stack expand ...\n", __FUNCTION__ , __LINE__);
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }

    //为栈重新分配空间,栈底指针指向新的分配好的空间
    s->base = (ElemType *)realloc(s->base, (s->stackLen+STACKINCREMENT)*sizeof(ElemType));
    //栈顶指针指向新的栈顶元素
    s->top = s->base + s->stackLen;
    s->stackLen += STACKINCREMENT;

    return TRUE;
}

4、入栈

/*
 * @brief 入栈
 * @param s 栈指针
 * @param data 需要入栈的元素
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int push(SqStack *s, ElemType data){
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }
    //如果栈满了
    if (s->top - s->base >= s->stackLen)
        expand(s);
    *(s->top) = data;
    s->top++;

    return TRUE;
}

5、出栈

/*
 * @brief 出栈
 * @param s 栈指针
 * @param data 存放栈顶元素的指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int pop(SqStack *s, ElemType *data){
    if (NULL == s || NULL == data)
        return FALSE;
    //如果栈为空
    if (s->top == s->base)
    {
        printf("[%s %d] stack is empty ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }
    s->top--;
    *data = *(s->top);

    return TRUE;
}

6、获取栈顶元素

/*
 * @brief 获得栈顶元素
 * @param s 栈指针
 * @param data 存放栈顶元素的指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int get_top_elem(SqStack *s, ElemType *data){
    if (NULL == s || NULL == data)
        return FALSE;
    //如果栈为空
    if (s->top == s->base)
    {
        printf("[%s %d] stack is empty ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }

    *data = *(s->top - 1);
    return TRUE;
}

7、求顺序栈的长度

int get_len(SqStack s){
	if (NULL==s)
		return 0;
	return s->top-s->base;
}

8、清空顺序栈

/*
 * @brief 清除一个栈
 * @param s 栈指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int stack_clear(SqStack *s){
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n");
        return FALSE;
    }
    s->top = s->base;
    return TRUE;
}

9、销毁顺序栈

 /*
 * @brief 销毁栈
 * @param s 栈指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int stack_destroy(SqStack *s){
    if (NULL == s){
        printf("[%s %d] stack pointer is NULL ...\n");
        return FALSE;
    }

    if (s->base != NULL){
        free(s->base);
        s->top = s->base = NULL;
        s->stackLen = 0;
    }
    return TRUE;
}

4.3 链式栈

链式栈 就是使用链表的方式实现的栈,为了实现先进后出(FILO),我们可以采用链表中的“头插法”实现一个链式栈。

但是与之前所学的链表不同的是:链式栈中不需要头结点(数据域为空的结点)。如上图所示,指向链表中的第一个结点的指针就是栈顶指针,指向链表最后一个结点的指针就是栈底指针。

1、链式栈的描述

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

2、链式栈的初始化

/*
 * @brief 初始化一个链式栈
 * @return 返回链式栈的栈顶指针
 * */
LinkStack stack_init()
{
    LinkStack s;
    //因为没有头结点,因此初始化链式栈时因为栈为空,所以栈顶指针赋值为NULL
    s = NULL; 
    return s;
}

3、入栈

入栈:链表的头插法,在链表的第一个结点之前插入新的结点

/*
 * @brief 入栈
 * @param s 栈顶指针的地址
 * @param data 需要入栈的元素
 * @return 成功返回TRUE,失败返回FALSE
 * */
int push(LinkStack *s, ElemType data)
{
    if (NULL == s)
        return FALSE;

    StackNode *p = (StackNode *)malloc(sizeof(StackNode));
    p->data = data;

    //新的结点的next指向链表的第一个结点(原来的栈顶)
    p->next = *s;
    //栈顶指针指向新的结点(p变成了链表上的第一个结点)
    *s = p;
  
    return TRUE;
}

void print_stack(LinkStack s)
{
    if (NULL == s)
        return ;

    StackNode *t = s;
    while (t)
    {
        printf("%d ", t->data);
        t = t->next;
    }
    printf("\n");
}

4、出栈

/*
 * @brief 出栈
 * @param s 栈顶指针的地址
 * @param data 需要出栈的元素
 * @return 成功返回TRUE,失败返回FALSE
 * */
int pop(LinkStack *s, ElemType *data){
    if (NULL == s || NULL == data || NULL == *s)
        return FALSE;
  
    *data = (*s)->data;
    StackNode *t = (*s)->next;
    free(*s);
    *s = t;
    return TRUE;
}

5、获取栈顶元素

/*
 * @brief 获取栈顶元素 
 * @param s 栈顶指针
 * @param data 需要入栈的元素
 * @return 成功返回TRUE,失败返回FALSE
 * */
int get_top(LinkStack s, ElemType *data)
{
    if (NULL == s || NULL == data)
        return FALSE;
    *data = s->data;

    return TRUE;
}

6、销毁链式栈

/*
 * @brief  销毁链式栈
 * @param s 栈顶指针的地址
 * @return 成功返回TRUE,失败返回FALSE
 * */
int stack_destroy(LinkStack *s)
{
    if (NULL == s || NULL == *s)
        return FALSE;  

    StackNode *t = *s;;
    while (t)
    {
        t = (*s)->next; 
        free(*s);
        *s = t;
    }
    //*s = NULL;
    return TRUE;
}

4.3.1 栈的应用

1、数值转换

void conversion()
{
    LinkStack s = stack_init();

    unsigned int num;
    printf("plz input A number to be converted : ");
    scanf("%d", &num);

    unsigned int N;
    printf("plz input decimal (2 8 16):");
    scanf("%d", &N);

    char f;
    while (num)
    {
        if (num % N < 10)
            f = num % N + '0';//将一个10以内的整数转换成字符形式:例如  5 -> '5'
        else 
            f = num % N - 10 + 'A';// 10~15转换为 'A' ~ 'F' 

        push(&s, f);
        num = num / N;
    }

    //出栈
    while (is_empty(s) != TRUE)
    {
        pop(&s, &f);
        printf("%c", f);
    }
    printf("\n");
  
}

2、括号匹配

假设表达式中允许出现两种括号:圆括号()和方括号[],其嵌套的顺序随意,即( [ ( ) ( ) ] )或者 [ ( [ ] [ ] ) ]等为正确的表达式,但是 ( [ ( ] ) 或者 [ ( [ ) ] 或者 ( [ ( ] ) )等为错误的表达式。检查括号是否匹配可以采用如下逻辑:

  • 遍历整个表达式

  • 如果是左括号入栈

  • 如果是右括号则从栈中出栈一个括号,如果与右括号匹配则继续往后遍历,如果不匹配则说明是错误的表达式

  • 如果表示遍历完毕,最后栈为空说明表达式正确否则说明表达式错误

int brackets_match()
{
    char *str = (char *)malloc(100);

    LinkStack s = stack_init();
    printf("plz input expression: ");
    scanf("%s", str);

    int i = 0;
    char f;
    while (str[i] != '\0')
    {
        if (str[i] == '(' || str[i] == '[')
            push(&s, str[i]);
        else if (str[i] == ')')
        {
            pop(&s, &f);
            if (f != '(')
                return FALSE;
        }
        else if (str[i] == ']')
        {
            pop(&s, &f);
            if (f != '[')
                return FALSE;
        }
        i++;
    }

    if (is_empty(s) == TRUE)
        return TRUE;
    else
        return FALSE;
}

3、 表达式求值

需求:输入一个表达式,含加减乘除括号运算,输出表达式的值

例如:求20+(3*400)-100/3的值

逻辑:

  • 使用两个栈,一个 栈存放操作符,一个 栈存放操作数

  • 遍历整个表达式,如果是操作数入栈操作数栈

  • 如果是操作符,则获取操作符栈的栈顶元素,比较两个操作符的优先级,如果比栈顶的操作符优先级低或者相等,则出栈一个操作符,出栈两个操作数,将计算结果入操作数栈

  • 如果比栈顶的操作符优先级高则直接入栈操作符栈

  • 如果遇到的是左括号 ( , 则直接入栈操作符栈

  • 如果是右括号 ),则从操作符栈出栈一个操作符,出栈两个操作数,将计算结果入操作数栈,直到将左括号出栈

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TRUE 0
#define FALSE -1

#define STACK_INIT_LEN 100
#define STACKINCREMENT 10

typedef unsigned int uint;
typedef int ElemType;

typedef struct SqStack{
    ElemType *top; //栈顶指针
    ElemType *base; //栈底指针
    uint stackLen; //分配的栈的长度
}SqStack;

int is_empty(SqStack *s)
{
    if (s->top == s->base)
        return TRUE;
    return FALSE;
}

/*
 * @brief 初始化一个顺序栈
 * @param 初始顺序栈的长度
 * @return 返回初始化后的栈的指针
 * */
SqStack *stack_init(uint size)
{
    //创建一个栈
    SqStack *s = (SqStack *)malloc(sizeof(SqStack));

    //为栈分配空间
    s->base = (ElemType *)malloc(size*sizeof(ElemType));
    s->top = s->base;
    s->stackLen = size;
    return s;
}

/*
 * @brief 对栈进行扩容
 * @param s 需要扩容的栈指针
 * @return 成功返回TRUE, 失败返回FALSE
 */
int expand(SqStack *s)
{
    printf("[%s %d] stack expand ...\n", __FUNCTION__ , __LINE__);
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }

    //为栈重新分配空间,栈底指针指向新的分配好的空间
    s->base = (ElemType *)realloc(s->base, (s->stackLen+STACKINCREMENT)*sizeof(ElemType));
    //栈顶指针指向新的栈顶元素
    s->top = s->base + s->stackLen;
    s->stackLen += STACKINCREMENT;

    return TRUE;
}

/*
 * @brief 入栈
 * @param s 栈指针
 * @param data 需要入栈的元素
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int push(SqStack *s, ElemType data)
{
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }
    //如果栈满了
    if (s->top - s->base >= s->stackLen)
        expand(s);
    *(s->top) = data;
    s->top++;

    return TRUE;
}

/*
 * @brief 出栈
 * @param s 栈指针
 * @param data 存放栈顶元素的指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int pop(SqStack *s, ElemType *data)
{
    if (NULL == s || NULL == data)
        return FALSE;
    //如果栈为空
    if (s->top == s->base)
    {
        printf("[%s %d] stack is empty ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }
    s->top--;
    *data = *(s->top);

    return TRUE;
}

/*
 * @brief 获得栈顶元素
 * @param s 栈指针
 * @param data 存放栈顶元素的指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int get_top_elem(SqStack *s, ElemType *data)
{
    if (NULL == s || NULL == data)
        return FALSE;
    //如果栈为空
    if (s->top == s->base)
    {
        printf("[%s %d] stack is empty ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }

    *data = *(s->top - 1);
    return TRUE;
}

int print_stack(SqStack *s)
{
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n");
        return FALSE;
    }

    ElemType *t = s->top;
    t--;
    while (t != s->base)
    {
        printf("%d ", *t);
        t--;
    }
    printf("%d \n", *t);
    return TRUE;
}

/*
 * @brief 清除一个栈
 * @param s 栈指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int stack_clear(SqStack *s)
{
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n");
        return FALSE;
    }
    s->top = s->base;
    return TRUE;
}

/*
 * @brief 销毁栈
 * @param s 栈指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int stack_destroy(SqStack *s)
{
    if (NULL == s)
    {
        printf("[%s %d] stack pointer is NULL ...\n");
        return FALSE;
    }

    if (s->base != NULL)
    {
        free(s->base);
        s->top = s->base = 0;
        s->stackLen = 0;
    }
    return TRUE;
}

int cal_oper(int num2, int num1, char f)
{
    switch (f) {
        case '+':
            return num2 + num1;
        case '-':
            return num2 - num1;
        case '*':
            return num2 * num1;
        case '/':
            return num2 / num1;
    }
}

int cal_exp(SqStack *oper, SqStack *oprand)
{
    //出栈一个操作符
    char f;
    pop(oper, &f);
    //出栈两个操作数
    int num1, num2;
    pop(oprand, &num1);
    pop(oprand, &num2);
    int num =  cal_oper(num2, num1, f);
    return num;
}

int calculate()
{
    //操作符栈
    SqStack *oper = stack_init(STACK_INIT_LEN);
    //操作数栈
    SqStack *oprand = stack_init(STACK_INIT_LEN);

    char exp[128];
    memset(exp, 0, sizeof(exp));

    printf("plz input expression: ");
    scanf("%s", exp);

    int i;
    char f;
    int num;
    while (exp[i] != '\0') {
        //如果是数字则计算出该数字后直接入栈
        if (exp[i] >= '0' && exp[i] <= '9') {
            int num = 0;
            while (exp[i] >= '0' && exp[i] <= '9') {
                num = num * 10 + (exp[i] - '0');  i++;
            }
            //将操作数入栈
            printf("[%d] push: %d\n", __LINE__, num);   push(oprand, num);   continue;
        }
        //如果是左括号 直接入栈
        if (exp[i] == '('){ printf("[%d] push: %c\n", __LINE__, exp[i]);   push(oper, exp[i]);  i++;  continue; }

        //如果是右括号 从操作符栈出栈一个操作符,从操作数栈出栈两个操作数,计算后将结果入栈操作数栈
        if (exp[i] == ')'){
            while (is_empty(oper) != TRUE){
                get_top_elem(oper, &f);
                if (f != '('){
                    //从操作符栈出栈一个操作符,从操作数栈出栈两个操作数,计算后将结果入栈操作数栈
                    push(oprand, cal_exp(oper, oprand));
                    continue;
                }
                else{

                    pop(oper, &f);
                    break;
                }
            }
            i++; continue;
        }
        //如果是+ -
        if (exp[i]=='+' || exp[i]=='-'){
            //如果操作符栈为空直接入栈
            if (is_empty(oper)==TRUE) {
                printf("[%d] push: %c\n", __LINE__, exp[i]);
                push(oper, exp[i]); i++; continue;
            }
            while (is_empty(oper) != TRUE){//一直出栈操作符栈中的符号直到遇到(或者操作符栈为空
                //  先获取操作符栈顶元素,如果是左括号直接入栈
                get_top_elem(oper, &f);
                if (f=='(') { break;}
                //从操作符栈出栈一个操作符,从操作数栈出栈两个操作数,计算后将结果入栈操作数栈
                push(oprand,cal_exp(oper, oprand));
                continue;
            }
            //最后将遍历到的符号入栈操作符栈
            printf("[%d] push: %c\n", __LINE__, exp[i]);
            push(oper, exp[i]); i++; continue;
        }
        //如果是 * /
        if (exp[i]=='*' || exp[i]=='/'){
            //如果操作符栈为空直接入栈
            if (is_empty(oper)==TRUE) {
                printf("[%d] push: %c\n", __LINE__, exp[i]);
                push(oper, exp[i]); i++; continue;
            }
            while (is_empty(oper) != TRUE) {//一直出栈操作符栈中的符号直到遇到(或者操作符栈为空,或者+ -
                //  先获取操作符栈顶元素,如果是左括号 或者+ - 直接入栈
                get_top_elem(oper, &f);
                if (f == '(' || f == '+' || f == '-') {break;}
                //从操作符栈出栈一个操作符,从操作数栈出栈两个操作数,计算后将结果入栈操作数栈
                push(oprand,cal_exp(oper, oprand));
                continue;
            }
            //最后将遍历到的符号入栈操作符栈
            printf("[%d] push: %c\n", __LINE__, exp[i]);
            push(oper, exp[i]); i++; continue;
        }
    }
    //遍历完整个表达式后将操作符中的元素依次出栈
    while (is_empty(oper) != TRUE)
        push(oprand,cal_exp(oper, oprand));

    pop(oprand,&num);
    return num;
}

int main() {

    int num;
    num = calculate();
    printf("%d\n", num);
    return 0;
}
//10-5*(3+2)+6/2+10*(4/2)

4.4 队列

1、队列的基本概念

队列是一种基先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。

2、队列的应用

  • 举例1:排队系统的实现

  • 举例2:使用循环队列存储网络摄像头的数据帧(图像数据)

3、队列的具体实现

  • 顺序队列

  • 链队列

4.4.1 顺序队列

在实现顺序队列之前,我们先来看一看对于顺序队列的操作:

顺序队列可以使用一维数组实现,在顺序队列中有两个指针,一个指针front指向队列的队首(数组的第0个元素),一个指针rear指向队列的队尾(最后一个放入队列的元素)。

1、顺序队列的描述

#define QUEUE_INIT_LEN 100
#define QUEUEINCREMENT 10
typedef struct SqQueue
{
    ElemType *base; //存储堆上元素的地址空间的首地址
    uint front; //队首指针
    uint rear; //队尾指针
}SqQueue;

2、关于顺序队列操作的思考

  • 如果对顺序队列进行出队操作,队首指针该如何移动?

    • 队首指针往后移动

  • 对顺序队列进行出队操作后,被出队的元素所占用的空间怎处理?

    • 只能暂时空着,等待下次循环使用

  • 如何判断顺序队列是否为满,是否为空呢?

    • 判断队列为满和空的依据都是front == rear,因此会有冲突

  • 如何判断队列是否溢出呢?

    • 当front为0,rear等于队列最大长度时为真溢出

  • 当front不为0,rear等于队列最大长度时为假溢出

解决方案:使用循环队列。

3、循环队列的概念

队列上的各个元素逻辑上形成一个圆环状。

如何判断队列是否为满呢?

  • 将队列上的一个位置作为空闲位置

  • 假设队列的长度为M,当 (rear + 1)% M == font时,认为队列为满

4、循环顺序队列的初始化

/*
 * @brief 初始化一个顺序队列
 * @param len 顺序队列的初始化长度(可以存储的元素的最大个数)
 * @return 返回初始化的顺序队列
 * */
SqQueue SqQueue_init(uint len)
{
    SqQueue s;
    s.base = (ElemType*)malloc(len*sizeof(ElemType));
    s.front = 0;
    s.rear = 0;
  
    return s;
}

5、获取顺序队列的长度(实际存储的元素的个数)

/*
 * @brief 获取顺序队列的长度
 * @param s 顺序队列
 * @return 顺序队列的长度
 * */
int SqQueue_length(SqQueue s)
{
    return s.rear - s.front;
}

6、判断顺序队列是否为空

/*
 * @brief 判断顺序队列是否为空
 * @param 顺序队列
 * @return 为空返回TRUE,不为空返回FALSE
 * */
int is_empty(SqQueue s)
{
    if (s.front == s.rear)
        return TRUE;
    else
        return FALSE;
}

7、判断顺序队列是否为满

/*
 * @brief 判断顺序队列是否为满
 * @param 顺序队列
 * @return 为满返回TRUE,不为满返回FALSE
 * */
int is_full(SqQueue s)
{
    if ((s.rear+1)%QUEUE_INIT_LEN == s.front)
        return TRUE;
    else
        return FALSE;
}

8、入队

/*
 * @brief 循环顺序队列入队
 * @param data 需要入队得元素
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int EnQueue(SqQueue *s, ElemType data)
{
    if (NULL == s)
        return FALSE;
    if (is_full(*s) == TRUE)
    {
        printf("[%s %d] queue is full ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }

    s->base[s->rear] = data;
    s->rear= (s->rear + 1) % QUEUE_INIT_LEN; //尾指针往后移动
    return TRUE;
}

9、顺序队列打印

/*
 * @brief 输出顺序队列中的元素
 * @param s 需要输出顺序队列
 * @return 成功返回TRUE,失败返回FALSE
 * */
int print_SqQueue(SqQueue s)
{
    if (is_empty(s) == TRUE)
    {
        printf("[%s %d] SqQueue is empty\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }
    int i = s.front;
    while (i != s.rear)
    {
        printf("%d ", s.base[i]);
        //i 指向下一个位置
        i = (i+1)%QUEUE_INIT_LEN;
    }
    printf("\n");
    return TRUE;
}

10、出队

/*
 * @brief 循环顺序队列出队
 * @param data 存储出队元素的指针
 * @return 成功返回TRUE, 失败返回FALSE
 * */
int DeQueue(SqQueue *s, ElemType *data)
{
    if (NULL == s)
        return FALSE;
    if (is_empty(*s) == TRUE)
    {
        printf("[%s %d] queue is empty ...\n", __FUNCTION__ , __LINE__);
        return FALSE;
    }

    *data = s->base[s->front];
    s->front = (s->front + 1) % QUEUE_INIT_LEN; //队首指针往后移动

    return TRUE;
}

4.4.2 链式队列

链式队列 可以理解为对单向链表的操作,入队就是单向链表的尾插法,出队则需要销毁第一个数据结点(类似删除链表上的第一个数据结点)。

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

#define TRUE   0
#define FALSE -1

typedef int ElemType;
typedef unsigned int uint;

/*
 * 链式队列可以理解成一个通过尾插法创建的链表(可以带头结点也可以不带头结点)
 * */
//定义链表上的一个结点
typedef struct LNode
{
    ElemType data; //数据域
    struct LNode *next; //指向当前结点的下一个结点的指针(这个指针保存了下一个结点的存储位置)
}LNode;

//定义一个结构体描述一个链式队列
typedef struct LinkQueue
{
    LNode *front; //队首指针
    LNode *rear; //队尾指针
}LinkQueue;

/*
 * @brief 初始化一个链式队列
 * @return 代表链式队列的结构体
 * */
LinkQueue queue_init()
{
    //创建一个头结点
    LinkQueue L;
    //当队列为空时,队首指针和队尾指针都为NULL
    L.front = L.rear = NULL;

    return L;
}

/*
 * @brief 打印链式队列中的元素
 * @param L 链式队列结构体
 * @return
 * */
void print_queue(LinkQueue L)
{
    //使用临时指针指向链表上的第一个数据结点(就是头节点的下一个结点)
    LNode *tmp;
    tmp = L.front;

    while (tmp != NULL)
    {
        printf("%d ", tmp->data);
        //让tmp指针指向下一个结点
        tmp = tmp->next;
    }
    printf("\n");
}

/*
 * @brief 入队
 * @param L 链式队列的指针
 * @param data 需要插入的元素
 * @return 成功返回TRUE,失败返回FALSE
 * */
int EnQueue(LinkQueue *L, ElemType data)
{
    if (NULL == L)
        return FALSE;

    //先创建一个结点
    LNode *p = (LNode *)malloc(sizeof(LNode));
    p->data = data;
    p->next = NULL;

    //先判断队列是否为空队列
    if (L->front == NULL)
    {
        L->front = p;
        L->rear = p;

        return TRUE;
    }
    //如果队列不为空,那么将rear指向新的结点
    L->rear->next = p;
    L->rear = p;

    return TRUE;
}

int DeQueue(LinkQueue *L, ElemType *data)
{
    if (NULL == L || NULL == data)
        return FALSE;

    if (NULL == L->front)
        return FALSE;

    //首先保存链式队列上的第一个结点的值
    *data = L->front->data;
    //使用临时指针执行队首
    LNode *t = L->front;
    //队首指针指向后一个结点(出队后原来队列上的第二个结点变成了新的队首结点)
    L->front = L->front->next;
    //删除原来的队首结点
    free(t);

    return TRUE;
}

int main() {
    LinkQueue L;

    L = queue_init();

    int i;
    for (i = 0; i < 10; i++)
        EnQueue(&L, i);
    print_queue(L);

    ElemType data;
    while (DeQueue(&L, &data) != FALSE)
        printf("%d\n", data);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/March_A/article/details/131691771