数据结构——链表(精简易懂版)

news2024/11/23 22:17:14

文章目录

  • 链表概述
  • 链表的实现
    • 链表的节点(单个积木)
    • 链表的构建
      • 直接构建
      • 尾插法构建
      • 头插法构建
    • 链表的插入
  • 总结

链表概述

1,链表(Linked List)是一种常见的数据结构,用于存储一系列元素。它由一系列节点(Node)组成,每个节点包含两部分:数据域(存储数据的部分)和指针域(指向下一个节点的指针)。链表的特点是节点在内存中的存储位置不必是连续的,而是通过指针来相互连接。

具体可以想象成一个很多个积木连接的蛇

画个图大概如下(单链表):
在这里插入图片描述

链表可以分为单向链表和双向链表两种常见形式

单向链表(Singly Linked List):每个节点包含一个指针,指向下一个节点。单向链表只能从头节点开始顺序访问,无法从尾节点快速访问到头节点。(就是上面那种)

双向链表(Doubly Linked List):每个节点包含两个指针,分别指向前一个节点和后一个节点。双向链表可以从任意节点开始向前或向后遍历,相比单向链表具有更灵活的操作。

双向循环链表如下:
在这里插入图片描述

链表的优点是插入和删除操作效率高,时间复杂度为O(1),而查找操作效率相对较低,最坏情况下为O(n)。链表适用于需要频繁插入和删除操作,而对查找操作要求不高的场景。

常见的链表操作包括:插入节点、删除节点、查找节点、反转链表、合并链表等。链表在计算机科学中应用广泛,常见于实现各种数据结构和算法,如栈、队列、图等。

链表的实现

链表的节点(单个积木)

1,链表的节点是自定义的一个结构体,数据域存的东西可以自定义,可以是数字链表,也可以是字符链表等,非常灵活。

struct Node
{
	int val;
	struct Node* next;   //指针域,指向下一个节点
	//双向链表就在加一个指针
	struct Node* Pre;
};

链表的构建

直接构建

1,最直接的就是一个个链表节点直接链接,最后一个节点的指针要置空,方便判断是否到达链表的结尾,如下:

typedef struct Node
{
	int val;
	struct Node* next;
}node;

int main()
{
	node* n1 = (node*)malloc(sizeof(node));
	n1->val = 1;
	node* n2 = (node*)malloc(sizeof(node));
	n2->val = 2;
	n1->next = n2;
	node* n3 = (node*)malloc(sizeof(node));
	n3->val = 3;
	n2->next = n3;
	n3->next = NULL;

	return 0;
}

尾插法构建

1,尾插即字面意思,构建一个尾指针指向链表最后一个节点,然后创建一个新节点插到尾巴后面,然后更新尾节点为新的尾。

图示

在这里插入图片描述

插入后

在这里插入图片描述

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

// 定义链表节点结构体
struct Node {
    int data;
    struct Node* next;
};

// 尾插法构建链表
struct Node* createLinkedList(int arr[], int n) {
    struct Node *head = NULL;
    struct Node *tail = NULL;

    for (int i = 0; i < n; i++) {
        // 创建新节点
        struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
        if (newNode == NULL) {
            printf("Memory allocation failed.\n");
            exit(1);
        }
        newNode->data = arr[i];
        newNode->next = NULL;

        // 如果是第一个节点,则设置为头节点
        if (head == NULL) {
            head = newNode;
            tail = newNode;
        } else {
            // 否则将新节点插入到尾部
            tail->next = newNode;
            tail = newNode;
        }
    }

    return head;
}

// 打印链表
void printLinkedList(struct Node* head) {
    while (head != NULL) {
        printf("%d ", head->data);
        head = head->next;
    }
    printf("\n");
}

// 主函数
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr) / sizeof(arr[0]);

    // 使用尾插法构建链表
    struct Node *head = createLinkedList(arr, n);

    // 打印链表
    printf("Linked List: ");
    printLinkedList(head);

    return 0;
}

头插法构建

1,头插法也是一种常见的方法,用于构建链表。与尾插法不同,头插法是在链表的头部插入新的节点,使新节点成为链表的新头节点。

