C语言数据结构(链表概念讲解和插入操作)

news/2024/11/22 18:32:16/

文章目录

  • 前言
  • 一、什么是链表
  • 二、链表的优点和缺点
  • 三、链表节点的定义
  • 四、初始化链表
  • 五、链表的插入
    • 1.头部插入
    • 2.尾部插入
    • 3.中间插入
  • 六、遍历链表
  • 七、释放链表
  • 总结


前言

本篇文章带大家正式的来学习数据结构,数据结构是学习操作系统,和深入C语言必不可少的,所以这篇文章开始带大家学习数据结构的知识。

一、什么是链表

链表(Linked List)是一种常见的数据结构,用于存储和组织数据元素。它由一系列节点(Node)组成,每个节点包含存储的数据(或称为元素/值)以及指向下一个节点的引用(或链接/指针)。

链表中的节点可以通过指针连接在一起,形成一个链式结构,而不像数组那样在内存中连续存储。每个节点只需要存储指向下一个节点的引用,而不需要预先分配固定大小的内存空间。这使得链表具有动态性,能够高效地插入、删除节点,但对于随机访问则效率较低。

链表分为单向链表(Singly Linked List)和双向链表(Doubly Linked List)两种常见类型。

在单向链表中,每个节点的引用指向下一个节点,最后一个节点的引用为空(null)。从头节点(首节点)开始遍历链表,通过一次次跳转到下一个节点来访问或操作数据。

双向链表在单向链表的基础上添加了一个指向前一个节点的引用。这使得双向链表可以从头节点和尾节点两个方向遍历,增加了一些操作的灵活性,但也增加了一些额外的空间开销。

链表适用于频繁的插入和删除操作,而不需要频繁的随机访问。它在内存使用上可以比数组更灵活,但在访问速度上相对较慢。链表常用于实现其他高级数据结构,如队列、栈和图等。

需要注意的是,链表的节点并不需要连续的内存空间,可以在内存中分散存储。这与数组的存储方式不同,数组在内存中需要一段连续的空间来存储所有元素。这是链表与数组的主要区别之一。

二、链表的优点和缺点

链表的优点包括:

1.动态性:链表的大小可以根据需要动态地增长或缩小,不像数组需要预先分配固定大小的空间。

2.插入和删除操作高效:由于链表中的节点仅需修改相邻节点的指针,插入和删除节点的操作复杂度为O(1),效率很高。

3.内存利用率高:链表节点可以在内存中分散存储,可以充分利用零散的空间。

4.支持灵活的数据结构:链表可以用于实现其他高级数据结构,如队列、栈和图等。

链表的缺点包括:

1.随机访问效率低:由于链表的内存存储不是连续的,要访问特定位置的节点,必须从头节点开始遍历,直到达到目标位置,这使得随机访问的效率较低。时间复杂度为O(n)。

2.额外的存储空间:链表需要额外的指针来维护节点之间的连接关系,这会占用一定的存储空间。

3.不支持随机访问:由于链表的节点间没有直接的索引关系,不能像数组那样通过索引快速访问任意位置的节点。

4.需要遍历整个链表:若要访问链表中的所有节点,需要从头节点开始遍历整个链表,这会增加一定的时间开销。

综上所述,链表适合在插入和删除操作较频繁、对随机访问要求较低的场景下使用,而在需要频繁的随机访问和占用连续内存空间的场景中,数组可能是更好的选择。选择使用链表或数组取决于具体的应用需求和优化目标。

三、链表节点的定义

链表节点(Node)是链表中的基本组成单元,包含存储的数据(或称为元素/值)以及指向下一个节点(或前一个节点)的引用。

基本的链表节点定义通常包括以下成员:

1.数据(Data):节点存储的数据或元素值。

2.下一个节点引用(Next):指向链表中下一个节点的引用。在单向链表中,该引用指向链表中的后继节点;在双向链表中,该引用则指向链表中的下一个节点。

3.(可选)前一个节点引用(Previous):仅在双向链表中存在,指向链表中的前一个节点。

// 定义链表节点结构体
struct Node {int data;          // 节点存储的数据struct Node* next; // 指向下一个节点的指针
};

四、初始化链表

在C语言中,初始化链表通常需要创建一个头节点并将其置为NULL,表示链表为空。

