C语言数据结构(二)—— 受限线性表 【栈(Stack)、队列(Queue)】

在数据结构逻辑层次上细分,线性表可分为一般线性表和受限线性表。一般线性表也就是我们通常所说的“线性表”,可以自由的删除或添加结点。受限线性表主要包括栈和队列,受限表示对结点的操作受限制。

一般线性表详解,请参考文章:C语言数据结构(一)—— 数据结构理论、线性表【动态数组、链表(企业版单向链表)】

1栈(Stack)

1.1栈的基本概念

  • 概念:

首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。定义中说是在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底。

  • 特性

它的特殊之处在于限制了这个线性表的插入和删除的位置,它始终只在栈顶进行。这也就使得:栈底是固定的,最先进栈的只能在栈底。符合先进后出的数据结构。

  • 操作

  • 栈的插入操作,叫做进栈,也成压栈。类似子弹入弹夹(如下图所示)

  • 栈的删除操作,叫做出栈,也有的叫做弾栈退栈。如同弹夹中的子弹出夹(如下图所示)

遍历:不重复不遗漏查看每个元素,并且执行过后不会更改元素
遍历算法属于非质变算法
栈能否遍历?不能

1.2栈的顺序存储

  • 基本概念

栈的顺序存储结构简称顺序栈,它是运算受限制的顺序表。顺序栈的存储结构是:利用一组地址连续的的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top只是栈顶元素在顺序表中的位置。

  • 设计与实现

因为栈是一种特殊的线性表,所以栈的顺序存储可以通过顺序线性表来实现。数组首地址端做栈底,尾地址端做栈顶,方便数据插入和删除。

对外接口设计:
初始化
入栈
出栈
返回栈顶
返回元素个数
判断是否为空
销毁栈
#define _CRT_SECURE_NO_WARNINGS
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define MAX 1024

struct SStack {
    //栈中的数组
    void * data[MAX];
    //栈的大小
    int m_Size;
};

typedef void * SeqStack;

//初始化栈
SeqStack init_SeqStack() {
    struct SStack * myStack = malloc(sizeof(struct SStack));
    if (myStack == NULL) {
        return NULL;
    }
    memset(myStack->data, 0, sizeof(void *) * MAX);
    myStack->m_Size = 0;
    return myStack;
}

//入栈
void push_SeqStack(SeqStack stack, void * data) {
    //本质  数组的尾插
    if (stack == NULL) {
        return;
    }
    if (data == NULL) {
        return;
    }
    //还原栈结构体
    struct SStack * myStack = stack;
    //判断栈是否满
    if (myStack->m_Size == MAX) {
        return;
    }
    //数组进行尾插
    myStack->data[myStack->m_Size] = data;
    //更新栈大小
    myStack->m_Size++;
}

//出栈
void pop_SeqStack(SeqStack stack) {
    //本质 数组的尾删除
    if (stack == NULL) {
        return;
    }
    //还原栈结构体
    struct SStack * myStack = stack;
    if (myStack->m_Size == 0) {
        return;
    }

    myStack->data[myStack->m_Size - 1] = NULL;
    myStack->m_Size--;
}

//返回栈顶
void * top_SeqStack(SeqStack stack) {
    //本质 返回数组的最后一个元素
    if (stack == NULL) {
        return NULL;
    }
    //还原栈结构体
    struct SStack * myStack = stack;
    if (myStack->m_Size == 0) {
        return NULL;
    }
    return myStack->data[myStack->m_Size - 1];
}

//返回栈大小
int size_SeqStack(SeqStack stack) {
    if (stack == NULL) {
        return -1;
    }
    //还原栈结构体
    struct SStack * myStack = stack;
    return myStack->m_Size;
}

//判断栈是否为空
int isEmpty_SeqStack(SeqStack stack) {
    if (stack == NULL) {
        return -1; //传入空指针 返回真 栈也是空
    }
    //还原栈结构体
    struct SStack * myStack = stack;
    if (myStack->m_Size == 0) {
        return 1; //1 代表真 栈确实为空
    }
    return 0;// 0假 不为空
}