原图

在这里插入图片描述
变化

在这里插入图片描述

最终

在这里插入图片描述

下面是使用头插法构建链表的C语言示例

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

// 定义链表节点结构体
struct Node {
    int data;
    struct Node* next;
};

// 头插法构建链表
struct Node* createLinkedList(int arr[], int n) {
    struct Node *head = NULL;

    for (int i = 0; i < n; i++) {
        // 创建新节点
        struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
        if (newNode == NULL) {
            printf("Memory allocation failed.\n");
            exit(1);
        }
        newNode->data = arr[i];
        
        // 将新节点插入到头部
        newNode->next = head;
        head = newNode;
    }

    return head;
}

// 打印链表
void printLinkedList(struct Node* head) {
    while (head != NULL) {
        printf("%d ", head->data);
        head = head->next;
    }
    printf("\n");
}

// 主函数
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr) / sizeof(arr[0]);

    // 使用头插法构建链表
    struct Node *head = createLinkedList(arr, n);

    // 打印链表
    printf("Linked List: ");
    printLinkedList(head);

    return 0;
}

链表的插入

1,链表的插入操作可以在指定位置或者指定节点之后进行。下面分别介绍两种情况的链表插入操作:

  • 在指定位置插入节点:这种情况下,需要知道要插入节点的位置,通常使用节点的索引或者位置来指定。插入操作涉及到节点的连接,需要将新节点插入到指定位置,同时调整前一个节点和后一个节点的连接关系。

  • 在指定节点之后插入节点:在这种情况下,需要先找到指定节点,然后在其后插入新节点。这个操作需要确保找到指定节点,然后调整节点的连接关系。

原(插入到1之后)

在这里插入图片描述

变化

换成代码就是

node* newnode;//(代表4节点)
node* cur;//(代表1节点)
newnode->next = cur->next;

在这里插入图片描述

最后

代码实现

cur->next = newnode;

在这里插入图片描述

下面是C语言示例代码,分别演示了在指定位置和指定节点之后进行插入操作

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

// 定义链表节点结构体
struct Node {
    int data;
    struct Node* next;
};

// 在指定位置插入节点
void insertAtIndex(struct Node** headRef, int index, int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    if (newNode == NULL) {
        printf("Memory allocation failed.\n");
        exit(1);
    }
    newNode->data = data;
    
    // 如果插入位置是头节点之前,则直接将新节点作为头节点
    if (index == 0) {
        newNode->next = *headRef;
        *headRef = newNode;
        return;
    }

    // 找到插入位置的前一个节点
    struct Node* current = *headRef;
    for (int i = 0; i < index - 1 && current != NULL; i++) {
        current = current->next;
    }

    // 如果插入位置超出链表长度,则插入失败
    if (current == NULL) {
        printf("Index out of range.\n");
        return;
    }

    // 插入新节点
    newNode->next = current->next;
    current->next = newNode;
}

// 在指定节点之后插入节点
void insertAfterNode(struct Node* prevNode, int data) {
    if (prevNode == NULL) {
        printf("Previous node cannot be NULL.\n");
        return;
    }

    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    if (newNode == NULL) {
        printf("Memory allocation failed.\n");
        exit(1);
    }
    newNode->data = data;

    // 插入新节点
    newNode->next = prevNode->next;
    prevNode->next = newNode;
}

// 打印链表
void printLinkedList(struct Node* head) {
    while (head != NULL) {
        printf("%d ", head->data);
        head = head->next;
    }
    printf("\n");
}

// 主函数
int main() {
    struct Node* head = NULL;

    // 插入节点示例
    insertAtIndex(&head, 0, 1); // 在头部插入节点
    insertAtIndex(&head, 1, 3); // 在索引1位置插入节点
    insertAtIndex(&head, 1, 2); // 在索引1位置插入节点
    insertAfterNode(head->next, 4); // 在节点3之后插入节点

    // 打印链表
    printf("Linked List: ");
    printLinkedList(head);

    return 0;
}

总结

