C语言单链队列的表示与实现实例详解

C语言单链队列的表示与实现实例详解

什么是队列?

在计算机科学中,队列(Queue)是一种特殊的数据结构,它只允许在一端进行插入操作,在另一端进行删除操作。将新元素插入队列的过程可以称之为入队,而将元素从队列中删除的过程则可以称之为出队。队列的核心思想是“先进先出”(First In First Out,FIFO),即先入队的元素先出队。

单链队列的表示方式

单链队列(Linked Queue)是一种基于链表的队列实现方式,它由若干个结点构成,每个结点至少需要包含两个部分:数据域和指针域。数据域用于存储实际数据,指针域则用于指向下一个结点。在单链队列中,队首指针指向链表头结点,队尾指针则指向链表尾结点。

单链队列的基本操作包括以下几个方面:

  • 初始化队列
  • 判断队列是否为空
  • 获取队列长度
  • 入队操作
  • 出队操作
  • 清空队列
  • 销毁队列

下面根据C语言代码实现这些操作。

单链队列的初始化操作

typedef struct QueueNode {
    int data;   // 数据域
    struct QueueNode *next;  // 指针域
} QueueNode, *QueueNodePtr;

typedef struct {
    QueueNodePtr front; // 队首指针
    QueueNodePtr rear;  // 队尾指针
    int length;     // 队列长度
} LinkedQueue;

// 初始化队列
void initQueue(LinkedQueue *queue) {
    queue->front = queue->rear = (QueueNodePtr) malloc(sizeof(QueueNode));
    if (NULL == queue->front) {
        exit(1);    // 内存分配失败,退出程序
    }
    queue->front->next = NULL;  // 队列为空,指向空指针
    queue->length = 0;
}

在队列初始化时,我们需要分配一个头结点,同时将队首和队尾指针都指向这个头结点,表示队列空。由于初始化操作有可能分配失败,因此需要进行内存异常处理。

单链队列的入队操作

// 入队操作
void enQueue(LinkedQueue *queue, int data) {
    QueueNodePtr p;
    p = (QueueNodePtr) malloc(sizeof(QueueNode));
    if (NULL == p) {
        exit(1);    // 内存分配失败,退出程序
    }
    p->data = data;
    p->next = NULL;
    queue->rear->next = p;
    queue->rear = p;
    queue->length++;
}

在入队操作中,我们需要新建一个结点,为其赋值后再插入到队列的尾部。为此需要指针操作,将尾指针指向新结点即可。同时还需要维护队列长度变量。

单链队列的出队操作

// 出队操作
int deQueue(LinkedQueue *queue) {
    int data;
    QueueNodePtr p;
    if (queue->front == queue->rear) {
        printf("队列为空\n");
        exit(1);
    }
    p = queue->front->next;
    data = p->data;
    queue->front->next = p->next;   // 将头结点的指针指向下一个结点
    if (queue->rear == p) {     // 最后一个结点出队,需要更新队尾指针
        queue->rear = queue->front;
    }
    free(p);
    queue->length--;
    return data;
}

在出队操作中,我们需要先判断队列是否为空。如果不为空,我们需要获取队列头结点的下一个结点,并将头结点的指针指向这个结点即可。如果出队的是队列中唯一的结点,则需要同时更新队首和队尾指针。

示例1:用单链队列求解斐波那契数列

下面我们来看一个具体的示例,在这个例子中,我们将使用单链队列来求解斐波那契数列。

斐波那契数列是指这样一个数列:0、1、1、2、3、5、8、13、21、34、…… 它从第三项开始,每一项都等于前两项之和,即 f(n) = f(n-1) + f(n-2)。用数学公式来表示,就是:

f(0) = 0
f(1) = 1
f(n) = f(n-1) + f(n-2) , n>=2

我们可以定义一个单链队列来保存前两个数,然后循环计算下一个数,并将其加入到队列中,直至计算出需要的数。

以下是用单链队列求解斐波那契数列的C语言代码:

// 用单链队列求解斐波那契数列
void fibonacciByLinkedQueue(int n) {
    LinkedQueue queue;
    initQueue(&queue);
    enQueue(&queue, 0);
    enQueue(&queue, 1);

    for (int i = 0; i < n; i++) {
        int x = deQueue(&queue);
        int y = deQueue(&queue);
        printf("%d ", x);
        enQueue(&queue, y);
        enQueue(&queue, x+y);
    }
    printf("\n");
    clearQueue(&queue);
}

这个例子中,我们需要初始化一个队列,并将前两个斐波那契数列中的值入队。然后在循环中计算下一个数,同时将新计算出来的数加入到队列中即可。

示例2:用单链队列解决迷宫问题

