数据结构 链表类定义代码上课讲义
数据结构—链表详解

数据结构—链表详解浅谈数据结构——链表本篇随笔就数据结构——链表进⾏讲解。
链表是⼀种特别实⽤的数据结构,我把它理解为数组的升级版,也就是在数组的基础上,它能做到在任意位置添加或者删除元素,⽽不影响其他元素。
链表还是我们进⾏图论学习时,图的常⽤存储⽅式——邻接表(链式前向星)的实现基础。
学习链表需要读者具有⼀定的语法基础,最好会⼀点点指针。
(不会也没关系,我们主要讲解数组模拟链表)什么是链表链表,顾名思义,就是带链的表。
我已经说过,链表属于数组的加强版。
那我们可以借助数组来理解链表:如果说数组是⼀长排连在⼀起的“⽅块”的话,那么链表就是把这些⽅块“拉开“,每个⽅块还有两个箭头,分别指向这个⽅块前⾯的⽅块和后⾯的⽅块。
这样我们就可以理解,为什么链表可以⽀持随机插⼊和删除了。
从某种意义上来说,这⾥的每⼀个⽅块都是离散的,我们在某两点插⼊的时候,只需要把要插⼊的元素,这个元素⽬标位置前⾯的元素、后⾯的元素的箭头改⼀下,就做到了插⼊的操作。
删除同理。
链表的实现原理根据刚才的理解,我们可以发现,我们可以⽤⼀个结构体来模拟每⼀个⽅块,结构体中存⼀个元素和两个指针,指针分别指向上⼀个元素的位置和下⼀个元素的位置。
但是蒟蒻不会指针指针的实现⽐较⿇烦,⽽且在调试的时候也不是很理想。
所以我们来想指针的本质就是告诉你⼀个位置,那么针对于”加强数组“链表来讲,这个位置可以⽤什么来表⽰呢?对,数组下标。
所以我们刚才的结构体就可以简化,变成存⼀个元素和两个int变量(存储数组下标)。
这样,我们就可以⽤结构体数组模拟链表的实现。
链表基本操作的代码实现链表实现的精髓就是更改指针,改掉了三个元素(前,中,后)的指针使链表合法,就完成了我们需要做的操作,本部分不再就每段代码进⾏过多讲解,请⼤家⾃⾏理解代码含义,最好借助纸笔推演,看的会更明⽩⼀些。
(1)初始化我们初始化链表的时候,要根据题⽬意思处理开头的第⼀个元素,这很重要!并且,我们把所有的指针都清成-1,这样保证了链表初始绝对合法。
c++链表的详细讲解

