C语言数据结构系列队列篇

目录
  • 一、队列(Queue)
    • 0x00队列的概念
    • 0x01队列的结构
  • 二、队列的定义
    • 0x00链式队列
    • 0x02 接口函数
  • 三、队列的实现
    • 0x00队列初始化(QueueInit)
    • 0x01销毁队列(QueueDestroy)
    • 0x02判断队列是否为空(HeapIsEmpty)
    • 0x03入队(QueuePush)
    • 0x04出队(QueuePop)
    • 0x05 返回队头数据(QueueFront)
    • 0x06 返回队尾数据(QueueBack)
    • 0x07 求队列大小(QueueSize)
    • 0x08完整代码

一、队列(Queue)

0x00 队列的概念

概念:

① 队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。

② 入队列,进行插入操作的一端称为 队尾。出队列,进行删除操作的一端称为 队头。

③ 队列中的元素遵循先进先出的原则,即 FIFO 原则(First In First Out)

0x01 队列的结构

结构:

二、队列的定义

0x00 链式队列

typedef int QueueDataType;   //队列类型

typedef struct QueueNode {
    struct QueueNode* next;  //指向下一个节点
    QueueDataType data;      //数据
} QueueNode;

typedef struct Queue {
    QueueNode* pHead;        //头指针
    QueueNode* pTail;        //尾指针
} Queue;

为什么不使用单链表?

单链表我们只定义了一个指针指向头,没有定义尾指针。因为定义尾指针解决不了问题,比如尾插尾删。所以我们没有必要定义一个结构体把他们封到一起。这里我们再定义一个头指针 head 一个尾指针 tail ,这两个指针才有意义。因为根据队列的性质,我们只会在队尾插,不会再队尾删。所以这个尾指针的价值就得到了完美的体现,实际中定义几个指针是看你的需求确定的。

0x02 接口函数

这是需要实现几个接口函数:

void QueueInit(Queue* pQ);                  //队列初始化
void QueueDestroy(Queue* pQ);               //销毁队列
bool QueueIsEmpty(Queue* pQ);               //判断队列是否为空
void QueuePush(Queue* pQ, QueueDataType x); //入队
void QueuePop(Queue* pQ);                   //出队
QueueDataType QueueFront(Queue* pQ);        //返回队头数据
QueueDataType QueueBack(Queue* pQ);         //返回队尾数据
int QueueSize(Queue* pQ);                   //求队列大小

三、队列的实现

0x00 队列初始化(QueueInit)

Queue.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int QueueDataType;   //队列类型

typedef struct QueueNode {
    struct QueueNode* next;  //指向下一个节点
    QueueDataType data;      //数据
} QueueNode;

typedef struct Queue {
    QueueNode* pHead;        //头指针
    QueueNode* pTail;        //尾指针
} Queue;

void QueueInit(Queue* pQ);   //队列初始化

Queue.c

/* 队列初始化:将头尾指针置为NULL */
void QueueInit(Queue* pQ) {
    assert(pQ);                          //防止传入的pQ为空

    pQ->pHead = pQ->pTail = NULL;        //将头尾指针置空
}

解析:首先使用断言防止传入的pQ为空。初始化只需要把头指针和尾指针都置成空即可。

0x01 销毁队列(QueueDestroy)

/* 销毁队列:free掉所有队列元素并将头尾置空 */
void QueueDestroy(Queue* pQ) {
    assert(pQ);                          //防止传入的pQ为空

    QueueNode* cur = pQ->pHead;          //创建遍历指针cur
    while(cur != NULL) {                 //cur不为空就进入循环
        QueueNode* curNext = cur->next;  //信标指针curNext,防止释放cur后找不到其下一个节点
        free(cur);                       //释放cur当前指向的节点
        cur = curNext;                   //移动指针cur
    }
    pQ->pHead = pQ->pTail = NULL;        //置空干掉野指针
}

解读:

① 首先断言防止传入的pQ为空。

② 销毁要把所有节点都释放掉,我们创建遍历指针 cur 遍历整个队列。既然要释放 cur 指向的节点,为了防止释放 cur 之后找不到其下一个节点导致无法移动,我们这里创建一个类似于信标性质的指针 curNext 来记录一下 cur 的下一个节点,之后再 free 掉 cur,这样就可以移动 cur 了。

③ 最后为了防止野指针,还需要把头指针和尾指针都置为空。

0x02 判断队列是否为空(HeapIsEmpty)

Queue.h

bool QueueIsEmpty(Queue* pQ);               //判断队列是否为空

解读:布尔值,返回 true 或 false

Queue.c

/* 判断队列是否为空 */
bool QueueIsEmpty(Queue* pQ) {
    assert(pQ);                          //防止传入的pQ为空

    return pQ->pHead == NULL;            //如果成立则为True,不成立则为False
}

解读:

① 首先断言防止传入的pQ为空。

② 判断队列是否为空,可以直接返回,巧妙地利用布尔类型的特性。如果 pQ->pHead == NULL 成立则为真,会返回 true;不成立则为假,会返回 false。

0x03 入队(QueuePush)

Queue.h

void QueuePush(Queue* pQ, QueueDataType x); //入队

Queue.c

/* 入队:队尾入数据,对头出数据。如果是第一个入队的则既要当头又当尾 */
void QueuePush(Queue* pQ, QueueDataType x) {
    assert(pQ);             //防止传入的pQ为空

    /* 创建新节点:创建一个大小为QueueNode的空间 */
    QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));
    /* 检查malloc */
    if(new_node == NULL) {
        printf("malloc failed!\n");
        exit(-1);
    }
    /* 放置 */
    new_node->data = x;     //待插入的数据
    new_node->next = NULL;  //默认为空

    /* 入队:
     *【思路草图】
     *   情况1:队列为空:既当头又当尾
     *         [new_node]
     *           ↑    ↑
     *         pHead pTail
     *
     *   情况2:队列不为空:队尾入数据
     *          [] -> [] -> [] -> []   ->  [new_node]
     *         pHead             pTail     pTail->next
     *                            ↓             ↑
     *                            ----------→ pTail(更新尾指针)
     */
    if(pQ->pHead == NULL) {                //情况1: 队列为空
        pQ->pHead = pQ->pTail = new_node;  //       既当头又当尾
    } else {                               //情况2: 队列不为空
        pQ->pTail->next = new_node;        //       在现有尾的后一个节点放置new_node
        pQ->pTail = new_node;              //       更新pTail,使它指向新的尾
    }
}

解读:

① 首先断言防止传入的pQ为空。

② 我们首先要创建新节点。通过 malloc 动态内存开辟一块 QueueNode 大小的空间,都学到这里了大家想必都养成了检查 malloc 的好习惯了吧?。最后放置数据吗,将待插入的数据 x 交给 data,next 默认置空,和之前学链表一样,这里就不过多赘述了。

③ 新节点创建好后,我们可以开始写入队的操作了。首先要理解队列的性质:队尾入数据,队头出数据。这里既然是入队,就要在对尾后面进行插入。这里我们还要考虑到如果队列为空的情况,这时我们要把头指针和尾指针都交付给 new_node 。为了理清思路,我们可以画一个思路草图来帮助我们更好地理解:

有了这个图,我们就可以清楚地实现了:

if(pQ->pHead == NULL) {                //情况1: 队列为空
    pQ->pHead = pQ->pTail = new_node;  //       既当头又当尾
}
else {                                 //情况2: 队列不为空
    pQ->pTail->next = new_node;        //       在现有尾的后一个节点放置new_node
    pQ->pTail = new_node;              //       更新pTail,使它指向新的尾
}