//销毁
void destroy_SeqStack(SeqStack stack) {
    if (stack == NULL) {
        return;
    }
    free(stack);
    stack = NULL;
}


struct Person {
    char name[64];
    int age;
};

void test() {

    //创建栈
    SeqStack myStack = init_SeqStack();

    //创建数据
    struct Person p1 = { "赵云", 18 };
    struct Person p2 = { "张飞", 19 };
    struct Person p3 = { "关羽", 20 };
    struct Person p4 = { "刘备", 19 };
    struct Person p5 = { "诸葛亮", 12 };
    struct Person p6 = { "黄忠", 17 };

    //入栈
    push_SeqStack(myStack, &p1);
    push_SeqStack(myStack, &p2);
    push_SeqStack(myStack, &p3);
    push_SeqStack(myStack, &p4);
    push_SeqStack(myStack, &p5);
    push_SeqStack(myStack, &p6);

    printf("栈的大小为:%d\n", size_SeqStack(myStack));

    //栈不为空 开始查看栈顶 并出栈
    while (isEmpty_SeqStack(myStack) == 0) {
        struct Person * pTop = top_SeqStack(myStack);
        printf("栈顶元素-姓名:%s  年龄:%d \n", pTop->name, pTop->age);

        //出栈
        pop_SeqStack(myStack);
    }

    printf("栈的大小为:%d\n", size_SeqStack(myStack));

}

//程序入口
int main() {

    test();

    system("pause"); // 按任意键暂停  阻塞功能

    return EXIT_SUCCESS; //返回 正常退出值  0

}

1.3栈的链式存储

  • 基本概念

栈的链式存储结构简称链栈。

思考如下问题
栈只是栈顶来做插入和删除操作,栈顶放在链表的头部还是尾部呢?
由于单链表有头指针,而栈顶指针也是必须的,那干嘛不让他俩合二为一呢,所以比较好的办法就是把栈顶放在单链表的头部。另外都已经有了栈顶在头部了,单链表中比较常用的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的。
  • 设计与实现

链栈是一种特殊的线性表,链栈可以通过链式线性表来实现头节点端做栈顶比较方便

对外接口设计:
初始化
入栈
出栈
返回栈顶
返回元素个数
判断是否为空
销毁栈
#define _CRT_SECURE_NO_WARNINGS
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

//节点结构体
struct LinkNode{
    //只维护指针域
    struct LinkNode * next;
};

//链表结构体
struct LStack {
    struct LinkNode pHeader;//头节点
    int m_Size;//栈大小
};

typedef void * LinkStack;

//初始化
LinkStack init_LinkStack() {
    struct LStack * mystack = malloc(sizeof(struct LStack));
    if (mystack == NULL) {
        return NULL;
    }

    mystack->pHeader.next = NULL;
    mystack->m_Size = 0;

    return mystack;
}

//入栈
void push_LinkStack(LinkStack stack, void * data) {
    //本质 头插
    if (stack == NULL) {
        return;
    }
    if (data == NULL) {
        return;
    }
    struct LStack * mystack = stack;
    //取出用户的前4个字节
    struct LinkNode * myNode = data;
    //建立节点之间的关系
    myNode->next = mystack->pHeader.next;
    mystack->pHeader.next = myNode;
    //更新栈的大小
    mystack->m_Size++;
}

//出栈
void pop_LinkStack(LinkStack stack) {
    //本质 头删
    if (stack == NULL) {
        return;
    }
    struct LStack * mystack = stack;
    if (mystack->m_Size == 0) {
        return;
    }
    //记录指向第一个节点的指针
    struct LinkNode * pFirst = mystack->pHeader.next;
    //更新节点指向
    mystack->pHeader.next = pFirst->next;
    //更新栈大小
    mystack->m_Size--;
}

