C语言数据结构与算法之链表(一)

欢迎阅读本篇文章,本文将为大家详细讲解C语言中数据结构与算法之链表。接下来,将从以下几个方面为大家讲述:

  1. 链表的定义
  2. 链表的特点
  3. 链表的分类
  4. 单向链表的实现及应用
  5. 双向链表的实现及应用
  6. 示例说明

1. 链表的定义

链表是由一系列节点组合而成的数据结构,每个节点都包含了一个数据域和一个指向下一个节点的指针域。其中,链表的头结点为第一个节点,而尾节点为最后一个节点。链表通过指针连接各个节点,形成链式的数据存储结构。

2. 链表的特点

链表具有以下特点:

  1. 以节点为单位存储数据,节点之间通过指向下一个节点的指针相连。
  2. 链表的大小可动态变化,可以随时在链表中添加或删除节点。
  3. 链表可以高效地插入和删除元素,时间复杂度为O(1)。

3. 链表的分类

链表可以分为以下两种类型:

  1. 单向链表
    单向链表中的节点只含有一个指针域,指向下一个节点。

  2. 双向链表
    双向链表中的节点含有两个指针域,一个指向下一个节点,另一个指向前一个节点。

4. 单向链表的实现及应用

单向链表的实现比较简单,其中一个简单的实现方式是:

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

typedef struct node {
    int data;            // 数据域
    struct node *next;   // 指针域,指向下一个节点
} Node;

int main() {
    Node *head = (Node*) malloc(sizeof(Node));    // 头结点
    head -> next = NULL;                           // 初始化

    // 创建链表
    for(int i = 0; i < 5; i++) {
        Node *newNode = (Node*) malloc(sizeof(Node));
        newNode -> data = i;
        newNode -> next = head -> next;
        head -> next = newNode;
    }

    // 遍历链表并输出
    Node *p = head -> next;
    while(p != NULL) {
        printf("%d ", p -> data);
        p = p -> next;
    }
    printf("\n");

    return 0;
}

单向链表的应用比较广泛,比如实现队列、链式栈和图等数据结构,甚至在一些算法题中也会用到。

5. 双向链表的实现及应用

双向链表相比单向链表,多出了一个指针来指向前面的节点。因此,双向链表有以下的实现方式:

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

typedef struct node {
    int data;            // 数据域
    struct node *prev;   // 指针域,指向前一个节点
    struct node *next;   // 指针域,指向下一个节点
} Node;

int main() {
    Node *head = (Node*) malloc(sizeof(Node));    // 头结点
    head -> prev = NULL;
    head -> next = NULL;

    // 创建链表
    Node *prev = head, *p = NULL;
    for(int i = 0; i < 5; i++) {
        p = (Node*) malloc(sizeof(Node));
        p -> data = i;
        p -> prev = prev;
        p -> next = NULL;
        prev -> next = p;
        prev = p;
    }

    // 遍历链表并输出
    p = head -> next;
    while(p != NULL) {
        printf("%d ", p -> data);
        p = p -> next;
    }
    printf("\n");

    return 0;
}

双向链表可以在单向链表的基础上,更方便地进行双向遍历以及双向查找。

6. 示例说明

下面,我们来看两个示例。

示例一:使用链表实现栈

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

typedef struct node {
    int data;            // 数据域
    struct node *next;   // 指针域,指向下一个节点
} Node;

typedef struct stack {
    Node *top;    // 栈顶指针
} Stack;

void InitStack(Stack *s) {
    s -> top = (Node*) malloc(sizeof(Node));    // 头结点
    s -> top -> next = NULL;
}

int IsEmpty(Stack *s) {
    return s -> top -> next == NULL;
}

void Push(Stack *s, int x) {
    Node *newNode = (Node*) malloc(sizeof(Node));
    newNode -> data = x;
    newNode -> next = s -> top -> next;
    s -> top -> next = newNode;
}

void Pop(Stack *s) {
    if(IsEmpty(s)) {
        printf("Error: Stack is empty!\n");
        return;
    }
    Node *temp = s -> top -> next;
    s -> top -> next = temp -> next;
    free(temp);
}

int Top(Stack *s) {
    if(IsEmpty(s)) {
        printf("Error: Stack is empty!\n");
        return -1;
    }
    return s -> top -> next -> data;
}

int main() {
    Stack s;
    InitStack(&s);
    Push(&s, 1);
    Push(&s, 2);
    Push(&s, 3);
    Pop(&s);
    Push(&s, 4);
    printf("%d\n", Top(&s));
    return 0;
}

以上代码使用单向链表实现了栈的基本操作,满足了栈的后进先出原则。

示例二:使用链表实现图

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

#define MAXVEX 100

typedef struct EdgeNode {
    int adjvex;                 // 邻接顶点在顶点数组中下标
    struct EdgeNode *next;      // 指向下一个邻接点
    int weight;                 // 边权值
} EdgeNode;

typedef struct VertexNode {
    int data;                   // 顶点信息
    EdgeNode *firstedge;        // 指向第一个邻接点
} VertexNode, AdjList[MAXVEX];

typedef struct GraphAdjList {
    AdjList adjList;            // 邻接表
    int numNodes, numEdges;     // 顶点数和边数
} GraphAdjList;