当队列为空时,令头指针和尾指针都指向 new_node ,当队列不为空时,再尾部地下一个节点放置 new_node ,随后再更新尾指针让其指向新的尾(new_node 的位置)。

0x04 出队(QueuePop)

Queue.h

void QueuePop(Queue* pQ);                   //出队

Queue.c

/* 出队:队尾入数据,对头出数据 */
void QueuePop(Queue* pQ) {
    assert(pQ);                            //防止传入的pQ为空
    assert(!QueueIsEmpty(pQ));             //防止队列为空

    /* 出队:
     *【思路草图】
     *        [free] ->  []     -> [] -> []
     *        pHead    headNext
     *           ↓         ↑
     *           -------→ pHead(更新头指针)
     */
    QueueNode* headNext = pQ->pHead->next; //信标指针HeadNext
    free(pQ->pHead);
    pQ->pHead = headNext;                  //更新头

    /* 如果队内都被删完了,不处理pTail就会带来野指针的隐患
     * 【思路草图】
     *          NULL               已经被free掉的内存!
     *           ↑                         ↑   (野指针警告)
     *         pHead(因为HeadNext是NULL)  pTail
    */
    if(pQ->pHead == NULL)                  //如果pHead为空
        pQ->pTail = NULL;                  //处理一下尾指针,将尾指针置空
}

解读:

① 首先断言防止传入的 pQ 为空,这里还要放置队列为空,如果队列为空还要求出队的话会出问题的,所以这里要断言一下 QueueIsEmpty 为假。

② 思路草图如下:

出数据需要释放,和销毁一样,这里使用一个类似于信标性质的指针来记录 pHead 的下一个节点,之后我们就可以大胆地释放 pHead 而不用担心找不到了。free 掉之后更新头即可,令头指针指向 headNext 即可。 

注意:这里还要考虑一个问题,如果队内都被删完了,pHead 往后走指向空,但是 pTail 仍然指向那块已经被 free 掉的空间。pTail 就是一个典型的野指针。

我们可以不用担心 pHead,因为后面没有数据他会自然指向 NULL,但是我们这里得关注 pTail !我们需要手动处理一下它:

如果 pHead 为空,我们就把 pTail 也置为空即可。

 if(pQ->pHead == NULL)                  //如果pHead为空
        pQ->pTail = NULL;               //处理一下尾指针,将尾指针置空

0x05 返回队头数据(QueueFront)

Queue.h

QueueDataType QueueFront(Queue* pQ);        //返回队头数据

Queue.c

/* 返回队头数据 */
QueueDataType QueueFront(Queue* pQ) {
    assert(pQ);                            //防止传入的pQ为空
    assert(!QueueIsEmpty(pQ));             //防止队列为空

    return pQ->pHead->data;
}  

解读:

① 首先断言防止传入的 pQ 为空,这里我们还是要断言一下 QueueIsEmpty 为假,因为如果队内没有数据,还返回个锤子数据呢。

② 这里直接返回头的数据即可,特别简单没有什么好讲的。

0x06 返回队尾数据(QueueBack)

Queue.h

QueueDataType QueueBack(Queue* pQ);         //返回队尾数据

Queue.c

/* 返回队尾数据 */
QueueDataType QueueBack(Queue* pQ) {
    assert(pQ);                            //防止传入的pQ为空
    assert(!QueueIsEmpty(pQ));             //防止队列为空

    return pQ->pTail->data;
}

解读:

① 首先断言防止传入的 pQ 为空,断言一下 QueueIsEmpty 为假。

② 这里直接返回队尾的数据即可。

0x07 求队列大小(QueueSize)

Queue.h

int QueueSize(Queue* pQ);                   //求队列大小

Queue.c

/* 求队列大小:计数器法 */
int QueueSize(Queue* pQ) {
    assert(pQ);             //防止传入的pQ为空

    int count = 0;          //计数器
    QueueNode* cur = pQ->pHead;    //创建遍历指针cur
    while(cur != NULL) {
        ++count;            //计数+1
        cur = cur->next;    //移动指针cur
    }
    return count;
}