#include <stdio.h>
#include <malloc.h>/* 定义链表节点 */
typedef struct node
{int data;          // 节点存储的数据struct node* PNext; // 指向下一个节点的指针
}Node;/* 初始化链表 */
Node* InitList(void)
{Node* head = (Node*)malloc(sizeof(Node));if (head != NULL){head->data = 0;head->PNext = NULL;}return head;
}int main(void)
{Node* Head = InitList();return 0;
}

五、链表的插入

1.头部插入

当我们在链表的头部插入节点时,意味着我们要将一个新的节点插入到链表的开头位置。
首先需要使用malloc函数来分配一个节点,有了节点后我们才可以进行插入操作。
进行头部插入只需要将新分配的节点的PNext指向原来的头指针,然后再将头指针指向新的节点。
这个时候新的节点就成为了头节点。

#include <stdio.h>
#include <malloc.h>/* 定义链表节点 */
typedef struct node
{int data;          // 节点存储的数据struct node* PNext; // 指向下一个节点的指针
}Node;/* 初始化链表 */
Node* InitList(void)
{Node* head = (Node*)malloc(sizeof(Node));if (head != NULL){head->data = 0;head->PNext = NULL;}return head;
}/* 头部插入 */
void HeadAdd(Node** HeadNode, int data)
{Node* NewNode = (Node*)malloc(sizeof(Node));if (NewNode != NULL){NewNode->data = data;NewNode->PNext = *HeadNode;*HeadNode = NewNode;}
}int main(void)
{Node* Head = InitList();HeadAdd(&Head, 1);printf("%d\n", Head->data);//打印1printf("%d\n", Head->PNext->data);//打印0free(Head);return 0;
}

这里使用一张图来帮助大家理解:
在这里插入图片描述

2.尾部插入

尾部插入要比头部插入稍微难一点,因为需要先找到最后的一个节点,然后将最后一个节点的下一个指针指向新的节点,这里就涉及到了链表的遍历了。我们放下面讲解。

#include <stdio.h>
#include <malloc.h>/* 定义链表节点 */
typedef struct node
{int data;          // 节点存储的数据struct node* PNext; // 指向下一个节点的指针
}Node;/* 初始化链表 */
Node* InitList(void)
{Node* head = (Node*)malloc(sizeof(Node));if (head != NULL){head->data = 0;head->PNext = NULL;}return head;
}/* 头部插入 */
void HeadAdd(Node** HeadNode, int data)
{Node* NewNode = (Node*)malloc(sizeof(Node));if (NewNode != NULL){NewNode->data = data;NewNode->PNext = *HeadNode;*HeadNode = NewNode;}
}/* 尾部插入 */
void TailAdd(Node** HeadNode, int data)
{Node* NewNode = (Node*)malloc(sizeof(Node));if (NewNode != NULL){NewNode->data = data;NewNode->PNext = NULL;/* 判断链表是否为空 */if (*HeadNode == NULL){*HeadNode = NewNode;}else{Node* current = *HeadNode; while (current->PNext != NULL){/* 遍历链表找出最后一个节点 */current = current->PNext;}/* 将最后一个节点的下一个指针指向新的节点 */current->PNext = NewNode;}}
}int main(void)
{Node* Head = InitList();TailAdd(&Head, 1);printf("%d\n", Head->data);//打印0printf("%d\n", Head->PNext->data);//打印1free(Head);return 0;
}

在这里插入图片描述

3.中间插入

中间插入的话就会稍微复杂一点了,因为是在中间进行插入操作,首先需要找到对应的位置,并且需要关注前后两个节点的PNext指针指向。

/* 中间插入 */
void MiddleAdd(Node** HeadNode, int data, int index)
{Node* NewNode = (Node*)malloc(sizeof(Node));Node* current = *HeadNode;int i = 0;if (NewNode != NULL){NewNode->data = data;/* 判断链表是否为空 */if (*HeadNode == NULL){*HeadNode = NewNode;}else if (current->PNext == NULL){/* 只有一个节点直接使用尾部插入 */TailAdd(HeadNode, data);}else{			for (i = 0; i < index - 1; i++){/* 遍历链表找到要插入位置的前一个链表节点 */current = current->PNext;}Node* currentNext = current->PNext;/* 记录要插入位置的下一个节点 */current->PNext = NewNode;NewNode->PNext = currentNext;}}
}

在这里插入图片描述

六、遍历链表

链表的遍历其实并不难,我们使用一个名为current的指针来追踪当前节点,初始化为*HeadNode。如果当前节点不为NULL,就继续移动current指针到下一个节点,遍历到链表的结尾。

