链表|数据结构|C语言深入学习

什么是链表

离散,就是“分离的、散开的”

链表是什么样子的:

有限个节点离散分配

彼此间通过指针相连

除了首尾节点,每个节点都只有一个前驱节点和一个后继节点

首节点没有前驱结点,尾节点没有后继节点

基本概念术语:

首节点:第一个存放有效数据的节点;

尾节点:最后一个存放有效数据的节点

头节点是首节点前面的那个节点。头结点里面不存放数据,有效数据是从首节点开始存的

头结点存在的目的是什么?

对链表进行操作的时候,在前面加上一个没有实际含义的头节点可以方便对链表进行操作

头指针:指向头节点的指针变量,存放了头节点的地址

尾指针:指向尾节点的指针变量,存放了尾节点的地址

链表中所有的节点的数据类型都是一样的,包括头结点

确定一个链表最少需要几个参数

只需要一个参数:头指针

通过头指针可以得到一个链表的其他所有信息

链表节点的数据类型怎么表示

每一个节点都有数据域指针域两部分

尾节点除外的每一个节点的指针域指向了下一个节点的[节点整体]

这个[节点整体]的数据类型跟他的前一个节点的数据类型是完全一样的

也就是说,一个结构体变量中的某一成员指向了跟它整体(这个结构体变量)一样的数据类型

因为第一个节点的指针域指向了第二个节点整体,而第二个节点整体和第一个节点整体的数据类型是一模一样的

说的抽象一点,就是:某一结构体的变量中的一个成员指向了和它本身的数据类型一样的另一个变量

typedef struct Node {

int data;//数据域

struct Node* pNext;//指针域  

}NODE,*PNODE;

//NODE是struct Node类型,PNODE是struct Node*类型

/*

指针域指向了和它整体本身的数据类型一模一样的另一个变量的整体

指针域存放的是下一个节点的地址,所以应该是【下一个节点的数据类型*】 类型的指针变量

下一个节点跟这个节点本身的数据类型一样,都是struct Node类型

所以,指针域的指针变量的类型是struct Node*类型

*/

链表的分类

·单链表

·双链表

每一个节点都有两个指针域

·循环链表

能通过任何一个节点找到其他所有的节点

·非循环链表

非循环单链表的创建和遍历

#include<stdio.h>

#include<malloc.h>

#include<stdlib.h>

typedef struct Node {

int data;//数据域

struct Node* pNext;//指针域  

}NODE, * PNODE;

//NODE是struct Node类型,PNODE是struct Node*类型

/*

  指针域指向了和它整体本身的数据类型一模一样的另一个变量的整体

  指针域存放的是下一个节点的地址,所以应该是【下一个节点的数据类型*】 类型的指针变量

  下一个节点跟这个节点本身的数据类型一样,都是struct Node类型

  所以,指针域的指针变量的类型是struct Node*类型

 */

PNODE createList();

void traverseList(PNODE pHead);

int main() {

PNODE pHead = NULL;

//等价于struct Node* PHead = NULL;

pHead = createList();//创建一个非循环单链表,并将链表的头结点的地址赋给pHead

traverseList(pHead);

return 0;

}

PNODE createList() {

int len;//用来存放链表有效节点的个数

int val;//用来临时存放用户输入的节点的个数

/*

  pHead是一个头指针,pHead指向了头结点

  这个头结点不存放有效数据*/

PNODE pHead = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

PNODE pTail = pHead;

pTail->pNext = NULL;

printf("请输入需要生成的链表节点的个数:");

scanf("%d", &len);

for (int i = 0; i < len; i++) {

printf("请输入需要生成的链表节点值:");

scanf("%d", &val);

PNODE pNew = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

pNew->data = val;

pTail->pNext = pNew;

pNew->pNext = NULL;

pTail = pNew;

}

/*

  循环了len次,每一次都创建并分配了一个新节点,用pNew(指针变量)表示这个新节点

  创建了新节点后,为新节点数据域赋值

  创建了一个pTail指针变量,链表中没有有效节点之前pTail指向头结点(此时,头指针pHead和pTail都指向头结点)

  创建了一个新节点之后,为新节点赋值数据域,pNew->data = val

  pTail指针变量指向的节点(最开始是头结点,之后是链表中最后一个节点)的指针域存上新节点的地址pTail->pNext = pNew

  此时pTail指针变量指向的是倒数第二个节点

  将新茶入的节点的指针域置空pNew->pNext = NULL

  将pTail指针变量指到最后一个新插入的节点上,确保下一次插入新节点之前pTail总是指到当前链表最后一个节点上

  pTail = pNew*/

return pHead;

}

