单向链表

什么是单向链表

链表是一种物理储存单元上非连续、非顺序的储存结构。它由一系列结点(链表中每一个元素称为结点)组成,结点可动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

其实可以形象的认为,单向链表就好像一列火车。

链表的节点就好像火车的每一节车厢,而链表节点的数据域就对应车厢中存放的货物,链表节点的指针域充当连接各节车厢的锁链。

为什么需要单向链表

我们知道如何用数组去存储数据。但是在实际应用的过程中,我们有时会出现一些不可避免的问题:

问题1

假如定义了并初始化一个数组 a[5] = {1,2,3,4,5}, 此时要在加入新的元素,只能开辟一个更大的数组。

问题2

假如定义一个数组 a[100000], 但是只存储了几个元素,会导致浪费空间。

而单向链表这样链式的动态存储的数据结构,恰恰解决了使用数组时若数组已满无法插入新的数据的问题,以及使用数组时可能浪费大量存储空间的问题。


单向链表的创建

单个链表节点的构成

首先看一下单向链表节点的结构体,我们把单个节点看作是一节车厢,数据域就好像车厢存储的货物,指针域就好像锁链。

typedef int Elemtype;        //数据类型

typedef struct Node {

    Elemtype data;           //结构体数据域
struct Node *next; //结构体指针域 } Linklist;

链表的初始化创建

链表一般需要一个头节点,这个头结点一般是不带数据的。可以看作是驱动火车前进的火车头。

//链表的初始化
Linklist* Initial_linklist(){
//向系统申请内存
Linklist *head = (Linklist *)malloc(sizeof(Linklist));
head->next = NULL;
return head;
}

链表初始数据插入

单向链表的初始数据可以是任意个,此处采用的是尾插法,后面会具体解释这个方法。

//创建初始链表  采用尾插法
void Create_linklist(Linklist *head, int n) {
Linklist *node, *end; //普通节点 尾节点
end = head; //当链表为空时 头尾指向同一个节点
printf("创建链表输入 %d 个元素:", n);
for (int i = 0; i < n; i++) { //n为插入普通节点的个数
node = (Linklist *)malloc(sizeof(Linklist));
scanf("%d", &node->data);
end->next = node; //当前end的next指向了新节点node
end = node; //end往后移,此时新的节点变成尾节点
}
end->next = NULL; //end最后置NULL
}

单向链表的基本操作

头插法 插入单个数据

头插法图解

头插法代码

//头插法 插入单个数据
void Insert_Front(Linklist *head, int data) {
Linklist *node = (Linklist *)malloc(sizeof(Linklist));
node->next = NULL;
node->data = data; node->next = head->next; //新节点node的next指向当前head的next
head->next = node; //head的next重新指向新节点node
}

尾插法 插入单个数据

尾插法图解

尾插法代码

//尾插法 插入单个数据
void Insert_Back(Linklist *head, int data) {
Linklist *node = (Linklist *)malloc(sizeof(Linklist));
node->next = NULL;
node->data = data; Linklist *end = head; //起初end指向头节点
while (end->next != NULL)
end = end->next; //end指针往后移,直到最后一个节点
end->next = node; //当前end的next指向了新节点node
}

指定位置插入节点

分析及解释

指定位置插入单个数据,这里一般设定为在第 k 个带数据的普通节点(除去头节点)位置插入一个新的节点。其实指定位置插入新节点的原理和头插法是类似的,只不过是将第 k 个带数据节点的前一个节点看成头结点一样。因此我们需要找到第 k-1 个带数据节点,假如遍历指针 t 从头节点开始,那么算上头节点,需要经历 k-1 次循环找到第 k-1 个带数据节点。值得注意的是,如果k = 1的话,也就是在第一个带数据节点的位置插入新节点,不存在第0个带数据节点,此时其实这个第 k-1 个节点就变成了头节点。那么此时明显和头插法是完全一样的。

图解的话可以参考头插法的图解,是差不多的。

指定位置插入单个数据 代码

//指定位置插入单个节点
void Insert_position(Linklist *head, int k) {
//k表示在第k个普通节点的位置插入新节点
Linklist *t = head, *in; //t为遍历指针
//in是要插入的新节点
for (int i = 0; i < k - 1; i++)
t = t->next; if (t != NULL) {
in = (Linklist *)malloc(sizeof(Linklist));
in->next = NULL;
printf("在第 %d 个节点处插入新节点的数据: ", k);
scanf("%d", &in->data);
in->next = t->next; //插入节点in的next指向当前第k-1个普通节点的next指向的节点
t->next = in; //第k-1个普通节点的next重新指向插入的节点in //原理和头插法类似 就好像把第k-1个普通节点t看做是头节点
} else {
puts("节点不存在");
}
}

遍历输出链表数据

分析及解释

我们需要用指针去访问链表中的数据。定义了一个指针t,来对链表的每一个存有数据的节点进行访问并读取数据,直到当前节点为NULL,停止遍历。

通俗地来说,就好比一个卸货员工,他挨个从头到尾取下每一节火车车厢的货物,直到最后到达尾部车厢的时候,他便不再取下货物。

打印链表代码

//打印链表
void Show_linklist(Linklist *head) {
Linklist *t = head->next; //t为遍历指针 访问每个节点数据 if (t == NULL)
puts("链表为空"); while (t != NULL) {
printf("%d ", t->data);
t = t->next;
}
printf("\n\n");
}

指定位置删除节点

分析及解释

删除单个节点很容易,只需要找到要删除的节点的前一个节点,然后直接跨过中间这个删除的节点指向删除节点的下一个节点就可以了。

指定位置删除节点 图解

指定位置删除节点 代码

void Delete_position(Linklist *head, int k) { //k表示要删除第k个节点
Linklist *t = head, *del = NULL; //t为遍历指针
int i = 0;
while (i < k - 1 && t != NULL) {
t = t->next; //t指向删除的第k个的前一个节点
i++;
}
if (t != NULL) {
del = t->next;
t->next = del->next;
free(del);
} else {
puts("节点不存在");
}
}

其他基本操作

其他基本操作代码

//查找元素返回节点位置
void Find_Element(Linklist *head, int x) {
Linklist *t = head->next;
while (t != NULL) {
int sub = 1;
if (t->data == x)
printf("元素 %d 的位置为: %d \n", x, sub); t = t->next;
sub++;
}
if (t == NULL)
puts("元素不存在");
} //读取指定节点位置元素
void Read_position(Linklist *head, int k) {
Linklist *t = head->next;
for (int i = 0; i < k; i++)
t = t->next;
printf("第 %d 个节点位置的数据为: %d \n", k, t->data);
} //计算链表的长度
void List_length(Linklist *head){
Linklist *t = head->next;
int len = 0;
while(t){
len++;
t = t->next;
}
printf("链表的长度为: %d \n", len);
} //清空链表
void Clear_linklist(Linklist *head) {
Linklist *t;
while (head->next != NULL) {
t = head->next;
head->next = t->next;
free(t);
}
} //判断是否为空
bool IsEmpty(Linklist *head){
return head->next == NULL;
}

完整程序

完整程序源代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef int Elemtype; //数据类型 typedef struct Node { Elemtype data; //结构体数据域
struct Node *next; //结构体指针域 } Linklist; //链表的初始化
Linklist* Initial_linklist(){
//向系统申请内存
Linklist *head = (Linklist *)malloc(sizeof(Linklist));
head->next = NULL;
return head;
} //创建初始链表 采用尾插法
void Create_linklist(Linklist *head, int n) { //头节点(不带数据)
Linklist *node, *end; //普通节点 尾节点
end = head; //当链表为空时 头尾指向同一个节点
printf("创建链表输入 %d 个元素:", n);
for (int i = 0; i < n; i++) { //n为插入普通节点的个数
node = (Linklist *)malloc(sizeof(Linklist));
scanf("%d", &node->data);
end->next = node; //当前end的next指向了新节点node
end = node; //end往后移,此时新的节点变成尾节点
}
end->next = NULL; //end最后置NULL
} //打印链表
void Show_linklist(Linklist *head) {
Linklist *t = head->next; //t为遍历指针 访问每个节点数据
if (t == NULL)
puts("链表为空"); while (t != NULL) {
printf("%d ", t->data);
t = t->next;
}
printf("\n\n");
} //头插法 插入单个数据
void Insert_Front(Linklist *head, int data) {
Linklist *node = (Linklist *)malloc(sizeof(Linklist));
node->next = NULL;
node->data = data; node->next = head->next; //新节点node的next指向当前head的next
head->next = node; //head的next重新指向新节点node
} //尾插法 插入单个数据
void Insert_Back(Linklist *head, int data) {
Linklist *node = (Linklist *)malloc(sizeof(Linklist));
node->next = NULL;
node->data = data; Linklist *end = head; //起初end指向头节点
while (end->next != NULL)
end = end->next; //end指针往后移,直到最后一个节点
end->next = node; //当前end的next指向了新节点node
} //指定位置插入单个数据
void Insert_position(Linklist *head, int k) { //k表示在第k个普通节点的位置插入新节点
Linklist *t = head, *in; //t为遍历指针
//in是要插入的新节点
for (int i = 0; i < k - 1; i++)
t = t->next; if (t != NULL) {
in = (Linklist *)malloc(sizeof(Linklist));
in->next = NULL;
printf("在第 %d 个节点处插入新节点的数据: ", k);
scanf("%d", &in->data);
in->next = t->next; //插入节点in的next指向当前第k-1个普通节点的next指向的节点
t->next = in; //第k-1个普通节点的next重新指向插入的节点in //原理和头插法类似 就好像把第k-1个普通节点t看做是头节点
} else {
puts("节点不存在");
}
} //指定位置改变节点的数据
void Change_position(Linklist *head, int n) { //n表示要改变的是第n个普通节点
Linklist *t = head; //t为遍历指针
for (int i = 0; i < n; i++)
t = t->next; //t指向要改变的节点 if (t != NULL) {
printf("修改第 %d 个节点的数据: ", n);
scanf("%d", &t->data);
} else {
puts("节点不存在");
}
} //指定位置删除节点
void Delete_position(Linklist *head, int k) { //k表示要删除第k个节点
Linklist *t = head, *del = NULL; //t为遍历指针
int i = 0;
while (i < k - 1 && t != NULL) {
t = t->next; //t指向删除的第k个的前一个节点
i++;
}
if (t != NULL) {
del = t->next;
t->next = del->next;
free(del);
} else {
puts("节点不存在");
}
} //查找元素返回节点位置
void Find_Element(Linklist *head, int x) {
Linklist *t = head->next;
while (t != NULL) {
int sub = 1;
if (t->data == x)
printf("元素 %d 的位置为: %d \n", x, sub); t = t->next;
sub++;
}
if (t == NULL)
puts("元素不存在");
} //读取指定节点位置元素
void Read_position(Linklist *head, int k) {
Linklist *t = head->next;
for (int i = 0; i < k; i++)
t = t->next;
printf("第 %d 个节点位置的数据为: %d \n", k, t->data);
} //计算链表的长度
void List_length(Linklist *head){
Linklist *t = head->next;
int len = 0;
while(t){
len++;
t = t->next;
}
printf("链表的长度为: %d \n", len);
} //清空链表
void Clear_linklist(Linklist *head) {
Linklist *t;
while (head->next != NULL) {
t = head->next;
head->next = t->next;
free(t);
}
} //判断是否为空
bool IsEmpty(Linklist *head){
return head->next == NULL;
} int main() {
//头指针初始化
Linklist *mylist;
mylist = Initial_linklist(); Create_linklist(mylist, 10);
printf("初始状态链表:\n");
Show_linklist(mylist); Insert_Front(mylist, 30);
Insert_Back(mylist, 30);
printf("链表进行首尾插入数字30后:\n");
Show_linklist(mylist); Insert_position(mylist, 5);
printf("链表进行在第5个节点后插入新节点后:\n");
Show_linklist(mylist); Change_position(mylist, 4);
printf("链表进行改变第4个数据后:\n");
Show_linklist(mylist); Delete_position(mylist, 1);
printf("链表进行删除第1个数据后:\n");
Show_linklist(mylist); Clear_linklist(mylist);
printf("链表进行清空后:\n");
if(IsEmpty(mylist))
puts("链表为空"); return 0;
}

