【数据结构复习】单链表

1.链表的定义

链表是一种动态存储的线性数据结构,通过节点指针关联离散的内存块。其核心特点是:

“节点本身不存储大量数据,而是作为数据挂载的钩子”

我们可能会觉得用一个数组去存储这些变量就好了,为什么要多引出一个“链表”?

数组是固定内存,如果你要插入一个元素到数组中间,你需要把后面的元素全部往后移,然后再插进去;如果数组分配的内存小了,则插不进去了,大了又浪费内存。

而链表能够完美解决数组在插入、扩容和内存利用上的痛点,核心在于其动态内存分配指针重定向机制。相比数组主要有以下两个优势:

1、无需移动后续元素

2、动态内存分配(合理使用内存中的非连续空间)

2.单向链表

2.1 链表结构

代码部分解析:

在链表中都会有数据域指针域

struct LNode {
    int data;//数据域
    struct LNode *next;//指针域
};

数据域:可存放该节点的任何数据类型值。

指针域:指向下一个节点的引用指针。

创建3个链表指针

struct LNode *head,*middle,*last;

指向对应节点

head = (LNode *)malloc(sizeof(struct LNode));
middle = (LNode *)malloc(sizeof(struct LNode));
last = (LNode *)malloc(sizeof(struct LNode));
给各个节点数据域data赋值

head -> data = 10;
middle -> data =20;
last -> data =30;

将节点连接起来

head的next指向middle,并且会将middle的地址2024存到head的next,其他以此类推。

last的后续没有节点,所以last的next为null

head->next = middle ;
middle->next = last;
last->next = NULL;

最后应该是这个样

2.2 通过头指针获取链表中的值

第一个循环

struct LNode *temp = head;//创建一个临时指针temp,把头结点指针赋值给temp
while(temp != NULL){//temp此时不等于NULL,满足条件,进入循环
    printf("%d ",temp->data);//输出序列“10”
    temp = temp->next;//把temp移到下一个节点2024
}

代码执行一遍之后,再进while,检查temp是否为NULL。不为NULL,满足条件,进入下一次遍历。

第二次循环

struct LNode *temp = head;//创建一个临时指针temp,把头结点指针赋值给temp
while(temp != NULL){//temp此时不等于NULL,满足条件,进入循环
    printf("%d ",temp->data);//输出序列“20”
    temp = temp->next;//把temp移到下一个节点3024
}

此时temp指向3024这个地址。

检查temp还是不为NULL,进入下一个遍历。

第三次循环

struct LNode *temp = head;//创建一个临时指针temp,把头结点指针赋值给temp
while(temp != NULL){//temp此时不等于NULL,满足条件,进入循环
    printf("%d ",temp->data);//输出序列“30”
    temp = temp->next;//把temp移到下一个节点NULL
}

temp = NULL,不满足while里面的条件,跳出循环,从而打出三个值。

2.3 单链表操作

#include <stdio.h>
#include <stdlib.h>
​
// 定义链表节点结构体
struct LNode {
    int data;          // 节点存储的数据
    struct LNode* next; // 指向下一个节点的指针
};
​
// 初始化空链表
struct LNode* head = NULL;
​
// 头插法插入节点
void insertHead(int e) {
    // 分配内存给新节点
    struct LNode* newNode = (struct LNode*)malloc(sizeof(struct LNode));
    if (newNode == NULL) {
        printf("Memory allocation failed\n"); // 内存分配失败处理
        return;
    }
    
    // 设置新节点的数据和指向原头结点的指针
    newNode->data = e;
    newNode->next = head;//也就是记录上一个节点的地址
    
    // 更新头指针地址
    head = newNode;
}
​
// 尾插法插入节点
void insertTail(int e) {
    // 分配内存给新节点
    struct LNode* newNode = (struct LNode*)malloc(sizeof(struct LNode));
    if (newNode == NULL) {
        printf("Memory allocation failed\n"); // 内存分配失败处理
        return;
    }
​
    // 设置新节点的数据和指针
    newNode->data = e;
    newNode->next = NULL;
​
    // 如果链表为空,则头指针指向新节点
    if (head == NULL) {
        head = newNode;
        return;
    }
​
    // 否则找到尾节点并将其next指向新节点
    struct LNode* tail = head;
    while (tail->next != NULL) {//用尾结点的特性找尾节点
        tail = tail->next;
    }
    tail->next = newNode;
}
​
// 删除指定值的节点
void deleteNode(int key) {
    struct LNode* temp = head;
    struct LNode* prev = NULL;
​
    // 如果头节点就是要删除的节点
    if (temp != NULL && temp->data == key) {
        head = temp->next; // 修改头指针
        free(temp); // 释放内存
        return;
    }
​
    // 查找要删除的节点
    while (temp != NULL && temp->data != key) {
        prev = temp;
        temp = temp->next;
    }
​
    // 如果找不到该节点
    if (temp == NULL) {
        printf("Key not found in the list\n");
        return;
    }
​
    // 跳过要删除的节点
    prev->next = temp->next;
    free(temp); // 释放内存
}
​
// 搜索指定值的节点
struct LNode* searchNode(int key) {
    struct LNode* current = head;
    while (current != NULL) {
        if (current->data == key) {
            return current; // 返回找到的节点
        }
        current = current->next;
    }
    return NULL; // 如果没有找到返回NULL
}
​
// 打印链表
void printList() {
    struct LNode* current = head;
    while (current != NULL) {
        printf("%d -> ", current->data); // 打印当前节点数据
        current = current->next;
    }
    printf("NULL\n"); // 表示链表结束
}
​
int main() {
    // 使用头插法插入节点
    insertHead(10);
    insertHead(20);
    insertHead(30);
    printf("After inserting nodes at head:\n");
    printList(); // 打印链表
​
    // 使用尾插法插入节点
    insertTail(40);
    insertTail(50);
    printf("\nAfter inserting nodes at tail:\n");
    printList(); // 打印链表
​
    // 删除指定值的节点
    deleteNode(20);
    printf("\nAfter deleting node with value 20:\n");
    printList(); // 打印链表
​
    // 搜索指定值的节点
    struct LNode* searchedNode = searchNode(30);
    if (searchedNode != NULL) {
        printf("\nFound node with value %d\n", searchedNode->data); // 打印找到的节点数据
    } else {
        printf("\nNode not found\n");
    }
​
    return 0;
}

学习抽象过程的笔记如下,看完之后再去理解前面的代码会好一点

在这里:指向 = 具体节点

是一种做题技巧,帮助理解。例如

  • p=q:p指向q

  • p=q->next:p指向q后面哪一个小方格

  • p->next=q:p的后面那个小方格指向q(也可以理解为圈起来的那根线)

  • p->next=q->next:p后面的小方格指向q的下一个地址

  • p=p->next:p指向p的下一个地址,也就是p的下一个地址赋值给p

用以上的方法理解

单链表插入

单链表删除与头插法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小张要努力o!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值