解读:这里我们采用计数器法来求大小即可,调用一次就是 O(N) ,也没什么不好的。

① 首先断言防止传入的 pQ 为空。

② 创建计数器变量和遍历指针 cur,遍历整个队列并计数,最后返回计数的结果即可。

0x08 完整代码

Queue.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int QueueDataType;   //队列类型

typedef struct QueueNode {
    struct QueueNode* next;  //指向下一个节点
    QueueDataType data;      //数据
} QueueNode;

typedef struct Queue {
    QueueNode* pHead;        //头指针
    QueueNode* pTail;        //尾指针
} Queue;

void QueueInit(Queue* pQ);                  //队列初始化
void QueueDestroy(Queue* pQ);               //销毁队列
bool QueueIsEmpty(Queue* pQ);               //判断队列是否为空
void QueuePush(Queue* pQ, QueueDataType x); //入队
void QueuePop(Queue* pQ);                   //出队
QueueDataType QueueFront(Queue* pQ);        //返回队头数据
QueueDataType QueueBack(Queue* pQ);         //返回队尾数据
int QueueSize(Queue* pQ);                   //求队列大小

Queue.c

#include <Queue.h>

/* 队列初始化:将头尾指针置为NULL */
void QueueInit(Queue* pQ) {
    assert(pQ);                          //防止传入的pQ为空

    pQ->pHead = pQ->pTail = NULL;        //将头尾指针置空
}

/* 销毁队列:free掉所有队列元素并将头尾置空 */
void QueueDestroy(Queue* pQ) {
    assert(pQ);                          //防止传入的pQ为空

    QueueNode* cur = pQ->pHead;          //创建遍历指针cur
    while(cur != NULL) {                 //cur不为空就进入循环
        QueueNode* curNext = cur->next;  //信标指针curNext,防止释放cur后找不到其下一个节点
        free(cur);                       //释放cur当前指向的节点
        cur = curNext;                   //移动指针cur
    }
    pQ->pHead = pQ->pTail = NULL;        //置空干掉野指针
}

/* 判断队列是否为空 */
bool QueueIfEmpty(Queue* pQ) {
    assert(pQ);                          //防止传入的pQ为空

    return pQ->pHead == NULL;            //如果成立则为True,不成立则为False
}

/* 入队:队尾入数据,对头出数据。如果是第一个入队的则既要当头又当尾 */
void QueuePush(Queue* pQ, QueueDataType x) {
    assert(pQ);             //防止传入的pQ为空

    /* 创建新节点:创建一个大小为QueueNode的空间 */
    QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));
    /* 检查malloc */
    if(new_node == NULL) {
        printf("malloc failed!\n");
        exit(-1);
    }
    /* 放置 */
    new_node->data = x;     //待插入的数据
    new_node->next = NULL;  //默认为空

    /* 入队:
     *【思路草图】
     *   情况1:队列为空:既当头又当尾
     *         [new_node]
     *           ↑    ↑
     *         pHead pTail
     *
     *   情况2:队列不为空:队尾入数据
     *          [] -> [] -> [] -> []   ->  [new_node]
     *         pHead             pTail     pTail->next
     *                            ↓             ↑
     *                            ----------→ pTail(更新尾指针)
     */
    if(pQ->pHead == NULL) {                //情况1: 队列为空
        pQ->pHead = pQ->pTail = new_node;  //       既当头又当尾
    } else {                               //情况2: 队列不为空
        pQ->pTail->next = new_node;        //       在现有尾的后一个节点放置new_node
        pQ->pTail = new_node;              //       更新pTail,使它指向新的尾
    }
}