下面我们来看另一个例子,它展示了单链队列在解决迷宫问题时的应用。

在迷宫问题中,我们需要遍历迷宫的所有路径,并找到从起点到终点的一条路径。这个过程可以使用深度优先搜索算法来实现。当然,我们也可以使用广度优先搜索算法来解决这个问题。

广度优先搜索算法使用队列来进行遍历,具体实现方式是:先将起点入队,然后每次出队一个节点,检查它的邻居是否可以到达终点,如果可以就入队,如果不行就弹出继续遍历下一个节点,直至遇到终点。

以下是用单链队列解决迷宫问题的C语言代码:

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

#define ROW 8
#define COL 8

typedef struct Point {
    int x;
    int y;
} Point;

// 定义迷宫的矩阵
int map[ROW][COL] = {
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 1, 1, 0, 1, 1, 1, 0},
    {0, 1, 1, 0, 1, 1, 1, 0},
    {0, 1, 1, 1, 1, 0, 0, 1},
    {0, 1, 0, 0, 0, 1, 1, 1},
    {0, 1, 1, 1, 0, 1, 1, 1},
    {0, 1, 0, 1, 1, 1, 0, 0},
    {0, 1, 1, 1, 0, 1, 1, 0}
};

// 定义单链队列的结构体
typedef struct QueueNode {
    Point data;
    struct QueueNode *next;
} QueueNode, *QueueNodePtr;

typedef struct {
    QueueNodePtr front;
    QueueNodePtr rear;
    int length;
} LinkedQueue;

// 初始化队列
void initQueue(LinkedQueue *queue) {
    queue->front = queue->rear = (QueueNodePtr) malloc(sizeof(QueueNode));
    if (NULL == queue->front) {
        exit(1);
    }
    queue->front->next = NULL;
    queue->length = 0;
}

// 入队操作
void enQueue(LinkedQueue *queue, Point data) {
    QueueNodePtr p;
    p = (QueueNodePtr) malloc(sizeof(QueueNode));
    if (NULL == p) {
        exit(1);
    }
    p->data = data;
    p->next = NULL;
    queue->rear->next = p;
    queue->rear = p;
    queue->length++;
}

// 出队操作
Point deQueue(LinkedQueue *queue) {
    Point data;
    QueueNodePtr p;
    if (queue->length == 0) {
        printf("队列为空\n");
        exit(1);
    }
    p = queue->front->next;
    data = p->data;
    queue->front->next = p->next;
    if (queue->rear == p) {
        queue->rear = queue->front;
    }
    free(p);
    queue->length--;
    return data;
}

// 判断是否越界
int isOutOfBounds(int x, int y) {
    if (x < 0 || x >= ROW || y < 0 || y >= COL) {
        return 1;
    }
    return 0;
}

// 判断当前点是否为终点
int isEndpoint(Point point) {
    return point.x == 6 && point.y == 7;
}

// 判断是否为障碍物
int isObstacle(Point point) {
    return map[point.x][point.y] == 1;
}

// 解决迷宫问题
void mazeSolution() {
    LinkedQueue queue;
    initQueue(&queue);
    Point start = {1, 0};
    Point end = {6, 7};
    enQueue(&queue, start);
    int book[ROW][COL];  // 记录每个点是否被走过
    memset(book, 0, ROW*COL*sizeof(int));  // 初始化为0

    int next[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};   // 右、左、下、上

    while (queue.length > 0) {
        Point current = deQueue(&queue);
        if (isEndpoint(current)) {
            printf("解:%d,%d\n", current.x, current.y);
            int x = current.x;
            int y = current.y;
            while (x != start.x || y != start.y) {
                // 逆推路径
                for (int i = 0; i < 4; i++) {
                    int tx = x - next[i][0];
                    int ty = y - next[i][1];
                    if (!isOutOfBounds(tx, ty) && book[tx][ty] == book[x][y]-1) {
                        printf("解:%d,%d\n", tx, ty);
                        x = tx;
                        y = ty;
                        break;
                    }
                }
            }
            clearQueue(&queue);
            return;
        }

        for (int i = 0; i < 4; i++) {
            int nx = current.x + next[i][0];
            int ny = current.y + next[i][1];
            Point nextPoint = {nx, ny};
            if (!isOutOfBounds(nx, ny) && !isObstacle(nextPoint) && book[nx][ny] == 0) {
                book[nx][ny] = book[current.x][current.y] + 1;
                enQueue(&queue, nextPoint);
            }
        }
    }

    printf("无解\n");
    clearQueue(&queue);
}

int main() {
    mazeSolution();
    return 0;
}