void traverseList(PNODE pHead) {

PNODE p = pHead->pNext;

while (NULL != p) {

printf("%d ", p->data);

p = p->pNext;

}

printf("\n");

}

非循环单链表插入节点

非循环单链表删除节点

链表排序算法

链表冒泡排序:

void sortList(PNODE pHead) {

if (isEmpty(pHead)) {

printf("链表为空,无法排序");

exit(-1);

}

PNODE p, q;

int i, j;

int n = numNode(pHead);//求链表有效节点个数

for (i = 0, p = pHead->pNext; i < n - 1; i++, p = p->pNext) {

for (j = 0, q = pHead->pNext; j < n - 1 - i; j++, q = q->pNext) {

if (q->data > q->pNext->data) {

int t = q->data;

q->data = q->pNext->data;

q->pNext->data = t;

}

}

}

}

由以下数组冒泡排序改编:

//数组冒泡排序:

for (int i = 0; i < n - 1; i++) {

for (int j = 0; j < n - 1 - i; j++) {

if (a[j] < a[j + 1]) {

int t = a[j];

a[j] = a[j + 1];

a[j + 1] = t;

}

}

}

链表完整代码

#include<stdio.h>

#include<malloc.h>

#include<stdlib.h>

typedef struct Node {

int data;//数据域

struct Node* pNext;//指针域  

}NODE, * PNODE;

//NODE是struct Node类型,PNODE是struct Node*类型

/*

  指针域指向了和它整体本身的数据类型一模一样的另一个变量的整体

  指针域存放的是下一个节点的地址,所以应该是【下一个节点的数据类型*】 类型的指针变量

  下一个节点跟这个节点本身的数据类型一样,都是struct Node类型

  所以,指针域的指针变量的类型是struct Node*类型

 */

PNODE createList();//创建

void traverseList(PNODE pHead);//遍历

bool isEmpty(PNODE pHead);//判空

int numNode(PNODE pHead);//求链表有效节点个数

void sortList(PNODE pHead);//排序

bool insertNode(PNODE pHead, int pos, int val);//插入节点

bool deleteNode(PNODE pHead, int pos);//删除节点

int main() {

PNODE pHead = NULL;

//等价于struct Node* PHead = NULL;

pHead = createList();//创建一个非循环单链表,并将链表的头结点的地址赋给pHead

traverseList(pHead);//遍历

if (isEmpty(pHead))

printf("链表为空");

else

printf("链表非空");

//求链表有效节点的个数

printf("链表中有效节点的个数为%d\n", numNode(pHead));

sortList(pHead);

traverseList(pHead);//遍历

insertNode(pHead, 1, 125);

traverseList(pHead);//遍历

deleteNode(pHead, 1);

traverseList(pHead);//遍历

return 0;

}

PNODE createList() {

int len;//用来存放链表有效节点的个数

int val;//用来临时存放用户输入的节点的个数

/*

  pHead是一个头指针,pHead指向了头结点

  这个头结点不存放有效数据*/

PNODE pHead = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

PNODE pTail = pHead;

pTail->pNext = NULL;

printf("请输入需要生成的链表节点的个数:");

scanf("%d", &len);

for (int i = 0; i < len; i++) {

printf("请输入需要生成的链表节点值:");

scanf("%d", &val);

PNODE pNew = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

pNew->data = val;

pTail->pNext = pNew;

pNew->pNext = NULL;

pTail = pNew;

}

/*

  循环了len次,每一次都创建并分配了一个新节点,用pNew(指针变量)表示这个新节点

  创建了新节点后,为新节点数据域赋值

  创建了一个pTail指针变量,链表中没有有效节点之前pTail指向头结点(此时,头指针pHead和pTail都指向头结点)

  创建了一个新节点之后,为新节点赋值数据域,pNew->data = val

  pTail指针变量指向的节点(最开始是头结点,之后是链表中最后一个节点)的指针域存上新节点的地址pTail->pNext = pNew

  此时pTail指针变量指向的是倒数第二个节点

  将新茶入的节点的指针域置空pNew->pNext = NULL

  将pTail指针变量指到最后一个新插入的节点上,确保下一次插入新节点之前pTail总是指到当前链表最后一个节点上

  pTail = pNew*/

return pHead;

}