/* 出队:队尾入数据,对头出数据 */
void QueuePop(Queue* pQ) {
    assert(pQ);                            //防止传入的pQ为空
    assert(!QueueIsEmpty(pQ));             //防止队列为空

    /* 出队:

     *【思路草图】
     *        [free] ->  []     -> [] -> []
     *        pHead    headNext
     *           ↓         ↑
     *           -------→ pHead(更新头指针)
     */
    QueueNode* headNext = pQ->pHead->next; //信标指针HeadNext,防止释放pHead后找不到其下一个节点
    free(pQ->pHead);
    pQ->pHead = headNext;                  //更新头

    /* 如果队内都被删完了,不处理pTail就会带来野指针的隐患
     * 【思路草图】
     *          NULL               已经被free掉的空间!
     *           ↑                         ↑   (野指针)
     *         pHead(因为HeadNext是NULL)  pTail
    */
    if(pQ->pHead == NULL)                  //如果pHead为空
        pQ->pTail = NULL;                  //处理一下尾指针,将尾指针置空
}

/* 返回队头数据 */
QueueDataType QueueFront(Queue* pQ) {
    assert(pQ);                            //防止传入的pQ为空
    assert(!QueueIsEmpty(pQ));             //防止队列为空

    return pQ->pHead->data;
}   

/* 返回队尾数据 */
QueueDataType QueueBack(Queue* pQ) {
    assert(pQ);                            //防止传入的pQ为空
    assert(!QueueIsEmpty(pQ));             //防止队列为空

    return pQ->pTail->data;
}

/* 求队列大小:计数器法 */
int QueueSize(Queue* pQ) {
    assert(pQ);             //防止传入的pQ为空

    int count = 0;          //计数器
    QueueNode* cur = pQ->pHead;    //创建遍历指针cur
    while(cur != NULL) {
        ++count;            //计数+1
        cur = cur->next;    //移动指针cur
    }
    return count;
}

Test.c

#include "Queue.h"

void TestQueue1() {
    Queue q;
    QueueInit(&q);

    QueuePush(&q, 1);
    QueuePush(&q, 2);
    QueuePush(&q, 3);
    QueuePush(&q, 4);

    QueuePop(&q);
    QueuePop(&q);
    QueuePop(&q);
    QueuePop(&q);
    //QueuePop(&q);

    QueueDestroy(&q);
}

void TestQueue2() {
    Queue q;
    QueueInit(&q);

    QueuePush(&q, 1);
    QueuePush(&q, 2);
    QueuePush(&q, 3);
    QueuePush(&q, 4);

    //假设先入了1 2,让1出来,再继续入,它的顺序还是不会变。
    // 永远保持先进先出的,无论是入了两个出两个,再入再出,还是全部入完了再出,都是不会变的。这就是队列的性质
    while(!QueueIsEmpty(&q)) {
        QueueDataType front = QueueFront(&q);
        printf("%d ", front);
        QueuePop(&q);  //pop掉去下一个
    }
    printf("\n");

    QueueDestroy(&q);
}

int main(void) {
    TestQueue2();

    return 0;
}

参考资料:

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

笔者:王亦优

更新: 2021.11.17

勘误: 无

声明: 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

本篇完。

