C语言一篇精通链表的各种操作

C 语言精通链表操作攻略

简介

链表是一种常用的数据结构,它相对于数组等数据结构来说,具有更高的灵活性和增删操作的效率。在 C 语言中,我们可以用指针来实现链表,这也是指针与动态内存分配的重要应用之一。本文将提供在 C 语言中精通链表操作的攻略,包括链表的创建、添加、删除、搜索、遍历等常用操作。

基本结构体定义

链表一般由结构体和指针构成,下面我们定义一个链表结构体节点:

struct ListNode {
    int val;
    struct ListNode* next;
};

其中,val 表示节点中存储的值,next 表示下一个节点的指针。为了方便后续的实现,我们还定义一个指向链表开头的指针:

typedef struct ListNode* LinkedList;

创建链表

首先,我们需要创建一个空链表。因为链表的第一个节点没有前驱节点,所以需要特别处理一下:

LinkedList createLinkedList() {
    LinkedList head = (LinkedList)malloc(sizeof(struct ListNode));
    head->next = NULL;
    return head;
}

手动添加节点

接下来,我们手动向链表中添加几个节点:

LinkedList head = createLinkedList();
LinkedList p = (LinkedList)malloc(sizeof(struct ListNode));
p->val = 1;
p->next = NULL;
head->next = p;

LinkedList q = (LinkedList)malloc(sizeof(struct ListNode));
q->val = 2;
q->next = NULL;
p->next = q;

这样就创建了一个有两个节点的链表。

添加节点

在链表结尾处添加一个节点

为了在链表结尾处添加一个节点,我们需要先找到链表的最后一个节点,即 next 指针为NULL的节点。然后在此节点之后,添加新的节点即可:

void addAtTail(LinkedList head, int val) {
    // 找到最后一个节点
    LinkedList p = head;
    while (p->next) {
        p = p->next;
    }

    // 在最后一个节点之后添加新的节点
    LinkedList newNode = (LinkedList)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = NULL;
    p->next = newNode;
}

在链表头部添加一个节点

在链表头部添加节点与在链表尾部添加节点相似,只是我们需要让新节点指向原来的头节点,然后将新节点作为新的头节点即可:

void addAtHead(LinkedList head, int val) {
    LinkedList newNode = (LinkedList)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = head->next;
    head->next = newNode;
}

在链表中间添加一个节点

为了在链表的中间位置添加一个节点,我们需要先找到要插入的位置,移动指针即可:

void addAtIndex(LinkedList head, int index, int val) {
    // 链表头结点为第0个元素
    if (index <= 0) {
        addAtHead(head, val);
        return;
    }

    // 找到要插入节点的前驱节点
    LinkedList p = head;
    for (int i = 0; i < index - 1; i++) {
        p = p->next;
        if (p == NULL) {
            return; // index 超出链表范围
        }
    }

    LinkedList newNode = (LinkedList)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = p->next;
    p->next = newNode;
}

删除节点

删除指定节点

为了删除指定的节点,我们需要先找到这个节点的前驱节点,然后将前驱节点的 next 指针,指向要删除节点的后继节点即可:

void deleteNode(LinkedList head, int val) {
    LinkedList p = head;
    while (p->next != NULL && p->next->val != val) {
        p = p->next;
    }
    if (p->next == NULL) {
        return; // 没有节点包含val值
    }

    LinkedList temp = p->next;
    p->next = temp->next;
    free(temp);
}

删除指定下标的节点

为了删除指定下标的节点,我们需要先找到要删除节点的前驱节点:

void deleteAtIndex(LinkedList head, int index) {
    // 链表头结点为第0个元素
    if (index < 0) {
        return;
    }

    LinkedList p = head;
    for (int i = 0; i < index; i++) {
        p = p->next;
        if (p == NULL) {
            return; // index 超出链表范围
        }
    }

    LinkedList temp = p->next;
    if (temp == NULL) {
        return; // index 超出链表范围
    }

    p->next = temp->next;
    free(temp);
}