程序运行结果图

[数据结构]单向链表及其基本操作(C语言)的更多相关文章

  1. 数据结构-单向链表 C和C++的实现

    数据结构,一堆数据的存放方式. 今天我们学习数据结构中的 链表: 链表的结构: 链表是一种特殊的数组,它的每个元素称为节点,每个节点包括两个部分: 数据域:存放数据,此部分与数组相同 指针域:存放了下 ...

  2. python数据结构——单向链表

    链表 ( Linked List ) 定义:由许多相同数据类型的数据项按照特定顺序排列而成的线性表. 特点:各个数据在计算机中是随机存放且不连续. 优点:数据的增删改查都很方便,当有新的数据加入的时候 ...

  3. Linux C 数据结构 ->单向链表<-(~千金散尽还复来~)

    之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢 延伸到真正的内核链表~(敢问路在何方?路在脚下~) 1. 简介 链表是Linux 内核中最简单,最普通的数据结 ...

  4. Linux C 数据结构 ->单向链表

    之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢 延伸到真正的内核链表~(敢问路在何方?路在脚下~) 1. 简介 链表是Linux 内核中最简单,最普通的数据结 ...

  5. 数据结构—单链表(类C语言描写叙述)

    单链表 1.链接存储方法 链接方式存储的线性表简称为链表(Linked List). 链表的详细存储表示为: ① 用一组随意的存储单元来存放线性表的结点(这组存储单元既能够是连续的.也能够是不连续的) ...

  6. C语言数据结构-单链表的实现-初始化、销毁、长度、查找、前驱、后继、插入、删除、显示操作

    1.数据结构-单链表的实现-C语言 typedef struct LNode { int data; struct LNode* next; } LNode,*LinkList; //这两者等价.Li ...

  7. Python3玩转单链表——逆转单向链表pythonic版

    [本文出自天外归云的博客园] 链表是由节点构成的,一个指针代表一个方向,如果一个构成链表的节点都只包含一个指针,那么这个链表就是单向链表. 单向链表中的节点不光有代表方向的指针变量,也有值变量.所以我 ...

  8. Python链表的实现与使用(单向链表与双向链表)

    参考[易百教程]用Python实现链表及其功能 """ python链表的基本操作:节点.链表.增删改查 """ import sys cl ...

  9. 数据结构算法C语言实现(五)---2.3重新定义线性链表及其基本操作

    一.简述 ...由于链表在空间的合理利用上和插入.删除时不需要移动等的优点,因此在很多场合下,它是线性表的首选存储结构.然而,它也存在着实现某些基本操作,如求线性表的长度时不如顺序存储结构的缺点:另一 ...

  10. C语言 - 基础数据结构和算法 - 单向链表

    听黑马程序员教程<基础数据结构和算法 (C版本)>,照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友可 ...

