数据结构之单链表单链表是一种链式存取的数据结构,用一组地址任意的存储单元 存放线性表中的数据元素 。链表中的数据是以结点来表示的,每个结点的构成:元素( 数据元素 的映象) + 指针 (指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
栏主介绍:单链表就是结构体变量和结构体变量 通过指针成员连接在一起,故:单链表就是多个结构体变量。
链表分类根据链表有无头结点,指针是是否双向,链表是否环状,我们把链表分为以下几种:
- 带头结点单链表。
- 无头结点单链表。
- 双向链表。
- 双向循环链表。
ps:本栏主要内容是带头结点的单链表,相关其他内容大家可以转接栏主的数据结构专栏,不知道在哪里的都可以私信栏主“数据结构链式结构讲解”。
单链表操作带头结点的链式结构主要有以下常规操作:
- 创建链表:创建头结点。
- 创建结点:为插入做准备。
- 表头插入。
- 表尾插入。
- 指定位置插入。
- 表头删除。
- 表尾删除。
- 指定位置删除。
- 链表的打印。
插曲:指针如何当做变量使用?代码如下:
结构体定义数据结构中的结构体设计, 一般都是单一个体的抽象,即把整个结构拆开的零件。
创建表头单链表就是结构体变量和结构体变量 通过指针成员连接在一起,故如果定义一个函数去创建链表,其实就是创建表头,表头也是一个结构体变量,故就是创建结构体变量的过程,然后数据结构一般习惯用指针去表示,指针变成变量,故通过动态内存是申请即可变成变量,最后只需要给变量初始化即可。
单链表的创建表头源码:
创建节点创建结点单独用一个函数封装,为插入节点做准备,结点也是结构体变量,相对于表头只是多了一个数据域,而数据可以形参传进去,这样就可以实现数据加工,加工为一个结点数据。
单链表的结点创建代码:
表头法插入因为是一个有表头的链表,故表头位置不可改变,插入结点只能放在表头后面。表头就像讲台,学生座位怎么排,都只能放在讲台后面,不能排到讲台前面去。
单链表的表头插入代码:
表尾插入表尾插入,首先找到表尾,然后把插入的结点放到表尾结点后面即可。
单链表的表尾插入代码:
指定位置插入只要找到指定位置与指定位置前面哪个结点,我想每一个同学都应该会插入结点。指定位置插入问题转换为找到指定位置以及指定位置的前面哪个结点。
单链表的指定位置插入代码实现:
链表遍历因为做的是一个有表头的链表 ,故打印数据的时候是从第二个节点开始打印,如果存在数据,打印数据,打印完后往下一个结点移动即可。就像公交车一样,过一站下一批乘客。
单链表的遍历实现:
测试检验
- 首先表头插入数据1,在表头插入数据3 ,链表中数据为 3 1。
- 在指定1前面插入2,链表中数据为3 2 1。
- 最后表尾插入0,链表中数据为3 2 1 0。
故打印结果是3 2 1 0
完整源码 #include <stdio.h>#include <stdlib.h>//结构体的写法:某一种结构的单一个体struct Node{ int data; struct Node* next;};//有表头的链表,创建表头--->创建结构体变量struct Node* createHead(){ //指针如何变成变量 struct Node* headNode = (struct Node*)malloc(sizeof(struct Node)); //使用变量有一个规则:变量使用前必须要初始化 //结构体变量 //(*headNode).data //不做数据的初始化,因为第一个节点不存放数据 headNode->next = NULL; return headNode;}//未插入做准备//创建节点-->把别人的数据变成节点的模样//节点模样-->结构体变量:和表头是一样的struct Node* createNode(int data){ struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = data; newNode->next = NULL; return newNode;}//表头插入//想清楚表头插入是怎么插入,把姿势想清楚void insertByHead(struct Node* headNode,int data){ struct Node* newNode = createNode(data); newNode->next = headNode->next; headNode->next = newNode;}//指定位置删除void deleteByAppoin(struct Node* headNode, int posData){ struct Node* posNodeLeft = headNode; struct Node* posNode = headNode->next; //两个条件顺序不能改变 NULL->data 会引发冲突 //posNode!=NULL 没有找到退出循环的条件 while (posNode != NULL&&posNode->data != posData) { //不等于,并排往下走 /* //第一种写法 posNodeLeft=posNodeLeft->next; posNode=posNode->next; */ posNodeLeft = posNode; posNode = posNode->next; } //分析退出循环的条件 if (posNode == NULL) { printf("为找到指定位置,无法删除!\n"); return; } else { posNodeLeft->next = posNode->next; free(posNode); posNode = NULL; printf("删除成功\n"); }}//查找结点struct Node* searchByData(struct Node* headNode, int posData){ struct Node* pMove = headNode->next; while (pMove != NULL&&pMove->data != posData) { pMove = pMove->next; } return pMove;}//删除所有的相同的数据void deleteAll(struct Node* headNode, int posData){ while (searchByData(headNode, posData) != NULL) { deleteByAppoin(headNode, posData); }}//打印函数封装void printList(struct Node* headNode){ struct Node* pMove = headNode->next; //从第二个节点开始打印 while (pMove != NULL) { printf("%d\t", pMove->data); pMove = pMove->next; } printf("\n");}//表尾插入: 找到表尾:tailNode tailNode->next=newNode;void insertByTail(struct Node* headNode, int data){ struct Node* newNode = createNode(data); struct Node* tailNode = headNode; while (tailNode->next != NULL) { tailNode = tailNode->next; } tailNode->next = newNode;}//表头删除void deleteByHead(struct Node* headNode){ struct Node* secondNode = headNode->next; //确保secondNode不能为NULL,因为NULL没有next if (secondNode != NULL) { headNode->next = secondNode->next; free(secondNode); secondNode = NULL; } }//表尾删除void deleteByTail(struct Node* headNode){ struct Node* tailLeftNode = NULL; struct Node* tailNode = headNode; while (tailNode->next != NULL) //循环条件一次都没有执行,意味着链表为NULL { tailLeftNode = tailNode; tailNode = tailNode->next; } if (tailLeftNode == NULL) { printf("链表为NULL无法删除!"); return; } else { tailLeftNode->next = NULL; //前一个结点的next指针未做处理 free(tailNode); tailNode->next = NULL; }}void deleteByTailWay(struct Node* headNode){ if (headNode->next == NULL) { return; } struct Node* tailNode = headNode; while (tailNode->next->next != NULL) { tailNode = tailNode->next; } free(tailNode->next); tailNode->next = NULL;}//指定插入void insertByAppoin(struct Node* headNode, int data, int posData){ struct Node* posNodeLeft = headNode; struct Node* posNode = headNode->next; //为什么不能缓过来?? //一旦posNode==NULL 那么不存在NULL->data操作 //a&&b a=0 b不会执行 //1>3&&a=1; while (posNode != NULL&&posNode->data != posData) { posNodeLeft = posNode; posNode = posNodeLeft->next; } if (posNode == NULL) { printf("未找到指定位置无法插入!\n"); return; } else { struct Node* newNode = createNode(data); posNodeLeft->next = newNode; newNode->next = posNode; printf("插入成功!\n"); }}int main(){ struct Node* list = createHead(); for (int i = 0; i < 3; i++) { insertByHead(list, i); insertByHead(list, i); } printList(list); deleteByAppoin(list, 1); //只能删除第一个被找到的元素 printList(list); deleteAll(list,2); printList(list); insertByTail(list, 100); printList(list); insertByAppoin(list, 90, 100); printList(list); deleteByHead(list); printList(list); deleteByTail(list); printList(list); deleteByTailWay(list); printList(list); system("pause"); return 0;}
|