搜索节点

为了搜索指定值的节点,我们只需要遍历整个链表,直到找到一个值为指定值,或者链表遍历结束:

LinkedList searchLinkedList(LinkedList head, int val) {
    LinkedList p = head->next;
    while (p != NULL && p->val != val) {
        p = p->next;
    }
    return p;
}

遍历节点

遍历节点是链表的最常见操作之一,我们可以用循环来遍历整个链表:

void traverseLinkedList(LinkedList head) {
    LinkedList p = head->next;
    while (p != NULL) {
        printf("%d ", p->val);
        p = p->next;
    }
}

示例运行

下面是这些链表操作的完整代码和使用示例:

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

struct ListNode {
    int val;
    struct ListNode* next;
};

typedef struct ListNode* LinkedList;

LinkedList createLinkedList() {
    LinkedList head = (LinkedList)malloc(sizeof(struct ListNode));
    head->next = NULL;
    return head;
}

void addAtTail(LinkedList head, int val) {
    LinkedList p = head;
    while (p->next) {
        p = p->next;
    }

    LinkedList newNode = (LinkedList)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = NULL;
    p->next = newNode;
}

void addAtHead(LinkedList head, int val) {
    LinkedList newNode = (LinkedList)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = head->next;
    head->next = newNode;
}

void addAtIndex(LinkedList head, int index, int val) {
    if (index <= 0) {
        addAtHead(head, val);
        return;
    }

    LinkedList p = head;
    for (int i = 0; i < index - 1; i++) {
        p = p->next;
        if (p == NULL) {
            return;
        }
    }

    LinkedList newNode = (LinkedList)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = p->next;
    p->next = newNode;
}

void deleteNode(LinkedList head, int val) {
    LinkedList p = head;
    while (p->next != NULL && p->next->val != val) {
        p = p->next;
    }
    if (p->next == NULL) {
        return;
    }

    LinkedList temp = p->next;
    p->next = temp->next;
    free(temp);
}

void deleteAtIndex(LinkedList head, int index) {
    if (index < 0) {
        return;
    }

    LinkedList p = head;
    for (int i = 0; i < index; i++) {
        p = p->next;
        if (p == NULL) {
            return;
        }
    }

    LinkedList temp = p->next;
    if (temp == NULL) {
        return;
    }

    p->next = temp->next;
    free(temp);
}

LinkedList searchLinkedList(LinkedList head, int val) {
    LinkedList p = head->next;
    while (p != NULL && p->val != val) {
        p = p->next;
    }
    return p;
}

void traverseLinkedList(LinkedList head) {
    LinkedList p = head->next;
    while (p != NULL) {
        printf("%d ", p->val);
        p = p->next;
    }
}

int main() {
    LinkedList head = createLinkedList();
    addAtHead(head, 5);
    addAtTail(head, 6);
    addAtIndex(head, 1, 4);
    deleteAtIndex(head, 1);
    LinkedList p = searchLinkedList(head, 5);
    printf("5 是否在链表中:");
    if (p) {
        printf("是");
    } else {
        printf("否");
    }
    printf("\n链表:");
    traverseLinkedList(head);
    printf("\n");

    return 0;
}

输出结果为:5 是否在链表中:是 链表:5 6

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言一篇精通链表的各种操作 - Python技术站

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