这个例子中,我们使用单链队列来实现广度优先搜索算法,求解迷宫问题。其中需要用到元素类型为Point的队列,因此需要在队列中进行相应的修改。通过遍历矩阵中的每个点,我们可以找到从起点到终点的一条路径。在遍历过程中,我们需要注意的是先入队后出队,同时在每个点上将路径距离的长度记录下来,以便之后回推路径。

以上代码可能有些长,但只要代码实现细节都注意到位,就可以很好地解决相关问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言单链队列的表示与实现实例详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

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

    C语言实现队列的完整攻略 什么是队列 队列是一种线性数据结构,它有两个端点:队头和队尾。新的元素插入到队尾,每次从队头取出一个元素。这就类似于人们排队买票,新的买票者排在队尾,每当售票员完成一笔交易,队列头的买票者出队。 基本操作 队列主要有以下3个基本操作: 入队(enqueue):将一个元素添加到队列的尾部 出队(dequeue):从队列的头部移除一个元…

    数据结构 2023年5月17日
    00
  • 数据结构 栈的操作实例详解

    数据结构 栈的操作实例详解 什么是栈? 栈(stack)是一种具有特殊限制的线性数据结构。它只允许在表的一端进行插入和删除操作,另一端是固定的,称为栈底;相反的另一端称为栈顶。栈底用于存放最早进入的元素,栈顶用于存放最近进入的元素,所以栈又称为后进先出的数据结构。 栈的操作 栈的主要操作包括入栈(push)、出栈(pop)、获取栈顶元素(top)和判断栈是否…

    数据结构 2023年5月17日
    00
  • 「学习笔记」AC 自动机

    「学习笔记」AC 自动机 点击查看目录 目录 「学习笔记」AC 自动机 算法 问题 思路 代码 例题 Keywords Search 玄武密码 单词 病毒 最短母串 文本生成器 背单词 密码 禁忌 前置:「学习笔记」字符串基础:Hash,KMP与Trie。 好像对例题的讲解越来越抽象了? 算法 问题 求 \(n\) 个单词在一个长度为 \(m\) 的文章里出…

    算法与数据结构 2023年5月5日
    00
  • C语言数据结构时间复杂度及空间复杂度简要分析

    C语言数据结构时间复杂度及空间复杂度简要分析 什么是时间复杂度和空间复杂度? 在分析算法和数据结构的性能时,时间复杂度和空间复杂度是必须考虑的因素。 时间复杂度:衡量算法执行时间所需的资源,也就是算法的速度。通常使用“大O符号”来表示时间复杂度,例如O(1)、O(n)、O(nlogn)等。 空间复杂度:衡量算法使用的内存资源,也就是算法的空间利用率。通常使用…

    数据结构 2023年5月17日
    00
  • Java数据结构常见几大排序梳理

    Java数据结构常见几大排序梳理 在Java中,数据排序是非常常见的操作。下面我们详细讲解一下Java数据结构常见几大排序的梳理。 常见几大排序 Java数据结构中常见几种排序算法包括: 冒泡排序(Bubble Sort) 快速排序(Quick Sort) 插入排序(Insertion Sort) 选择排序(Selection Sort) 希尔排序(Shel…

    数据结构 2023年5月17日
    00
  • 莫比乌斯反演,欧拉反演学习笔记

    (未更完) 我算法中也就差点数论没学了,这几周卷了,学了一下,分享一下啊。 我会讲得详细一点,关于我不懂得地方,让新手更容易理解。 学习反演有很多定义啥的必须要记的,学的时候容易崩溃,所以希望大家能坚持下来。   第一个定义: $\lfloor x\rfloor$:意思是小于等于 $x$ 的最大整数。 数论分块 学习反演之前,要先学习一些边角料,先来看数论分…

    算法与数据结构 2023年4月17日
    00
  • Java数据结构之KMP算法的实现

    Java数据结构之KMP算法的实现 1. KMP算法的概述 KMP算法的全称是Knuth-Morris-Pratt算法,是一种字符串匹配算法,用于在文本串S内查找一个模式串P的出现位置。它的特点是在P和S两个序列中,当匹配失败时,它会跳过P的部分已匹配的字符,利用这个信息来减少S和P之间的匹配次数,从而提高匹配效率。 2. KMP算法的实现 2.1 预处理失…

    数据结构 2023年5月17日
    00
  • Java 中很好用的数据结构(你绝对没用过)

    Java 中很好用的数据结构(你绝对没用过) 介绍 Java 中的数据结构有很多,比如数组、链表、栈、队列、堆、树等等。在这些常见的数据结构中,我们或多或少都会使用到。但是本篇文章要讲述的是一些比较冷门,但是很好用的数据结构。 双向队列(Deque) 双向队列,顾名思义,是一种可以双向操作的队列。它可以从队列的两端插入和删除元素,因此常被用作实现栈和队列以及…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部