数据结构-单链表

前言`

1.链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表是线性表的一种,由一个一个的节点组成,每个节点都是一个数据结构,链表的节点通常是动态分配,使用和删除的。
2.链表的常见用途:
1.用于实现其它高效的数据结构,如栈、队列、哈希表。链表插入和删除操作都非常高效。如果在知道需要操作的对象时,链表的增加和删除效率都是O(1),而数组则需要进行大量的数据搬移。
2管理动态数据:链表是一种动态数据结构,可以自由添加和删除节点,因此它经常用于管理动态大小的数据集合,例如文件系统、操作系统内存管理和网络协议。
3.排序:例如归并排序和快速排序,这些算法通常需要在运行时动态创建和删除结点。

1.单链表

链表是由数据域和指针域来构成的,数据域用来存储元素值,指针域用来存储结点地址。
每个结点只有一个指针域,则称为单链表。

1.1 单链表的分类

1.带头节点的单链表
非空链表:它有一个头指针,指向头节点,头节点之后是首元节点
在这里插入图片描述
2.空表:头指针指向头节点,但是头节点的指针域为空,后面没有元素。
在这里插入图片描述

1.2 单链表的存储结构

单链表的每个节点都由数据域和指针域构成,则可以使用struct类型来构造链表。
在这里插入图片描述

2 单链表的基本操作

2.1 空链表的初始化

1.这个相当于构造一个空表。
步骤1:在内存中分配一块空间,作为头节点,然后用头指针指向头节点。
步骤2:将头节点的指针域置空即可。
2.算法描述

typedef struct Lnode
{
	Elemtype data;
	struct Lnode *next;
}LNode,*LinkList;
Status InitList(LinkList &L)
{
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;
	return OK;
}

2.2 如何判断链表为空

空表:链表中无元素,称为空链表,但是头指针和头节点仍然存在。
算法思路:判断头节点指针域是否为空,为空则为空表。

int ListEmpty(LinkList L)
{
	if (L->next) //非空
		return 0;
	else
		return 1}

2.3 销毁单链表

链表销毁后不存在。
1算法思路:从头节点开始,依次释放所有节点。
1.1定义指针p,用来操作当前的节点。首先让P指向头节点,即P = L;
在这里插入图片描述
1.2.在释放P指针之前,需要将L指针指向下一个节点(L = L->next;),不然直接释放p节点,会导致L节点也没了。
在这里插入图片描述
1.3.删除第一个节点之后,将p移动到L的位置,即第二个节点,然后再将L移动到下一个节点,然后释放p,删除第二个节点,重复这三步,一直到最后一个节点结束,即L为空节点结束。
在这里插入图片描述
2.算法描述

Status DestroyList_L(LinkList &L)
{
	LiknList p;
	while (L)
	{
		p = L;
		L = L->	next;
		free(p);
	}
}	

2.4 清空单链表

清空链表不同于销毁链表,清空链表是把链表的元素清除,头指针和头节点还在。
算法思路:从头节点开始,依次释放所有节点,并将头节点指针域置空.
1.首先让p指向首元节点
在这里插入图片描述
2.在删除p节点之前,需要把p节点的下一个节点给存起来,方便能够删除下一个节点,所以引入了指针变量q。然后再删除p节点
在这里插入图片描述
3.删除p之后,再把p指向q,则p又指向了新的需要清空的节点,然后再更新节点q,然后就可以删除节点p。
在这里插入图片描述
4.当p的指针域为空时,则链表元素已清空,则需要将头结点的next指针域置空,即L->next = NULL;

Status ClearList(LinkList &L)
{
	Lnode *p,*q;
	p = L->next;
	whiel (p)
	{
		q = p->next;
		free(p);
		p = q;
	}
	L->next = NULL; 
	return OK;
}

2.4 单链表的表长

算法思路:
从首元节点开始,依次计数所有节点。
在这里插入图片描述
算法描述:

int ListLength_L(LinkList L)
{
	LinkList p;
	p = L->next;
	i = 0;
	while (p)
	{
		i++;
		p = p->next;
	}
	return i;
}

2.5 取单链表第i个元素的值

算法思路:从链表的头指针出发,顺着节点往下搜索,直到搜索到第i个节点为止。所有,链表不是随机存取结构
算法描述:

Status GetElem_L(LinkList L,int i,ElemType &e)
{
	p = L->next;
	j = 1;
	while (p && j < i) //p不为空且j小于i,则继续循环
	{
		p = p->next;
		++j;
	}
	if (!p || j > i)
		return ERROR;
	e = p->data;
	return OK;
}

2.6 按值查找

根据指定的数据获取该数据的位置
例子1:假如查找的数值位30和15
在这里插入图片描述
算法思路:
1.从第一个节点起,依次和e相比较。
2.如果找到一个其值与e相等的元素,则返回链表的位置或地址
3.如果整个链表都没找到,则返回0或空
算法描述:

Lnode *LocateElem(LinkList L,Elemtype e)
{
	p = L->next;
	while (p && p->date != e)
	{
		p = p->next;
	}
	return p;
}

例子2:返回指定数据的位置序号
算法描述:

int LocateElem(LinkList L,Elemtype e)
{
	p = L->next; j =1;
	while (p && p->data != e)
	{
		p = p->next;
		j++;
	}
	if (p)
		return j;
	else
		return 0;
}

2.7 插入节点

在第i个节点前插入值为e的新节点
算法思路:首先找到第i-1个节点位置,设为p,然后创建新节点,然后将新节点的next指针域指向第i个节点,再将p节点的next指针域指向新节点。
算法描述:

Status ListInsert(LinkList &L,int i,ElemType e)
{
	p = L;j = 0;
	while (p && j < i - 1) //寻找第i-1个节点
	{
		p = p->next;
		++j;
	}
	if (!p || j > i - 1) //没找到
		return ERROR;
	s = (LinkList*)malloc(sizeof(LNode));
	s ->data = e;
	//插入节点,并且这两行不能互换
	s->next = p->next; 
	p->next = s;
}

2.8删除第i个节点

算法思路:
首先找个第i - 1个节点,将第i -1个节点存储在指针p中,并保存第i个节点的值,因为需要将第i-1节点和第i+1节点链接起来,但第i+1节点的地址保存在第i个节点中。
然后将第i-1个节点的next指针指向第i+1个节点,即**p->next = p->next->next;**再将第i个节点释放掉就行了。
算法描述:

Status ListDelete(LinkList &L,int i,ElemType &e)
{
	p = L;j = 0;
	q = (LinkList*)malloc(sizeof(LNode));
	while (p->next && j < i -1) //寻找第i-1个节点
	{
		p = p->next;
		++j;
	}
	if (!(p->next) || j > i -1) //删除的位置不合理
		return ERROR;
	q = p->next; 
	p->next = q->next; //将第i-1个节点和第i+1个节点链接起来
	e = q->data;
	free(q); //释放第i个节点
	return OK;
}

2.9 查找、插入、删除算法时间效率分析

我们先来看查找

Lnode *LocateElem(LinkList L,Elemtype e)
{
	p = L->next;
	while (p && p->date != e)
	{
		p = p->next;
	}
	return p;
}

可以看出,查找的效率取决于要查找的位置,最坏的情况整个链表都要找一遍,即时间复杂度为O(n)
插入和删除的效率:
插入和删除并不需要移动链表的位置,只需要改变指针指向。所以其时间复杂度为O(1),但由于插入和删除之前,需要查找链表位置,所以它的复杂度依然为O(n)

2.10 头插

顾名思义,头插就是在链表头部插入元素
在这里插入图片描述
算法思路:
1.先创建一个头结点,让头结点的指针域为空
2.在内存中创建一个新结点,用于存放数据
3.将新结点的next指针域指向头结点的next指针域,头结点的next指针域指向新结点
4.重复2,3步骤
算法描述:

void CreateList(LinkList &L,int n)
{
	L = (LinkList*)malloc(sizeof(LNode));
	L->next  = NULL;
	for (i = n;i > 0;--i)
	{
		p = (LinkList*)malloc(sizeof(LNode));
		scanf(&p->data);
		p->next = L->next; //插入到表头
		L->next = p;
	}
}

2.11尾插

元素插在链表尾部。
算法思路:
1.有一个空表L和尾指针r,尾指针r指向链表的尾节点
2.L和r一同指向头节点,新的节点插入到尾节点,r指向新节点。
算法描述:

void CreatList(LinkList &L,int n)
{
	L = (LinkList*)malloc(sizeof(LNode));
	L->next = NULL;
	LinkList *r = L; //尾指针指向头节点
	for (i = 0;i < n;i++)
	{
		p = (LinkList*)malloc(sizeof(LNode));
		scanf("%d",&(p->data));
		p->next = NULL;
		r->next = p;
		r = p;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值