相关文章

  • C语言数据结构之vector底层实现机制解析

    C语言数据结构之vector底层实现机制解析 什么是vector? vector是C++标准库中的一种容器,可以动态调整大小,用于存储数据。 vector的底层实现机制 vector实际上是通过数组实现的,当需要添加元素时,如果当前数组已满,就会重新创建一个更大的数组,并将原数组中的元素复制到新数组中。这样,内存空间得到了增加,同时操作后的元素仍然是顺序存储…

    数据结构 2023年5月17日
    00
  • Halcon学习教程(一) 之提取十字线中心 图像分割

      原文作者:aircraft   原文链接:https://www.cnblogs.com/DOMLX/p/17266405.html      废话不多说,因为毕业后工作原因比较忙,好久没更新博客了,直接上图。。。     上图有个十字线,我们要提取出十字线的中心(Hhhh这个线是我随手画的 没画直!!) 第一步:肯定是读取图像进行灰度提取处理啦。   …

    算法与数据结构 2023年4月18日
    00
  • C语言数据结构顺序表中的增删改(头插头删)教程示例详解

    C语言数据结构顺序表中的增删改(头插头删)教程示例详解 什么是顺序表? 顺序表是一种用数组实现的线性表,所有元素存储在一块连续的存储区中。顺序表的操作包括插入、删除、查找等。 常用的顺序表操作 增加元素 删除元素 修改元素 查找元素 以下以头插和头删为例,讲解如何在C语言数据结构顺序表中实现这些操作。 头插操作 头插的实现首先需要考虑插入位置的下标,由于是头…

    数据结构 2023年5月17日
    00
  • 设要采用CRC编码传送的数据信息x=1001,当生成多项式为G(x)=1101时,请写出它的循环校验码。若接收方收到的数据信息x’ =1101,说明如何定位错误并纠正错误

    题目:设要采用CRC编码传送的数据信息x=1001,当生成多项式为G(x)=1101时,请写出它的循环校验码。若接收方收到的数据信息x’ =1101,说明如何定位错误并纠正错误 根据题目描述,需要采用CRC编码对数据信息x=1001进行编码,生成多项式为G(x)=1101。下面是计算循环冗余校验码的步骤:1.首先将数据信息x乘以x的次数,使得它的位数与G(x…

    算法与数据结构 2023年4月18日
    00
  • redis数据结构之intset的实例详解

    Redis数据结构之intset的实例详解 介绍 Redis是一个高性能的key-value存储系统,支持多种数据结构。其中,intset是Redis内置的一种特殊的数据结构,它可以高效地存储整型数据。 本篇文章将介绍intset的基本特性、底层实现以及相关用例,以便读者能够更好地了解该数据结构在Redis中的应用。 intset的基本特性 intset是一…

    数据结构 2023年5月17日
    00
  • C语言深入浅出解析二叉树

    C语言深入浅出解析二叉树攻略 什么是二叉树 二叉树是一种树形数据结构,其每个节点最多只有两个子节点,分别称为其左子节点和右子节点。一般采用链式存储方式来实现二叉树,也可以使用数组来存储。 二叉树的遍历 二叉树的遍历分为三种方式:前序遍历,中序遍历和后序遍历。 前序遍历 前序遍历的顺序是先遍历根节点,然后遍历左子树,最后遍历右子树。可以使用递归或栈来实现。 v…

    数据结构 2023年5月17日
    00
  • 一些常见的字符串匹配算法

    作者:京东零售 李文涛 一、简介 1.1 Background 字符串匹配在文本处理的广泛领域中是一个非常重要的主题。字符串匹配包括在文本中找到一个,或者更一般地说,所有字符串(通常来讲称其为模式)的出现。该模式表示为p=p[0..m-1];它的长度等于m。文本表示为t=t[0..n-1],它的长度等于n。两个字符串都建立在一个有限的字符集上。 一个比较常见…

    算法与数据结构 2023年4月25日
    00
  • PAT甲级真题1020.树的遍历

    翻译和代码思路:Acwing 一个二叉树,树中每个节点的权值互不相同。 现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。 输入格式 第一行包含整数 N,表示二叉树的节点数。 第二行包含 N个整数,表示二叉树的后序遍历。 第三行包含 N 个整数,表示二叉树的中序遍历。 输出格式 输出一行 N个整数,表示二叉树的层序遍历。 数据范围 1<=N<…

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