其他操作的本质上也是差不多的,都是建立新节点,修改指针指向的问题,通过画图可以更好理解的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1649483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python实现的信号合成分析系统(DSP)

python实现的信号合成分析系统(DSP) 流程 1、在QT界面上设置好信号频率,采样频率,采样点数 2、使用np构建sin函数 3、使用matplotlib画出 4、分别分析合成信号的FFT频域信息1、效果图 2、示例代码 def btn_com_clicked(self):# 信号合成分析Fs = self.com_fs_edit_value #…

【嵌入式DIY实例】-基于GSM的远程灌溉系统

基于GSM的远程灌溉系统 文章目录 基于GSM的远程灌溉系统1、硬件准备与接线2、软件准备3、代码实现本文将详细介绍如何搭建通过使用手机实现对灌溉系统的远程控制。该系统利用全球移动通信系统(GSM)技术在灌溉系统和移动电话之间建立通信。该系统建立在流行的开源微控制器平台…

重庆大足某厂不锈钢管件酸洗钝化-智渍洁

简报&#xff1a;重庆大足某厂不锈钢管件酸洗钝化 重庆大足某厂不锈钢管件酸洗钝化 - 重庆智渍洁环保科技有限公司简报&#xff1a;重庆大足某厂不锈钢管件酸洗钝化https://www.zhizijie.com/hl/zixun/gongsi/237.html

【Linux网络】网络文件共享

目录 一、存储类型 二、FTP文件传输协议 2.1 FTP工作原理 2.2 FTP用户类型 2.3 FTP软件使用 2.3.1 服务端软件vsftpd 2.3.2 客户端软件ftp 2.4 FTP的应用 2.4.1 修改端口号 2.4.2 匿名用户的权限 2.4.3 传输速率 三、NFS 3.1 工作原理 3.2 NFS软件介绍 3.3 NFS配…

企业加密软件有哪些:企业加密软件排行榜|常用分享汇集

在当前的数字化时代&#xff0c;数据的安全性成为了企业运营中至关重要的一环。因此&#xff0c;企业加密软件的需求也日益增长。在这个竞争激烈的市场中&#xff0c;各大加密软件厂商纷纷推出自己的产品&#xff0c;以满足企业的不同需求。 首先是Ping32加密软件。Ping32文件加…

Flutter实战记录-协作开发遇到的问题

一.前言 Android项目使用了混合架构&#xff0c;部分模块使用Flutter进行开发。在电脑A上开发的项目提交到git仓库&#xff0c;电脑B拉取后进行操作&#xff0c;遇到两个问题&#xff0c;特此做一下记录&#xff1b; 二.问题A Settings file ‘D:\xxx\settings.gradle’ line…

Linux初识

1.操作系统的那点事 &#xff08;1&#xff09;结论&#xff1a;操作系统是作软硬件管理的软件&#xff1b; &#xff08;2&#xff09;计算机是操作系统&#xff0c;设备驱动&#xff0c;硬件三个相互结合发挥作用的&#xff0c;操作系统是用来管理硬件的&#xff0c;常见的…

PMBOK第七版,通往项目管理的新地图|分析2024软考光环PMP第六版培训课程

目录 文明福利 历次升级分析 2PMBOK第七版解读 1、和第六版保持一致&#xff1a;由知识体系指南和项目管理标准2部分构成。 2、区别于第六版的结构性颠覆&#xff1a;12原则、8大绩效域取代5大过程组、10大知识领域。 3PMBOK第七版VS第六版 4PMBOK第七版 就是带领我们寻找…

RabbitMQ是如何保证消息可靠性的?——Java全栈知识(16)

RabbitMQ 的消息不可靠也就是 RabbitMQ 消息丢失只会发生在以下几个方面&#xff1a; 生产者发送消息到 MQ 或者 Exchange 过程中丢失。Exchange 中的消息发送到 MQ 中丢失。消息在 MQ 或者 Exchange 中服务器宕机导致消息丢失。消息被消费者消费的过程中丢失。 大致就分为生…

数仓分层——ODS、DW、ADS