到此这篇关于C语言数据结构系列队列篇的文章就介绍到这了,更多相关C语言 队列内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言数据结构之队列算法详解

    目录 一.前言 二.基本概念 三.顺序队列 四.链队列 五.循环队列 六.总结与提高 一.前言 队列在程序设计中经常出现,如:操作系统中的排队问题. 这篇文章主要介绍了队列的基本概念.性质,顺序.链.循环三种不同的方法实现队列,顺序和循环队列在算法中比较常用 二.基本概念    定义:队列是允许在一端插入,另一端删除的线性表 队头(front):允许删除的一端 队尾(rear):允许插入的一端 特点:先进先出 三.顺序队列 动态图: 算法讲解:  图解:入队,rear++,出队,front++

  • C语言数据结构之链队列的基本操作

    目录 1.队列的定义 2.队列的表示和实现 (1) 初始化操作 (2)销毁队列 (3) 入队操作 (4) 出队操作 附录完整代码: 总结 1.队列的定义 队列 (Queue)是另一种限定性的线性表,它只允许在表的一端插入元素,而在另一端删除元素,所以队列具有先进先出(Fist In Fist Out,缩写为FIFO)的特性.在队列中,允许插入的一端叫做队尾(rear),允许删除的一端则称为队头(front). 假设队列为q=(a1,a2,-,an),那么a1就是队头元素,an则是队尾元素.队列中

  • 详解数据结构C语言实现之循环队列

    本文讲的是循环队列,首先我们必须明白下面几个问题 循环队列的基础知识 1.循环队列需要几个参数来确定 循环队列需要2个参数,front和rear 2.循环队列各个参数的含义 (1)队列初始化时,front和rear值都为零: (2)当队列不为空时,front指向队列的第一个元素,rear指向队列最后一个元素的下一个位置: (3)当队列为空时,front与rear的值相等,但不一定为零: 3.循环队列入队的伪算法 (1)把值存在rear所在的位置: (2)rear=(rear+1)%maxsize

  • C语言数据结构进阶之栈和队列的实现

    目录 栈的实现: 一.栈的概念和性质 二.栈的实现思路 三.栈的相关变量内存布局图 四.栈的初始化和销毁 五.栈的接口实现: 1.入栈 2.出栈 3.获取栈顶的数据 4.获取栈的元素个数 5.判断栈是否为空 队列的实现: 一.队列的概念和性质 二.队列的实现思路 三.队列相关变量的内存布局图 四.队列的初始化和销毁 五.队列的接口实现: 1. 入数据 2.出数据 3.取队头数据 4.取队尾数据 5.获取队列元素个数 6.判断队列是否为空 总结 栈的实现: 一.栈的概念和性质 栈(stack)又名

  • C语言编程数据结构栈与队列的全面讲解示例教程

    目录 一.栈的表示和实现 1栈的概念和结构 2栈的初始化 3压栈(栈顶插入一个数据) 4出栈(栈顶删除一个数据) 5取栈顶元素 6取栈顶元素 7判断栈是否为空 二.队列的表示和实现 1队列的概念及结构 2队列的实现 3队列初始化 4入队(队尾插入一个数据) 5出队(队头删除一个数据) 6取队头数据 7取队尾数据 8计算队列中数据个数 9判断队列是否为空 10销毁队列 总结 一.栈的表示和实现 1栈的概念和结构 栈:一种特殊的线性表(逻辑上数据是连着放的),其只允许在固定的一端进行插入和删除操作.

  • C语言编程数据结构的栈和队列

    目录 栈 数组实现 标题全部代码 Stack_array.c Stack_array.h 初始化数组栈 满栈后扩容 是否为空栈 压栈和退栈 链表实现 stack_chain.h stack_chain.c 整个压栈流程 整个弹栈流程 出栈情况 队列 队列的实现 queue_chain.h queue_chain.c 一个结构体类型用于维护这个队列 概念流程图 入队列的实现 出队列的实现 是否空队 栈 栈是一种以后进先出为顺序对对象进行添加或删除的数据结构 对栈进行形象记忆就像是桌子上的一堆书或一

  • C语言数据结构链表队列的实现

    C语言数据结构链表队列的实现 1.写在前面 队列是一种和栈相反的,遵循先进先出原则的线性表. 本代码是严蔚敏教授的数据结构书上面的伪代码的C语言实现代码. 分解代码没有包含在内的代码如下: #include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 typedef int QElemtype; typedef int status; 2.代码分解 2.1对队列和节点的结构定义 typedef struct

  • C语言数据结构系列队列篇

    目录 一.队列(Queue) 0x00队列的概念 0x01队列的结构 二.队列的定义 0x00链式队列 0x02 接口函数 三.队列的实现 0x00队列初始化(QueueInit) 0x01销毁队列(QueueDestroy) 0x02判断队列是否为空(HeapIsEmpty) 0x03入队(QueuePush) 0x04出队(QueuePop) 0x05 返回队头数据(QueueFront) 0x06 返回队尾数据(QueueBack) 0x07 求队列大小(QueueSize) 0x08完整

  • C语言数据结构系列篇二叉树的遍历

    目录 前言: Ⅰ. 定义二叉树 0x00二叉树的概念(回顾) 0x00定义二叉树 0x01 手动创建二叉树 Ⅱ. 二叉树的遍历 0x00关于遍历 0x01二叉树前序遍历 0x02二叉树中序遍历 0x03二叉树后序遍历 0x04层序遍历 前言: 学习二叉树的基本操作前,需要先创建一颗二叉树,然后才能学习其相关的基本操作,考虑到我们刚刚接触二叉树,为了能够先易后难地进行讲解,我们将暂时手动创建一颗简单的二叉树,用来方便大家学习.等二叉树结构了解的差不多后,后期我们会带大家研究二叉树地真正的创建方式.

  • C语言数据结构系列篇二叉树的概念及满二叉树与完全二叉树

    链接:C语言数据结构系列之树的概念结构和常见表示方法 0x00 概念 定义:二叉树既然叫二叉树,顾名思义即度最大为2的树称为二叉树. 它的度可以为 1 也可以为 0,但是度最大为 2 . 一颗二叉树是节点的一个有限集合,该集合: ① 由一个根节点加上两颗被称为左子树和右子树的二叉树组成 ② 或者为空 观察上图我们可以得出如下结论: ① 二叉树不存在度大于 2 的节点,换言之二叉树最多也只能有两个孩子. ② 二叉树的子树有左右之分,分别为左孩子和右孩子.次序不可颠倒,因此二叉树是有序树. 注意:对

  • C语言数据结构之队列的定义与实现

    目录 一.队列的性质 二.队列的结构 三.代码实现 头文件 功能函数 一.队列的性质 上次我们学习栈,了解到栈储存释放数据的方式是:先进后出 而队列与其相反,队列是:先进先出,后进后出. 二.队列的结构 多个链表节点 + 头尾指针   (链表式队列) 链表节点负责存储数据:头节点 负责定位先进的起始数据,方便先出: 尾节点负责记录尾部数据,方便确定队列当前状态. 三.代码实现 头文件 这里方便统一调用,将头尾指针定义成一个结构体 . #include<stdio.h> #include<

  • C语言数据结构系列之树的概念结构和常见表示方法

    0x00 树的概念 树是一种非线性的数据结构,它是由 n(n >= 0)个有限节点组成的一个具有层次关系的集合. 那么为什么叫 "树" 呢? 我们之所以把它成为 "树",是因为它很像我们现实生活中的树.只是它是倒过来的,根朝上叶子朝下. 0x01 树的结构 ① 有一个特殊的节点,成为根节点,根节点不存在前驱节点. ② 除根节点外,其余节点被分成 M(M>0) 个互不相交的集合 T1.T2.…….Tm,期中没一个集合 Ti(1 <= i <=

  • C利用语言实现数据结构之队列

    目录 一.链队列 二.链队的表示 三.链队的基本操作 1. 链队的初始化 2. 链队的销毁 3. 入队 4. 出队 四.顺序队列 五.循环队列 1. 初始化 2. 求队列长度 3. 入队 4. 出队 前言: 队列在生活中也比较常见,例如购物排队--新来的成员总是加入队尾,每次离开的成员总是队列头上的. 队列按存储方式可以分为两种:顺序队列和链队列. 一.链队列 链式队列中每个元素定义成一个结点,含数据域与指针域(指向下一结点),并设头尾指针.用图表示就是. 二.链队的表示 前面的链式结构,总是使

随机推荐