c++链表的详细讲解在C++中,链表是一种常见的数据结构,用于存储有序的元素集合。
每个元素在链表中都有一个唯一的地址,并且通过指针链接在一起。
下面是对C++链表的详细讲解:1.定义链表节点:链表由节点组成,每个节点包含两个部分:数据和指向下一个节点的指针。
在C++中,我们可以定义一个结构体来表示节点,如下所示:cpp复制代码struct Node {int data; // 存储数据Node* next; // 指向下一个节点的指针};2.创建链表:要创建链表,我们需要创建一个指向链表第一个节点的头指针。
头指针可以指向链表的第一个节点或为空(表示空链表)。
以下是如何创建空链表的代码示例:cpp复制代码Node* head = nullptr;3.插入节点:要向链表中插入节点,我们需要遵循以下步骤:a. 创建一个新的节点。
b. 将数据存储在新节点中。
c. 将新节点的next指针指向当前链表的最后一个节点(或空节点)。
d. 如果链表为空,将头指针指向新节点。
否则,找到链表的最后一个节点,并将其next指针指向新节点。
以下是向链表中插入节点的代码示例:cpp复制代码Node* createNode(int data) {Node* newNode = new Node(); // 创建新节点newNode->data = data; // 将数据存储在新节点中newNode->next = nullptr; // 将新节点的next指针初始化为nullptrreturn newNode; // 返回新节点}void insertNode(Node* head, int data) {Node* newNode = createNode(data); // 创建新节点if (head == nullptr) { // 如果链表为空,将头指针指向新节点head = newNode;} else {Node* current = head; // 找到链表的最后一个节点while (current->next != nullptr) {current = current->next;}current->next = newNode; // 将最后一个节点的next指针指向新节点}}4.删除节点:要从链表中删除节点,我们需要遵循以下步骤:a. 找到要删除的节点。
数据结构--链表入门超详细解析(简单易懂纯原篇)

数据结构--链表⼊门超详细解析(简单易懂纯原篇)个⼈博客:链表的基础概念这⾥就不讲了,随便⼀个搜索引擎就能找到⽆数答案,我这⾥想讲点别⼈不会讲的东西,以及尝试如何让⼀窍不通于链表的同学快速理解和⼊门。
此处我们使⽤C进⾏演⽰⽂章⽬录结点的创建我们知道链表是由⼀个个结点串联⽽成的,⽽每个结点分为两块区域,⼀块是数据域,相当于数组中存储的那个数据;另⼀块是指针域,这⾥存放的是指向下⼀个结点的地址。
在链表遍历的过程中,我们根据前⼀个结点的指针域中的那个地址找到下⼀个结点,就这样⼀个接⼀个往下遍历,进⾏增删改查等⼀系列基础操作。
知道了结点的基础结构就可以来进⾏创建了。
typedef struct lint{// 数据域int score;// 指针域struct lint* next; // 因为next是指向结点的,因此也要是结点的类型}Lint;这⾥使⽤typedef是为了重命名,省的以后创建指针时还要连带着 struct lint*。
之后创建指针就可以直接Lint* p⽽不是struct lint* p链表初始化链表中有⼀个特殊的结点–头结点。
头结点的数据域⼀般不⽤来存放和其他节点同类的数据,它的诞⽣是为了在实际应⽤过程中存放⼀些数据(下⾯会演⽰到)。
我们先来进⾏⽆头结点的初始化。
Lint * initLint(){// 创建头指针Lint* p = NULL;// 创建⼀个临时指针初始化⾸元结点,并且⽤于移动和调整后续结点Lint* temp = (Lint*)malloc(sizeof(Lint));temp->score = 90;temp->next = NULL;// 头指针需要指向⾸元结点,这样才能定位这串链表的位置p = temp;// ⼿动创建10个元素for(int i = 0;i < 10;i++){// 从第⼆个结点开始创建Lint * a = (Lint*) malloc(sizeof(Lint));a->score = i + 91;a->next = NULL;// 将当前temp指向的结点的next指向下⼀个结点temp->next = a;// temp移到下⼀个结点。
数据结构课件单链表

删除链表中的节点需要遍历至指定位置,时间复杂度为 O(n)。
查找节点
在链表中查找一个节点需要遍历整个链表,时间复杂度为 O(n)。
空间复杂度
空间占用
单链表的空间占用主要取决于链表中的 节点数,因此空间复杂度为O(n)。
VS
内存分配
每个节点需要分配内存空间存储数据和指 针,因此内存分配的空间复杂度也为O(n) 。
需要根据数据元素顺 序进行遍历的场景, 如排序算法等。
需要频繁插入、删除 操作的场景,如动态 规划、图算法等。
02
单链表的实现
创建单链表
定义节点结构体
首先需要定义一个节点结构体,包含 数据域和指针域两个部分,数据域用 于存储数据,指针域用于指向下一个 节点。
初始化头节点
创建一个头节点,并将其指针域指向 NULL,表示单链表的起始位置。
05
单链表常见问题与解决方 案
循环链表
总结词
循环链表是一种特殊类型的单链表,其中尾节点的指针指向头节点,形成一个闭环。
详细描述
在循环链表中,由于尾节点的指针指向头节点,因此遍历链表时需要特别注意,以避免无限循环。常见的解决方 法是在遍历时记录已经访问过的节点,避免重复访问。
链表中的重复元素
总结词
链表中可能存在重复元素的问题,这会影响数据处理的正确性。
详细描述
为了解决这个问题,可以在插入节点时检查新元素是否已存在于链表中。如果存在,则不进行插入操 作。另外,也可以使用哈希表等数据结构来快速查找重复元素。
链表的排序
总结词
对链表进行排序是常见的需求,但链表的排 序算法通常比数组的排序算法复杂。
合并单链表
总结词
将两个已排序的单链表合并为一个新的已排序的单链表。
《C语言链表》课件

详细描述
删除链表中的节点需要找到要删除的节点,修改其前一个节点的指针,使其指向要删除节点的下一个 节点,然后将要删除节点的指针置为NULL。如果要删除的是头节点或尾节点,还需要对头指针或尾 指针进行相应的修改。
遍历链表
总结词
了解如何遍历链表中的所有节点
VS
详细描述
遍历链表需要从头节点开始,依次访问每 个节点,直到达到链表的尾部。在遍历过 程中,可以使用一个指针变量来指向当前 节点,每次循环将指针向后移动一个节点 ,即修改指针的next指针。
链表和循环链表的主要区别在于它们的最后一个节点指向的方向。在链表中,最后一个节点指向NULL; 而在循环链表中,最后一个节点指向第一个节点。循环链表具有更好的性能,但实现起来相对复杂一些 。
05
总结与展望
总结链表的重要性和应用场景
总结1
链表作为C语言中一种基本的数据结构,在计算机科学中 有着广泛的应用。通过学习链表,可以更好地理解数据 结构的基本概念,提高编程能力和解决实际问题的能力 。
详细描述
合并两个有序链表可以通过比较两个链表的 节点值来实现。从头节点开始比较,将较小 的节点添加到结果链表中,并将指针向后移 动。重复此过程直到其中一个链表为空。如 果还有剩余的节点,将其添加到结果链表的 末尾。这种方法的时间复杂度为O(n),其中
n为两个链表中节点的总数。
04
常见错误与注意事项
内存泄漏问题
内存泄漏定义
在C语言中,内存泄漏是指在使用动 态内存分配函数(如malloc、calloc 、realloc等)分配内存后,未能正确 释放这些内存,导致程序运行过程中 不断占用越来越多的内存,最终可能 导致程序崩溃或性能下降。
Python数据结构之链表详解

Python数据结构之链表详解⽬录0.学习⽬标1.线性表的链式存储结构1.1指针相关概念1.2指针结构1.3结点1.4结点类2.单链表的实现2.1单链表的初始化2.2获取单链表长度2.3读取指定位置元素2.4查找指定元素2.5在指定位置插⼊新元素2.6删除指定位置元素2.7其它⼀些有⽤的操作3.单链表应⽤3.1单链表应⽤⽰例3.2利⽤单链表基本操作实现复杂操作0. 学习⽬标在顺序存储⽅式中,根据数据元素的序号就可随机存取表中任何⼀个元素,但同时在插⼊和删除运算需要移动⼤量的元素,造成算法效率较低。
解决此缺陷的⼀个办法是:对线性表采⽤链式存储⽅式。
在链表存储⽅式中,在逻辑上相邻的数据元素在存储空间中不⼀定相邻,数据元素的逻辑次序是通过链表中指针链接实现的。
本节将介绍链式存储结构的特点以及各种基本操作的实现。
通过本节学习,应掌握以下内容:线性表的链式存储及实现⽅法链表基本操作的实现利⽤链表的基本操作实现复杂算法1. 线性表的链式存储结构链式存储结构⽤于存放线性表中的元素的存储单元在内存中可以是连续的,也可以是零散分布的。
由于线性表中各元素间存在着线性关系,为了表⽰元素间的这种线性关系,链式存储结构中不仅要存储线性表中的元素,还要存储表⽰元素之间逻辑关系的信息。
所以⽤链式存储结构表⽰线性表中的⼀个元素时⾄少需要两部分信息,除了存储每⼀个数据元素值以外,还需存储其后继或前驱元素所在内存的地址。
采⽤链式存储结构表⽰的线性表简称链表 (Linked List)。
1.1 指针相关概念在继续进⾏讲解前,我们⾸先来了解指针的相关概念,以便更好的理解链表。
假设我们需要处理⼀个⼤型数据⽂件,这⼀⽂件已经被读取保持在内存中,当我们在函数间传递⽂件时,并不会直接传递整个⽂件,我们需要创建变量来保存⽂件在内存中的位置,这些变量很⼩,很容易在不同的函数之间传递。
使⽤指针的好处之⼀就是可以⽤⼀个简单的内存地址就可以指向⼀个更⼤的内存地址段。
6.2.5链表 - 链表_教学课件[14页]
![6.2.5链表 - 链表_教学课件[14页]](https://img.taocdn.com/s3/m/861c3dcc6c175f0e7dd1376b.png)
DeleteDoc
Print_Stu_Doc
1 程序解析
struct stud_node{ int num;
/*链表结点类型*/
char name[20];
int score;
struct stud_node *next;
}; struct stud_node * Create_Stu_Doc(); /* 新建链表 */
例如:(int *) malloc(sizeof(int)) (struct student *) malloc(sizeof(struct student))
void free(void *ptr)
功能:释放由malloc()申请的动态内存空间,ptr存放该空 间的首地址。
返回值:无。 例如:free(p);
void Print_Stu_Doc(struct stud_node * head); /* 遍历 */
2 链表的概念
链表是一种常见而重要的动态存储分布的数 据结构。它由若干个同一结构类型的“结点” 依次串接而成。链表分单向链表和双向链表。
头指针 头结点
结点
尾结点
2 链表的概念
通常使用结构来定义单向链表结点的数据 类型:
3 单向链表的常用操作
1. 链表的建立 2. 链表的遍历 3. 插入结点 4. 删除结点
3 单向链表的常用操作
1. 链表的建立
3 单向链表的常用操作
1. 链表的建立
head = tail = NULL;
scanf("%d%s%d", &num,name, &score);
while(num != 0){
tail->next = p; tail = p;
数据结构教学课件:线性表链表

头结点好处
a、由于开始结点的位置被存放在头结点的指针域中, 所以在链表的第一个位置上的操作(如插入、删 除等)就和在表的其它位置上的操作一致,无需 进行特殊处理; b、无论链表是否为空,其头指针是指向头结点的非 空指针(空表中头结点的指针域为空),头指针 始终不为空,因此空表和非空表的处理也就统一 了
}
带头结点的单链表 H
# a1 a2
…
an NULL
头结点
H
# NULL
头结点
linklist * initList() linklist * initList() { { linknode* pHead = (linknode*) linknode* pHead = 0; // pHead、 malloc( sizeof(linknode)); pRail分别为头、尾指针 linknode* pRail = pHead; linknode* pRail = pHead; // pHead、 char c; pRail分别为头、尾指针 char c; while( (c = getchar()) != ‘\n’) { linknode* pNode = while( (c = getchar()) != ‘\n’) { (linknode*)malloc( sizeof(linknod linknode* pNode = e )); (linknode*)malloc( sizeof( linknode scanf( "%c", &pNode->data ); )); scanf( "%c", &pNode->data ); pNode->next = 0; //新结点指 针域为空 pNode->next = 0; //新结点指针 pRail->next = pNode; 域为空 pRail = node; if( !pHead) pHead = pNode; } else pRail->next = pNode; return pHead; pRail = node; } } return pHead; }
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构链表类定义
代码
链表类定义:将该类保存在文件LinkList.h中。
//链表类定义:将该类保存在文件LinkList.h中。
template <class T>
struct Node
{
T data;
Node<T> *next; //此处<T>也可以省略
};
template <class T>
class LinkList
{
public:
LinkList( )
{
first=new Node<T>;
first->next=NULL;
} //建立只有头结点的空链表
LinkList(T a[ ], int n); //建立有n个元素的单链表
~LinkList( ); //析构函数
int Length( ); //求单链表的长度
T Get(int i); //取单链表中第i个结点的元素值
int Locate(T x); //求单链表中值为x的元素序号
void Insert(int i, T x); //在单链表中第i个位置插入元素值为x的结点
T Delete(int i); //在单链表中删除第i个结点
void PrintList( ); //遍历单链表,按序号依次输出各元素private:
Node<T> *first; //单链表的头指针
};
template <class T>
LinkList<T>:: ~LinkList( )
{
Node<T> * p=first; //工作指针p初始化
while (p) //释放单链表的每一个结点的存储空间
{
Node<T> * q=p; //暂存被释放结点
p=p->next; //工作指针p指向被释放结点的下一个结点,使单链表不断开
delete q;
}
}
template <class T>
T LinkList<T>::Get(int i)
{
Node<T> *p; int j;
p=first->next; j=1; //或p=first; j=0;
while (p && j<i)
{
p=p->next; //工作指针p后移
j++;
}
if (!p) throw "位置";
else return p->data;
}
template <class T>
void LinkList<T>::Insert(int i, T x)
{
Node<T> *p; int j;
p=first ; j=0; //工作指针p初始化
while (p && j<i-1)
{
p=p->next; //工作指针p后移
j++;
}
if (!p) throw "位置";
else {
Node<T> *s;
s=new Node<T>;
s->data=x; //向内存申请一个结点s,其数据域为x
s->next=p->next; //将结点s插入到结点p之后
p->next=s;
}
}
template <class T>
T LinkList<T>::Delete(int i)
{
Node<T> *p; int j;
p=first ; j=0; //工作指针p初始化
while (p && j<i-1) //查找第i-1个结点
{
p=p->next;
j++;
}
if (!p || !p->next) throw "位置"; //结点p不存在或结点p的后继结点不存在
else {
Node<T> *q; int x;
q=p->next; x=q->data; //暂存被删结点
p->next=q->next; //摘链
delete q;
return x;
}
}
template <class T>
LinkList<T>:: LinkList(T a[ ], int n)
{
first=new Node<T>;
first->next=NULL; //初始化一个空链表
for (int i=0; i<n; i++)
{
Node<T> * s=new Node<T>;
s->data=a[i]; //为每个数组元素建立一个结点
s->next=first->next; //插入到头结点之后
first->next=s;
}
}
template <class T>
void LinkList<T>::PrintList( )
{
Node<T> *p;
p=first->next;
while (p)
{
cout<<p->data<<" ";
p=p->next;
}
}
#include <iostream.h>
#include"LinkList.h"
void main( )
{
int r[ ]={10,9,8,7,6,5,4,3,2,1};
LinkList <int> a( r , 10 );
cout<<"原表为:"<<endl;
a.PrintList();
cout<<endl;
a.Insert(1,-2); //执行插入操作;
a.Insert(2,-1);
a.Insert(3,0 );
cout<<"执行插入后输出为:"<<endl;
a.PrintList();
cout<<endl;
a.Delete(0);
a.Delete(-1);
a.Delete(-2);
cout<<"执行删除后输出为:"<<endl;
a.PrintList();
cout<<endl;
cout<<"按位查找第二个元素:"<<endl;
cout<<"第 5 个元素为: ";
cout<<a.Get(5)<<endl; //查找链表中第 5 个元素
cout<<endl; }。