一、什么是数仓分层 数据仓库分层是一种组织和管理数据仓库的结构化方法&#xff0c;它将数据仓库划分为不同的层次或级别&#xff0c;每个层次具有特定的功能和目的。这种分层方法有助于管理数据仓库中的数据流程、数据处理和数据访问&#xff0c;并提供一种清晰的结构来支持…

51-49 CVPR 2024 | OMG:通过混合控制器实现开放词汇的运动生成

23年12月&#xff0c;腾讯联合上海科技大学联合发布OMG&#xff1a;Towards Open-vocabulary Motion Generation via Mixture of Controllers&#xff0c;从零样本开放词汇文本提示中生成引人注目的动作。这款控制器关键思想是将 pretrain-then-finetune 范式运用到文本-运动的…

self-attention 的 CUDA 实现及优化 (上)

self-attention 的 CUDA 实现及优化 (上) 导 读 self-attention 是 Transformer 中最关键、最复杂的部分&#xff0c;也是 Transformer 优化的核心环节。理解 self-attention &#xff0c;对于深入理解 Transformer 具有关键作用&#xff0c;本篇主要就围绕 self-attention 展…

Linux进程通信-信号

信号概念 信号是 Linux 进程间通信的最古老的方式之一&#xff0c;是事件发生时对进程的通知机制&#xff0c;有时也称之为软件中断&#xff0c;它是在软件层次上对中断机制的一种模拟&#xff0c;是一种异步通信的方式。信号 可以导致一个正在运行的进程被另一个正在运行的异…

解决Pyppeteer下载chromium慢或者失败的问题[INFO] Starting Chromium download.

文章目录 1.进入网址2.选择上面对应自己系统的文件夹进去3. 然后找到自己的python环境中的site-packages中pyppeteer中的chromium_downloader.py文件并打开 在首次使用Pyppeteer时需要下载chromium 1.进入网址 https://registry.npmmirror.com/binary.html?pathchromium-bro…

H5页面跳转去微信的客服页面不需要添加客服就可以直接聊天

我并没有添加客服的微信。但是页面直接跳转了进来。可以直接聊天。 首先你公司要有个企业微信。然后登陆公司的企业微信。搜索框找到应用里面的企业客服 然后你就看到了客服账号的接入连接。代码上直接写个 <div οnclick"window.location.href接入链接粘贴到这里&q…

UDP广播

1、UDP广播 1.1、广播的概念 广播&#xff1a;由一台主机向该主机所在子网内的所有主机发送数据的方式 例如 &#xff1a;192.168.3.103主机发送广播信息&#xff0c;则192.168.3.1~192.168.3.254所有主机都可以接收到数据 广播只能用UDP或原始IP实现&#xff0c;不能用TCP…

DirClass

DirClass 通过分析&#xff0c;发现当接收到DirClass远控指令后&#xff0c;样本将返回指定目录的目录信息&#xff0c;返回数据中的远控指令为0x2。 相关代码截图如下&#xff1a; DelDir 通过分析&#xff0c;发现当接收到DelDir远控指令后&#xff0c;样本将删除指定目录…

46. UE5 RPG 实现角色死亡效果

在上一篇文章中&#xff0c;我们实现了敌人受到攻击后会播放受击动画&#xff0c;并且还给角色设置了受击标签。并在角色受击时&#xff0c;在角色身上挂上受击标签&#xff0c;在c里&#xff0c;如果挂载了此标签&#xff0c;速度将降为0 。 受击有了&#xff0c;接下来我们将…

机器学习之基于Jupyter多种混合模型的糖尿病预测

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着现代生活方式的改变&#xff0c;糖尿病的患病率在全球范围内呈现上升趋势。糖尿病是一种慢性代谢…

【R语言从0到精通】-4-回归建模

通过之前的文章&#xff0c;我们已经基本掌握了R语言的基本使用方法&#xff0c;那从本次教程开始&#xff0c;我们开始聚焦如何使用R语言进行回归建模。 4.1 回归简介 回归分析是一种统计学方法&#xff0c;用于研究两个或多个变量之间的相互关系和依赖程度。它可以帮助我们了…