单链表—增删查改
- 在单链表中增加结点
增加新结点:1.在链表末尾增加(示例代码)
2.在链表头部增加
3.在中间某个位置增加
接头体:
//链表结点 结构体
typedef struct Node {
int data; //数据域
struct Node* next; //指针域
}Node;
创建一个带有虚拟头结点的空链表。(初始化)
代码如下:
//创建头结点
Node* Creat() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
- 在链表尾插入新结点(尾插)
condition 1:链表为空,直接让该链表指向开辟的结点
condition 2:第二种情况非空,比较复杂,我们通过画图来解决:
代码实现:
//插结点到尾部
void Insert(Node* head, int data) {
Node *new = (Node*)malloc(sizeof(Node));//分配新的Node结构体对象,将指针值赋值
new->data = data;//设置数据成员
new->next = NULL;//表示是最后一个结点
Node* current = head;
while (current->next != NULL) { //遍历,直至找到链表最后一个结点
current = current->next; //指针滑动
}
current->next = new; //current指针指向最后一个结点,使current->next成为链表的最后一个结点
}
在链表指定位置插入新结点
【特别注意,不管在什么地方插入数据,我们都要传递二级指针】
当前有链表如下:
例如想在元素4之间插入新结点,过程如下:
- 生成新结点
- 将新结点下一结点为NULL,表示是最后一个结点
- 遍历链表,直到找到指定位置
- 插入新结点到指定位置
类似与找到位置,将新结点插入到其中
代码实现如下:
void InsertAtIndex(Node* head, int index, int data) {
if (index<0) {
printf("无效的指数\n");
return;
}
Node* new = (Node*)malloc(sizeof(Node));
new->data = data;
new->next = NULL;
Node* current = head;
int i = 0;//记录遍历链表的索引
while (i < index && current != NULL)//遍历,直到找到指定位置,然后插入新节点
{
current = current->next;//滑动指针
i++;//记录遍历索引
}
if (current == NULL) { //没找到提醒
printf("索引超出范围\n");
}
new->next = current->next;//将新结点的下一结点设置为当前结点的下一结点,即插入
current->next = new; //将当前结点的下一个指针设置为新结点,完成
}
删除结点
特别注意: 和插入数据一样,因为我们删除的可能是链表中的最后一个数据,即可能会改变current的指向(使之指向NULL)所以不管我们在什么地方删除数据,都需要传递二级指针。
删除单链表上第i个结点ai:
为了删除单向链表中的某个结点,首先要找到待删除结点的前趋结点,然后将此前趋结点的指针域去指向待删除结点的后继结点(p->next=q->next),最后释放被删除结点所占的存储空间。
- 查找第i-1个结点的地址,并使工作指针p指向第i-1个结点的地址;
- 将q指向被删除的第i个结点;
- 使p的指针域指向被删除结点的直接后继;
- 释放被删除的结点的空间;
内存关系:
在删除结点使用free函数释放了结点所占内存。prev->next = current->next; 正确的更新了链表的结构,删除结点之后的链表仍然是有效的链表。
代码示例
//删除结点(头删)
void Delete(Node* head, int data) {
Node* current = head->next; //将结点指针初始为第一个结点
Node* prev = head; //将结点指针初始为链表头结点
while (current != NULL) //遍历,查找需删除的结点
{
if (current->data == data) { //遍历找到位置,执行操作
prev->next = current->next;//将前一结点的下一结点指针设置为当前结点的下一结点,即删除结点
free(current);
return;
}
prev = current;//将当前结点指针更新为下一结点(滑动指针)
current = current->next;
}
printf("没有找到数据%d的节点\n", data);
}
修改单链表结点(调用getNode函数)
原理:
通过getNode函数将查找到数据所在结点,然后将结点所指向的数据更新
循环遍历链表,每次循环将当前指针指向链表下一结点(滑动指针),找到据返回该结点,否则返回NULL指针
代码示例:
//修改单链表数据
void Modify(Node* head, int data, int NewData) {
Node* current = getNode(head, data);
if (current != NULL) {
current->data = NewData;
}
else {
printf("没有找到数据%d的节点\n", data);
}
}
Node* getNode(Node* head, int data) {
Node* current = head->next;
while (current != NULL) {
if (current->data == data) {
return current;
}
current = current->next;
}
return NULL; // 找不到结点
}
查找指定数据的索引位置
采用循环遍历,每次循环将当前结点索引(index)自增,并将当前结点的指针指向下一结点,找到返回索引位置,否则返回-1
代码示例:
//查找位置
int GetIndex(Node* head, int data) {
Node* current = head->next;//滑动指针
int index = 0;
while (current != NULL)
{
if (current->data == data) {
return index;
}
index++;
current = current->next;
}
return -1;//找不到结点
}
总结:
注意以下:
1. 内存管理:在对链表进行增删查改操作时,需要动态地分配和释放内存。需要注意内存泄漏和内存碎片的问题,避免造成程序崩溃或性能下降。
2. 边界条件:在对链表进行操作时,需要注意边界条件的处理。例如,在删除节点时,需要考虑当前节点是否为链表的头节点或尾节点;在查找节点时,需要考虑链表是否为空等情况。
3. 链表的遍历:在对链表进行操作时,需要使用循环来遍历链表。需要注意循环的终止条件,避免出现死循环或遍历不完整的情况。
4. 数据的一致性:在对链表进行操作时,需要保证数据的一致性。例如,在删除节点时,需要更新前后节点的指针;在插入节点时,需要更新后续节点的指针等。
对动态内存函数的几大注意:
常见动态内存错误:
1.对NULL指针的解引用操作
2.对动态开辟空间的越界访问
3.对非动态开辟内存使用free释放
4.使用free释放动态开辟内存的一部分
5.对同一块动态内存的多次释放
6.动态开辟内存忘记释放(内存泄漏)
完整代码示例(单链表增删查改)
#include<stdio.h>
#include<stdlib.h>
//链表结点 结构体
typedef struct Node {
int data;
struct Node* next;
}Node;
//创建头结点
Node* Creat() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
//插结点到尾部
void Insert(Node* head, int data) {
Node *new = (Node*)malloc(sizeof(Node));
new->data = data;
new->next = NULL;
Node* current = head;
while (current->next != NULL) {
current = current->next;
}
current->next = new;
}
int getlength(Node* head) {
Node* current = head;
int len = 0;
while (current != NULL)
{
len++;
current = current->next;
}
return len;
}
//指定位置插入结点
void InsertAtIndex(Node* head, int index, int data) {
if (index<0) {
printf("无效的指数\n");
return;
}
Node* new = (Node*)malloc(sizeof(Node));
new->data = data;
new->next = NULL;
Node* current = head;
int i = 0;
while (i < index && current != NULL)
{
current = current->next;
i++;
}
if (current == NULL) {
printf("索引超出范围\n");
}
new->next = current->next;
current->next = new;
}
//删除结点
void Delete(Node* head, int data) {
Node* current = head->next;
Node* prev = head;
while (current != NULL)
{
if (current->data == data) {
prev->next = current->next;
free(current);
return;
}
prev = current;
current = current->next;
}
printf("没有找到数据%d的节点\n", data);
}
Node* getNode(Node* head, int data) {
Node* current = head->next;
while (current != NULL) {
if (current->data == data) {
return current;
}
current = current->next;
}
return NULL; // 找不到结点
}
//修改单链表数据
void Modify(Node* head, int data, int NewData) {
Node* current = getNode(head, data);
if (current != NULL) {
current->data = NewData;
}
else {
printf("没有找到数据%d的节点\n", data);
}
}
//查找位置
int GetIndex(Node* head, int data) {
Node* current = head->next;
int index = 0;
while (current != NULL)
{
if (current->data == data) {
return index;
}
index++;
current = current->next;
}
return -1;//找不到结点
}
//打印链表
void PrintList(Node* head) {
Node* current = head->next;
while (current != NULL)
{
printf("%d", current->data);
current = current->next;
}
printf(" \n");
}
int main() {
//创建空链表
Node* head = Creat();
//插入结点
Insert(head, 10);
Insert(head, 20);
Insert(head, 30);
Insert(head, 40);
Insert(head, 50);
//指定位置插入结点
InsertAtIndex(head, 2, 100);
//删除
Delete(head, 30);
//修改
Modify(head, 20, 25);
//打印
printf("原始链表:\n");
PrintList(head);
return 0;
}
代码实现效果