//返回栈顶
void * top_LinkStack(LinkStack stack) {
    if (stack == NULL) {
        return NULL;
    }
    struct LStack * mystack = stack;
    if (mystack->m_Size == 0) {
        return NULL;
    }
    return mystack->pHeader.next;
}

//返回元素个数
int size_LinkStack(LinkStack stack) {
    if (stack == NULL) {
        return -1;
    }
    struct LStack * mystack = stack;
    return mystack->m_Size;
}

//判断是否为空
int isEmpty_LinkStack(LinkStack stack) {
    if (stack == NULL) {
        return -1;
    }
    struct LStack * mystack = stack;
    if (mystack->m_Size == 0) {
        return 1; //为空 返回真
    }
    return 0;
}

//销毁栈
void destroy_LinkStack(LinkStack stack) {
    if (stack == NULL) {
        return;
    }
    free(stack);
    stack = NULL; 
}


struct Person {
    struct LinkNode node;
    char name[64];
    int age;
};

void test() {
    //创建栈
    LinkStack myStack = init_LinkStack();

    //创建数据
    struct Person p1 = { NULL,"赵云", 18 };
    struct Person p2 = { NULL,"张飞", 19 };
    struct Person p3 = { NULL,"关羽", 20 };
    struct Person p4 = { NULL,"刘备", 19 };
    struct Person p5 = { NULL,"诸葛亮", 12 };
    struct Person p6 = { NULL,"黄忠", 17 };

    //入栈
    push_LinkStack(myStack, &p1);
    push_LinkStack(myStack, &p2);
    push_LinkStack(myStack, &p3);
    push_LinkStack(myStack, &p4);
    push_LinkStack(myStack, &p5);
    push_LinkStack(myStack, &p6);

    printf("栈的大小为:%d\n", size_LinkStack(myStack));

    //栈不为空 开始查看栈顶 并出栈
    while (isEmpty_LinkStack(myStack) == 0) {
        struct Person * pTop = top_LinkStack(myStack);
        printf("栈顶元素-姓名:%s  年龄:%d \n", pTop->name, pTop->age);

        //出栈
        pop_LinkStack(myStack);
    }

    printf("栈的大小为:%d\n", size_LinkStack(myStack));
}

//程序入口
int main() {

    test();

    system("pause"); // 按任意键暂停  阻塞功能

    return EXIT_SUCCESS; //返回 正常退出值  0

}

1.4 栈的应用(案例)

1.4.1 就近匹配

几乎所有的编译器都具有检测括号是否匹配的能力,那么如何实现编译器中的符号成对检测?如下字符串:

5+5*(6)+9/3*1)-(1+3(

算法思路
从第一个字符开始扫描
当遇见普通字符时忽略,
当遇见左括号时压入栈中
当遇见右括号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空

总结

  • 当需要检测成对出现但又互不相邻的事物时可以使用栈“后进先出”的特性;

  • 栈非常适合于需要“就近匹配”的场合;

1.4.2 中缀表达式和后缀表达式
  • 后缀表达式(由波兰科学家在20世纪50年代提出)

  • 将运算符放在数字后面 ===》 符合计算机运算

  • 我们习惯的数学表达式叫做中缀表达式===》符合人类思考习惯

  • 实例

  • 5 +4 => 5 4 +

  • 1 +2 * 3 => 1 2 3 * +

  • 8 +(3 – 1 ) * 5 => 8 3 1 – 5 * +

  • 中缀转后缀算法:

遍历中缀表达式中的数字和符号:

  • 对于数字:直接输出

  • 对于符号:

  • 左括号:进栈

  • 运算符号:与栈顶符号进行优先级比较

  • 若栈顶符号优先级低:此符号进栈

(默认栈顶若是左括号,左括号优先级最低)

  • 若栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈

  • 右括号:将栈顶符号弹出并输出,直到匹配左括号,将左括号和右括号同时舍弃

遍历结束:将栈中的所有符号弹出并输出

  • 动手练习

将我们喜欢的读的中缀表达式转换成计算机喜欢的后缀表达式

中缀表达式: 8 + ( 3 – 1 ) * 5

后缀表达式: 8 3 1 – 5 * +

1.4.3 基于后缀表达式计算
  • 思考

计算机是如何基于后缀表达式计算的?

例如:8 3 1 –5 * +

  • 计算规则

遍历后缀表达式中的数字和符号

  • 对于数字:进栈

  • 对于符号:

  • 从栈中弹出右操作数

  • 从栈中弹出左操作数

  • 根据符号进行运算

  • 将运算结果压入栈中

遍历结束:栈中的唯一数字为计算结果

2队列(Queue)

2.1队列基本概念

队列是一种特殊的受限制的线性表。

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表

队列是一种先进先出的t(First In First Out)的线性表,简称FIFO。允许插入的一端为队尾,允许删除的一端为队头。队列不允许在中间部位进行操作!假设队列是q=(a1,a2,……,an),那么a1就是队头元素,而an是队尾元素。这样我们就可以删除时,总是从a1开始,而插入时,总是在队列最后。这也比较符合我们通常生活中的习惯,排在第一个的优先出列,最后来的当然排在队伍最后。如下图:

2.3队列的顺序存储

  • 基本概念

队列也是一种特殊的线性表;可以用线性表顺序存储来模拟队列

对外接口设计:
初始化队列 init
入队 push
出队 pop
返回队头 front
返回队尾 back
返回队列大小 size
判断是否为空 isEmpty
销毁队列 destroy

头文件 dynamicArray.h:

#pragma  once 
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//动态数据结构体
struct dynamicArray
{
    void ** pAddr; // 维护开辟到堆区真实数组的指针

    int m_Capacity; //数组容量

    int m_Size; //数组大小
};

//初始化数组 参数代表 初始化的容量
struct dynamicArray * init_dynamicArray(int capacity);

//插入元素
void insert_dynamicArray(struct dynamicArray * arr, int pos, void * data);

//遍历数组
void foreach_dynamicArray(struct dynamicArray * arr, void(*myPrint)(void *));

//删除数组
void removeByPos_dynamicArray(struct dynamicArray * arr, int pos);

//按照值 来删除数组中数据
void removeByValue_dynamicArray(struct dynamicArray * arr, void * data, int(*myCompare)(void *, void *));

//销毁数组
void destroy_dynamicArray(struct dynamicArray * arr);

头文件dynamicArray.h :

#pragma  once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "dynamicArray.h"


#define  MAX 1024

typedef void * seqQueue;

//初始化队列
seqQueue init_SeqQueue();
//入队
void push_SeqQueue( seqQueue queue , void * data);
//出队
void pop_SeqQueue(seqQueue queue);
//返回队头元素
void * front_SeqQueue(seqQueue queue);

//返回队尾元素
void * back_SeqQueue(seqQueue queue);

//队列大小
int size_SeqQueue(seqQueue queue);

//判断是否为空
int isEmpty_SeqQueue(seqQueue queue);

//销毁队列
void destroy_SeqQueue(seqQueue queue);

源文件 dynamicArray.c :

#include "dynamicArray.h"


//初始化数组 参数代表 初始化的容量
struct dynamicArray * init_dynamicArray(int capacity)
{
    struct dynamicArray * array = malloc(sizeof(struct dynamicArray));

    if (array == NULL)
    {
        return NULL;
    }

    //给数组属性初始化
    array->m_Capacity = capacity;
    array->m_Size = 0;
    array->pAddr = malloc(sizeof(void *)* capacity);

    if (array->pAddr == NULL)
    {
        return NULL;
    }

    return array;
}


//插入元素
void insert_dynamicArray(struct dynamicArray * arr, int pos, void * data)
{
    if (arr == NULL)
    {
        return;
    }
    if (data == NULL)
    {
        return;
    }

    if (pos < 0 || pos > arr->m_Size)
    {
        //无效的位置  进行尾插
        pos = arr->m_Size;
    }

    //判断是否有空间进行插入,如果没有空间了,那么动态扩展
    if (arr->m_Size >= arr->m_Capacity)
    {
        //1、计算申请空间大小
        int newCapacity = arr->m_Capacity * 2;

        //2、创建新空间
        void ** newSpace = malloc(sizeof (void *)* newCapacity);

        //3、 将原有数据拷贝到新空间下
        memcpy(newSpace, arr->pAddr, sizeof(void*)* arr->m_Capacity);

        //4、 释放原有空间
        free(arr->pAddr);

        //5、 更新指针的指向
        arr->pAddr = newSpace;

        //6、更新新数组容量
        arr->m_Capacity = newCapacity;
    }


    //插入数据

    for (int i = arr->m_Size - 1; i >= pos; i--)
    {
        //数据后移
        arr->pAddr[i + 1] = arr->pAddr[i];
    }
    //将新数据放入到指定位置中
    arr->pAddr[pos] = data;


    //更新数组大小
    arr->m_Size++;
}

//遍历数组
void foreach_dynamicArray(struct dynamicArray * arr, void(*myPrint)(void *))
{
    if (arr == NULL)
    {
        return;
    }

    if (myPrint == NULL)
    {
        return;
    }

    for (int i = 0; i < arr->m_Size; i++)
    {
        myPrint(arr->pAddr[i]);
    }
}


//删除数组
void removeByPos_dynamicArray(struct dynamicArray * arr, int pos)
{
    if (arr == NULL)
    {
        return;
    }

    //无效位置 就直接return
    if (pos < 0 || pos >arr->m_Size - 1)
    {
        return;
    }

    //移动数据
    for (int i = pos; i < arr->m_Size - 1; i++)
    {
        arr->pAddr[i] = arr->pAddr[i + 1];
    }

    //更新大小
    arr->m_Size--;

}

//按照值 来删除数组中数据
void removeByValue_dynamicArray(struct dynamicArray * arr, void * data, int(*myCompare)(void *, void *))
{
    if (arr == NULL)
    {
        return;
    }
    if (data == NULL)
    {
        return;
    }

    for (int i = 0; i < arr->m_Size; i++)
    {
        if (myCompare(arr->pAddr[i], data))
        {
            //如果对比成功了,那么要删除i下标的元素
            removeByPos_dynamicArray(arr, i);
            break;
        }
    }


}

//销毁数组
void destroy_dynamicArray(struct dynamicArray * arr)
{

    if (arr == NULL)
    {
        return;
    }

    if (arr->pAddr != NULL)
    {
        free(arr->pAddr);
        arr->pAddr = NULL;
    }


    free(arr);
    arr = NULL;

}

源文件 seqQueue.c :

#include "seqQueue.h"

//初始化队列
seqQueue init_SeqQueue()
{
    struct dynamicArray * array = init_dynamicArray(MAX);

    return array;
}
//入队
void push_SeqQueue(seqQueue queue, void * data)
{
    //等价于 尾插
    if (queue == NULL)
    {
        return;
    }
    if (data == NULL)
    {
        return;
    }

    struct dynamicArray * array = queue;

    if (array->m_Size >= MAX)
    {
        return;
    }

    insert_dynamicArray(array, array->m_Size, data);
}
//出队
void pop_SeqQueue(seqQueue queue)
{
    //等价于  头删除
    if (queue == NULL)
    {
        return;
    }

    struct dynamicArray * array = queue;

    if (array->m_Size <= 0)
    {
        return;
    }
    removeByPos_dynamicArray(array, 0);
}
//返回队头元素
void * front_SeqQueue(seqQueue queue)
{
    if (queue == NULL)
    {
        return NULL;
    }

    struct dynamicArray * array = queue;

    return array->pAddr[0];

}

//返回队尾元素
void * back_SeqQueue(seqQueue queue)
{
    if (queue == NULL)
    {
        return NULL;
    }

    struct dynamicArray * array = queue;
    
    return array->pAddr[array->m_Size - 1];

}

//队列大小
int size_SeqQueue(seqQueue queue)
{
    if (queue == NULL)
    {
        return -1;
    }

    struct dynamicArray * array = queue;

    return array->m_Size;

}

//判断是否为空
int isEmpty_SeqQueue(seqQueue queue)
{
    if (queue == NULL)
    {
        return -1;
    }

    struct dynamicArray * array = queue;
    if (array->m_Size == 0)
    {
        return 1;
    }

    return 0;
}

//销毁队列
void destroy_SeqQueue(seqQueue queue)
{

    if (queue == NULL)
    {
        return ;
    }

    destroy_dynamicArray(queue);
    queue = NULL;
}

源文件test.c :

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "seqQueue.h"

struct Person
{
    char name[64];
    int age;
};

void test01()
{
    //初始化队列
    seqQueue myQueue = init_SeqQueue();

    //准备数据
    struct Person p1 = { "aaa", 10 };
    struct Person p2 = { "bbb", 20 };
    struct Person p3 = { "ccc", 30 };
    struct Person p4 = { "ddd", 40 };
    struct Person p5 = { "eee", 50 };

    //入队
    push_SeqQueue(myQueue, &p1);
    push_SeqQueue(myQueue, &p2);
    push_SeqQueue(myQueue, &p3);
    push_SeqQueue(myQueue, &p4);
    push_SeqQueue(myQueue, &p5);

    printf("队列大小为:%d\n", size_SeqQueue(myQueue));

    while (isEmpty_SeqQueue(myQueue) == 0) 
    {
        //队头元素
        struct Person * pFront =  front_SeqQueue(myQueue);
        printf("队头元素姓名:%s 年龄:%d \n", pFront->name, pFront->age);

        //队尾元素
        struct Person * pBack = back_SeqQueue(myQueue);
        printf("队尾元素姓名:%s 年龄:%d \n", pBack->name, pBack->age);

        //出队
        pop_SeqQueue(myQueue);
    }

    printf("队列大小为:%d\n", size_SeqQueue(myQueue));

    //销毁队列
    destroy_SeqQueue(myQueue);
    myQueue = NULL;
}

int main(){

    test01();

    system("pause");
    return EXIT_SUCCESS;
}

2.4队列的链式存储

  • 基本概念

队列也是一种特殊的线性表;可以用线性表链式存储来模拟队列的链式存储

头文件 linkQueue.h :

#pragma  once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//节点结构体
struct QueueNode
{
    struct QueueNode * next;

};

//链表的结构体 --- 队列
struct LQueue
{
    struct QueueNode pHeader; //头节点
    int m_Size; //队列的大小
    struct QueueNode * pTail; //记录尾节点的指针
};

typedef void * LinkQueue;

//初始化队列
LinkQueue init_LinkQueue();
//入队
void push_LinkQueue(LinkQueue queue, void * data);
//出队
void pop_LinkQueue(LinkQueue queue);
//返回队头
void * front_LinkQueue(LinkQueue queue);
//返回队尾
void * back_LinkQueue(LinkQueue queue);
//返回队列大小
int size_LinkQueue(LinkQueue queue);
//判断队列是否为空
int isEmpty_LinkQueue(LinkQueue queue);
//销毁队列
void destroy_LinkQueue(LinkQueue queue);

源文件 linkQueue.c :

#include "linkQueue.h"


//初始化队列
LinkQueue init_LinkQueue()
{
    struct LQueue * myQueue = malloc(sizeof(struct LQueue));
    if (myQueue == NULL)
    {
        return NULL;
    }

    myQueue->m_Size = 0;
    myQueue->pHeader.next = NULL;
    myQueue->pTail = &myQueue->pHeader; //尾节点开始指向的就是头节点
    return myQueue;
}
//入队
void push_LinkQueue(LinkQueue queue, void * data)
{
    //等价于 尾插
    if (queue == NULL)
    {
        return;
    }
    if (data == NULL)
    {
        return;
    }

    struct LQueue * myQueue = queue;

    struct QueueNode * myNode = data; 


    //更改指针指向
    myQueue->pTail->next = myNode;
    myNode->next = NULL;
    //更新尾节点
    myQueue->pTail = myNode;

    //更新队列大小
    myQueue->m_Size++;

}
//出队
void pop_LinkQueue(LinkQueue queue)
{
    //等价于 头删 

    if (queue == NULL)
    {
        return;
    }
    struct LQueue * myQueue = queue;

    if (myQueue->m_Size == 0)
    {
        return;
    }

    if (myQueue->m_Size == 1)
    {
        myQueue->pHeader.next = NULL;
        myQueue->pTail = &myQueue->pHeader; //维护尾节点指针
        myQueue->m_Size = 0;
        return;
    }

    //记录第一个节点
    struct QueueNode * pFirst = myQueue->pHeader.next;

    myQueue->pHeader.next = pFirst->next;

    //更新队列大小
    myQueue->m_Size--;

}
//返回队头
void * front_LinkQueue(LinkQueue queue)
{
    if (queue == NULL)
    {
        return NULL;
    }
    struct LQueue * myQueue = queue;

    return myQueue->pHeader.next;

}
//返回队尾
void * back_LinkQueue(LinkQueue queue)
{
    if (queue == NULL)
    {
        return NULL;
    }
    struct LQueue * myQueue = queue;

    return myQueue->pTail;

}
//返回队列大小
int size_LinkQueue(LinkQueue queue)
{
    if (queue == NULL)
    {
        return -1;
    }
    struct LQueue * myQueue = queue;

    return myQueue->m_Size;

}
//判断队列是否为空
int isEmpty_LinkQueue(LinkQueue queue)
{
    if (queue == NULL)
    {
        return -1;
    }
    struct LQueue * myQueue = queue;

    if (myQueue->m_Size == 0)
    {
        return 1;
    }

    return 0;

}
//销毁队列
void destroy_LinkQueue(LinkQueue queue)
{
    if (queue == NULL)
    {
        return;
    }
    free(queue);
    queue = NULL;
}

源文件 test.c :

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "linkQueue.h"

struct Person
{
    void * node;
    char name[64];
    int age;
};

void test01()
{
    //初始化队列
    LinkQueue myQueue = init_LinkQueue();

    //准备数据
    struct Person p1 = { NULL, "aaa", 10 };
    struct Person p2 = { NULL, "bbb", 20 };
    struct Person p3 = { NULL, "ccc", 30 };
    struct Person p4 = { NULL, "ddd", 40 };
    struct Person p5 = { NULL, "eee", 50 };

    //入队
    push_LinkQueue(myQueue, &p1);
    push_LinkQueue(myQueue, &p2);
    push_LinkQueue(myQueue, &p3);
    push_LinkQueue(myQueue, &p4);
    push_LinkQueue(myQueue, &p5);

    printf("队列大小为:%d\n", size_LinkQueue(myQueue));

    while (isEmpty_LinkQueue(myQueue) == 0)
    {
        //队头元素
        struct Person * pFront = front_LinkQueue(myQueue);
        printf("链式存储 队头元素姓名:%s 年龄:%d \n", pFront->name, pFront->age);

        //队尾元素
        struct Person * pBack = back_LinkQueue(myQueue);
        printf("链式存储 队尾元素姓名:%s 年龄:%d \n", pBack->name, pBack->age);

        //出队
        pop_LinkQueue(myQueue);
    }

    printf("队列大小为:%d\n", size_LinkQueue(myQueue));

    //销毁队列
    destroy_LinkQueue(myQueue);
    myQueue = NULL;
}


int main(){

    test01();

    system("pause");
    return EXIT_SUCCESS;
}

一般线性表详解,请参考文章:C语言数据结构(一)—— 数据结构理论、线性表【动态数组、链表(企业版单向链表)】

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

心上学事上练

感谢您的支持,让我们一起成长

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

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

打赏作者

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

抵扣说明:

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

余额充值