/* 遍历链表 */
void TraverseList(Node* HeadNode)
{Node* current = HeadNode;while (current != NULL){printf("Node data : %d\n", current->data);current = current->PNext;//向后移动一个位置}
}

七、释放链表

释放链表也是比较简单的,和遍历链表的操作有一些类似,只不过这里多了一步操作就是需要记录当前节点,当当前节点移动后对当前节点进行free释放,这样就可以完成链表的释放了。

/* 释放链表 */
void FreeList(Node* HeadNode)
{Node* Nowcurrent = HeadNode;Node* Oldcurrent = NULL;while (Nowcurrent != NULL){Oldcurrent = Nowcurrent;/* 记录当前链表节点 */Nowcurrent = Nowcurrent->PNext;//向后移动一个位置free(Oldcurrent);/* 释放当前链表节点 */}
}

总结

本篇文章开始我们会开始使用C语言来讲解数据结构中的知识,希望大家可以跟着我好好的掌握这些知识,掌握了数据结构后对C语言的提高会有很大的帮助。
本篇文章的代码将同步到公众号中提供给大家使用。


http://www.ppmy.cn/news/965475.html

相关文章

ChatGPT未来会拥有自我情感和思维吗?

ChatGPT是一种基于人工智能的聊天机器人&#xff0c;它可以模拟人类的对话&#xff0c;并且可以回答各种问题。目前&#xff0c;ChatGPT已经非常先进&#xff0c;但是它是否会拥有自我情感和思维呢&#xff1f; 首先&#xff0c;我们需要明确一点&#xff0c;ChatGPT是一种基于…

Chatgpt使用的是哪种自我监督学习算法

ChatGPT模型使用的自我监督学习算法是基于语言模型的自监督学习方法。具体来说&#xff0c;在训练ChatGPT模型时&#xff0c;使用的是一种被称为「掩码语言模型」&#xff08;Masked Language Model&#xff0c;MLM&#xff09;的自监督学习算法。 该算法的主要思路是在示例中…

ChatGPT:机器学习的下一个大步骤

Chatgpt | Chat | Gpt | 小智Ai | Chat小智 | Gpt小智 | ChatGPT小智Ai | GPT小智 | GPT小智Ai | Chat小智Ai 丨 机器学习是人工智能领域的重要分支之一&#xff0c;已经广泛应用于各个领域&#xff0c;例如自然语言处理、计算机视觉、智能推荐等。然而&#xff0c;当前的机器…

ChatGPT的自我学习与人类创造力的关系

chatgpt丨chatgpt丨chat丨openAI丨open丨小智ai丨openai丨chatgpt丨chat丨小智ai ChatGPT作为一种具备自我学习能力的人工智能模型&#xff0c;引发了对于人类创造力与机器学习之间关系的思考。本文将探讨ChatGPT的自我学习如何与人类创造力相互作用&#xff0c;并讨论其对于人…

利用ChatGPT完成深度学习分类任务

利用ChatGPT完成深度学习分类任务 一、任务背景 ​ 关于早期诊断NEC&#xff08;坏死性小肠结肠炎&#xff08;Necrotizing enterocolitis&#xff0c;NEC&#xff09;&#xff09;和及时干预一直是临床关注的重点和难点问题。现在手上有相关的临床数据集&#xff0c;我们想要…

vue Duplicate keys detected: ‘‘. This may cause an update error. found in

错误原因&#xff1a; 在使用v-for的时候&#xff0c;都要必须加上一个唯一的key值&#xff0c;key的值写成一样的了。所以就导致了警告。尽量不要使用index下标作为key值 换成后台数据返回的id或者i*随机数作为key值就好

情人节也是假的!全球30%男性打算用ChatGPT写情书了

视学算法报道 编辑&#xff1a;Aeneas 木槿 【导读】调查显示&#xff0c;有42%的美国男性打算使用ChatGPT写情书了。AI写的情书&#xff0c;能比人类的好吗&#xff1f; 今天还要辛苦搬砖一整天的单身狗小编&#xff0c;该怎么庆祝这个节日呢&#xff1f; 虽然无法体会爱情的…

活着也是修行啊

禅的行囊&#xff0c;比尔波特&#xff0c;美国禅宗居士&#xff0c;讲的是他2006年到中国参访禅宗各代祖师道场的经历。六祖惠能说入世修行&#xff0c;挑水生火做饭走路都是修行。 我个人虽然是佛教华严宗居士&#xff0c;不是禅宗居士&#xff0c;但我仍然很同意六祖惠能的观…