随机推荐

  1. 中国数字化是怎么转型成新范式TOP 50的?

    我不大认可"中国数字化转型成新范式TOP 50"的,确切的说,照着"中国数字化转型新范式TOP 50"做转型,大概率失败,对中国企业数字化转型的帮助甚微 ,尤其 ...

  2. Wine 安装迅雷5.8.14.176

    测试过的系统版本:Kubuntu 22.04 测试过的Wine版本 Wine7.8 程序下载地址: https://pan.baidu.com/s/1pSgunVH3WtACssX5we3DdQ 提取 ...

  3. 谣言检测(ClaHi-GAT)《Rumor Detection on Twitter with Claim-Guided Hierarchical Graph Attention Networks》

    论文信息 论文标题:Rumor Detection on Twitter with Claim-Guided Hierarchical Graph Attention Networks论文作者:Erx ...

  4. 程序员便于开发的一些工具、网站、App。

    http://www.kancloud.cn 关于文档,各种技术,框架的学习指南,API文档搜索方便. https://leetcode.com/ 程序员刷题面试网站,无聊的时候可以做一做.

  5. 结构体struct知识

    2022-10-12 08:52:03 //    结构体知识#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>#include<m ...

  6. Vue学习之--------计算属性(2022/7/9)

    文章目录 1.计算属性 1.1 计算属性实现 1.1.1 基础知识 1.1.2 代码实例 1.1.3 测试效果 1.2 计算属性简写 1.2.1 简写代码 1.3 使用插值语法实现 1.3.1 代码实 ...

  7. __g is not defined

    新手小白学习小程序开发遇到的问题以及解决方法 文章目录 1.出现的问题 2.解决的方法 1.出现的问题 2.解决的方法 删除app.json中的 "lazyCodeLoading" ...

  8. 自建流媒体如何录制视频。齐博x1齐博x2齐博x3齐博x4齐博x5齐博x6齐博x7齐博x8齐博x9齐博x10

    http://x1.eapis.site/ 先打开配置文件\conf\config.php 里边的内容大概如下,第一项是必须要配置的,换成你的网站域名网址.第二项,如果流媒体服务器配置了https证书 ...

  9. N32G4系列——复用功能重映射(USART为例)

    开发测试环境:SDK,N32G455x系列芯片 在国民MCU中G系列IO口有第二复用功能,这时需要用到重映射功能. 一.系列芯片手册定义 1.1.芯片IO口默认功能查看 如图,在该系列芯片的数据手册中 ...

  10. JAVA开发搞了一年多的大数据,究竟干了点啥

    JAVA开发搞了一年多大数据的总结 ​ 2021年7月份加入了当前项目组,以一个原汁原味的Java开发工程师的身份进来的,来了没多久,项目组唯一一名大数据开发工程师要离职了,一时间一大堆的数据需求急需 ...