void CreateGraph(GraphAdjList *G) {
    int i, j, k, w;
    printf("Enter the number of nodes and edges: \n");
    scanf("%d%d", &G -> numNodes, &G -> numEdges);
    for(i = 0; i < G -> numNodes; i++) {
        printf("Enter vertex[%d] data: \n", i);
        scanf("%d", &G -> adjList[i].data);
        G -> adjList[i].firstedge = NULL;
    }
    for(k = 0; k < G -> numEdges; k++) {
        printf("Enter edge[%d] direction(i, j) and weight(w): \n", k);
        scanf("%d%d%d", &i, &j, &w);
        EdgeNode *e = (EdgeNode*) malloc(sizeof(EdgeNode));
        e -> adjvex = j;
        e -> weight = w;
        e -> next = G -> adjList[i].firstedge;
        G -> adjList[i].firstedge = e;
    }
}

void Print(GraphAdjList *G) {
    int i;
    EdgeNode *p;
    for(i = 0; i < G -> numNodes; i++) {
        printf("vertex[%d]: data=%d, edge=%d->", i, G -> adjList[i].data, i);
        p = G -> adjList[i].firstedge;
        while(p != NULL) {
            printf("%d(%d) ", p -> adjvex, p -> weight);
            p = p -> next;
        }
        printf("\n");
    }
}

int main() {
    GraphAdjList G;
    CreateGraph(&G);
    Print(&G);
    return 0;
}

以上代码使用单向链表实现了图的存储,并实现了基本的创建和输出操作。其中,每个顶点通过一个边链表来存储与之相邻接的顶点。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言数据结构与算法之链表(一) - Python技术站

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

相关文章

  • C语言近万字为你讲透树与二叉树

    C语言近万字为你讲透树与二叉树 什么是树? 树是一种用来组织数据的非线性数据结构,它由一个根节点和若干个子节点组成,并且每个节点可能有若干个子节点。 什么是二叉树? 二叉树是一种特殊的树,它的每个节点最多只有两个子节点,并且分别称为左子节点和右子节点,左子节点在二叉树中永远排在右子节点的前面。 二叉树的遍历方式 二叉树的遍历方式有三种: 前序遍历(preor…

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

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

    数据结构 2023年5月17日
    00
  • C语言编程数据结构线性表之顺序表和链表原理分析

    C语言编程数据结构线性表之顺序表和链表原理分析 线性表的定义 线性表是由n(n>=0)个数据元素a1、a2、…、an组成的有限序列,通常用(a1, a2, …, an)表示,其中a1是线性表的第一个元素,an是线性表的最后一个元素。线性表中的元素个数n称为线性表的长度,当n=0时,线性表为空表。 线性表的分类 根据存储结构,线性表可以分为顺序表…

    数据结构 2023年5月17日
    00
  • Java数据结构之插入排序与希尔排序

    Java数据结构之插入排序与希尔排序 插入排序 插入排序是一种简单而有效的排序算法。它的基本思想是将一个元素插入已经排好序的部分中。插入排序的过程可以用以下伪代码表示: for i=1 to length-1 j = i while j > 0 and array[j-1] > array[j] swap array[j] and array[j…

    数据结构 2023年5月17日
    00
  • 字符串算法–$\mathcal{KMP,Trie}$树

    \(\mathcal{KMP算法}\) 实际上,完全没必要从\(S\)的每一个字符开始,暴力穷举每一种情况,\(Knuth、Morris\)和\(Pratt\)对该算法进行了改进,称为 \(KMP\) 算法。 而\(KMP\)的精髓在于,对于每次失配之后,我都不会从头重新开始枚举,而是根据我已经得知的数据,从某个特定的位置开始匹配;而对于模式串的每一位,都有…

    算法与数据结构 2023年4月18日
    00
  • C++实现LeetCode(211.添加和查找单词-数据结构设计)

    首先,我们需要先了解一下题目的要求和限制,以及具体的解题思路。 题目描述 设计一个支持添加、删除、查找单词的数据结构。添加和删除单词的操作需要支持普通词和通配符’.’。查找单词只支持普通词,不支持通配符’.’。所有单词都是非空的。 解题思路 这道题可以使用前缀树(Trie树)来实现。 首先,我们需要定义一个单词类,它包含两个字段:单词字符串和单词长度。然后,…

    数据结构 2023年5月17日
    00
  • Go语言数据结构之希尔排序示例详解

    Go语言数据结构之希尔排序示例详解 希尔排序简介 希尔排序,也称为缩小增量排序,是插入排序的一种更高效的改进版本;希尔排序是非稳定排序算法。 希尔排序的基本思想是已距离进行“减半”的插入排序;先将整个待排序的记录序列分割成若干个子序列,分别进行直接插入排序,待各子序列中的记录基本有序时,再将子序列合并为整体有序序列。 希尔排序的过程 从上面的简介中我们可以得…

    数据结构 2023年5月17日
    00
  • C语言数据结构之图书借阅系统

    C语言数据结构之图书借阅系统是一款基于C语言的软件,主要用于管理图书馆的借阅信息,并提供图书查询、借阅、归还等功能。本文将介绍图书借阅系统的完整攻略。 设计思路 图书借阅系统的设计主要包括三个阶段:系统设计、数据结构设计和用户接口设计。 系统设计 系统设计是构建整个系统的重要阶段,需要确定系统的功能需求、模块划分和流程控制。本系统的主要功能包括: 图书查询:…

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