void traverseList(PNODE pHead) {

PNODE p = pHead->pNext;

while (NULL != p) {

printf("%d ", p->data);

p = p->pNext;

}

printf("\n");

}

bool isEmpty(PNODE pHead) {

if (pHead->pNext == NULL)

return true;

else

return false;

}

int numNode(PNODE pHead) {

PNODE p = pHead->pNext;

int num = 0;

while (p != NULL) {

num++;

p = p->pNext;

}

return num;

}

void sortList(PNODE pHead) {

if (isEmpty(pHead)) {

printf("链表为空,无法排序");

exit(-1);

}

PNODE p, q;

int i, j;

int n = numNode(pHead);//求链表有效节点个数

for (i = 0, p = pHead->pNext; i < n - 1; i++, p = p->pNext) {

for (j = 0, q = pHead->pNext; j < n - 1 - i; j++, q = q->pNext) {

if (q->data > q->pNext->data) {

int t = q->data;

q->data = q->pNext->data;

q->pNext->data = t;

}

}

}

}

bool insertNode(PNODE pHead, int pos, int val) {

PNODE p = pHead;

int i = 0;

while (p && i < pos) {

p = p->pNext;

i++;

}

if (p == NULL || i > pos) {

return false;

}

PNODE pNew = (PNODE)malloc(sizeof(NODE));

if (pNew == NULL) {

printf("动态内存分配失败");

return false;

}

pNew->data = val;

pNew->pNext = p->pNext;

p->pNext = pNew;

return true;

}

bool deleteNode(PNODE pHead, int pos) {

int i = 0;

PNODE p = pHead;

while (p && i < pos) {

p = p->pNext;

i++;

}

if (!p || i > pos)

return false;

PNODE q = (PNODE)malloc(sizeof(NODE));

q = p->pNext;

p->pNext = p->pNext->pNext;

free(q);

return true;

}

  • 31
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
数据结构与算法是计算机科学中非常重要的一门课程,它研究的是如何组织和存储数据,以及如何使用算法来对这些数据进行操作和处理。C语言是一种非常流行的编程语言,广泛应用于软件开发领域。因此,有很多关于数据结构与算法的C语言的电子版书籍提供给学习者参考和学习。 这些电子版书籍通常会详细介绍数据结构和算法的基本概念和原理,然后逐步引入不同的数据结构,如数组、链表、栈、队列、树、图等,并讲解它们的实现和应用方法。同时,还会介绍一些常用的算法,如排序、查找、递归、动态规划等,并给出相应的C语言实现代码。 与纸质版书籍相比,电子版书籍具有一些明显的优势。首先,电子版书籍可以随时随地进行阅读,只需在电子设备上打开即可。此外,电子版书籍还可以进行全文搜索,方便查找和定位需要的内容。还可以通过超链接跳转到相关的章节和知识点,方便复习和深入学习。 对于想要学习数据结构与算法,并使用C语言实现的人来说,电子版书籍是一个非常好的选择。它们可以帮助学习者系统地掌握数据结构和算法的知识,并提供大量的实例和练习,帮助学习者加深理解并提升编程能力。此外,电子版书籍还可以根据学习者的需求进行个性化设置和调整,提供更好的学习体验。 总之,数据结构与算法C语言电子版书籍是学习学习和掌握数据结构与算法知识的重要资源,它们提供了方便灵活的学习方式和大量的学习资源,帮助学习者快速入门和提高编程能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

01红C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值