链表的基础知识

链表的基础知识


链表的基础概念


链表概念:

  • 链表是一种常用的数据结构,它通过指针将一些列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性(非顺序储存)。

  • 一个结点分为数据域和指针域,数据域用来储存数据,指针域用于建立与下一个结点的联系。链表在内存中是非连续的。
    请添加图片描述

  • 建立链表时无需预先知到数据总量的,可以随机的分配空间,可以高效的在链表中实现灵活的内存动态管理。

链表与数组的不同:

  • 操作上,如果数组在指定位置插入和删除会导致元素大量移动,耗时低效;而链表在指定位置插入和删除不需要移动元素,只需要修改指针即可。但是链表在查找数据时没有数组方便,查找效率相对于数组低。
  • 内存上,链表相对于数组来讲,多了指针域空间开销。

链表分类:

  • 1、静态链表 动态链表
    静态链表和动态链表是线性表链式存储结构的两种不同的表示方式:
    • 所有结点都是在程序中定义的,不是临时开群的,也不能用完后释放 ,这种链表 称为"静态链表"
    •所谓动态链表,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,井建立起前后相链的关系。

  • 2、单向链表 双向链表 循环链表 单向循环链表 双向循环链表(下面的讲解用单向链表为例)

静态链表


链表节点类型定义:

#include <stdio.h>
//#include <string.h>
//#include <stdlib.h>

struct LinkNode
{
	int data;//数据域
	struct LinkNode* next;//指针域
};
void test()
{
	struct LinkNode node1 = { 10,NULL };
	struct LinkNode node2 = { 20,NULL };
	struct LinkNode node3 = { 30,NULL }; 
	struct LinkNode node4 = { 40,NULL };
	struct LinkNode node5 = { 50,NULL };

	node1.next = &node2;
	node2.next = &node3;
	node3.next = &node4;
	node4.next = &node5;//将结点串起
}
int main()
{
    test();
	return 0;
}

链表的初始化与遍历


如何实现链表遍历?
链表的结点是通过指针来访问的,所以我们可以从指针下手:
我们考虑先创建一个指针struct LinkNode *pcurrent = &node1,通过指针访问当前链节,再修改指针访问下一链节。

  • 静态链表的遍历:
#include <stdio.h>
//#include <string.h>
//#include <stdlib.h>

struct LinkNode
{
	int data;
	struct LinkNode* next;
};
void test()
{
	struct LinkNode node1 = { 10,NULL };
	struct LinkNode node2 = { 20,NULL };
	struct LinkNode node3 = { 30,NULL }; 
	struct LinkNode node4 = { 40,NULL };
	struct LinkNode node5 = { 50,NULL };

	node1.next = &node2;
	node2.next = &node3;
	node3.next = &node4;
	node4.next = &node5;//将结点串起

	//遍历链表
	//先定义一个辅助指针变量
	struct LinkNode* pCurrent = &node1;
	while (pCurrent != NULL)
	{
		printf("%d ", pCurrent->data);
		//指针移动到下一个元素的首地址
		pCurrent = pCurrent->next;
	}
}
int main()
{
	test();
	return 0;
}
  • 动态链表的初始化:
    代码和注释:
#include <stdio.h>
//#include <stdlib.h>
#include <stdbool.h>
struct LinkNode* Init_LinkList()//头结点形式
{
	//创建头结点
	struct LinkNode* header = malloc(sizeof(struct LinkNode));//malloc可以实现动态内存分配
	header->data = -1;
	header->next = NULL;//头结点一般定义为空
	//尾部指针
	struct LinkNode* pRear = header;//先指向头结点

	int val = -1;
	while (true)
	{
		printf("请输入插入的数据:>\n");
		scanf("%d", &val);
		if (-1 == val)
		{
			break;
		}
		//先创建新结点
		struct LinkNode* newnode = malloc(sizeof(struct LinkNode));
		newnode->data = val;
		newnode->next = NULL;

		//新节点插入到链表中
		//利用pRear将新结点与前一个结点联系
		pRear->next = newnode;
		//更新尾部指针pRear,将pRear指针往后移
		pRear = newnode;
	}
	return header;//返回头结点的地址可以找到这个链表的头从而找到整个链表
};
  • 动态链表的遍历:
    代码和注释:
void Foreach_Linklist(struct LinkNode* header)
{
	if (NULL == header)
	{
		return;
	}
	//辅助指针变量
	struct LinkNode* pcurrent = header->next;//因为头结点一般不储存数据,所以让指针变量指向头结点的下一节点
	while (pcurrent != NULL)
	{
		printf("%d ", pcurrent->data);
		pcurrent = pcurrent->next;//指针后移
	}
}

动态链表的插入、清空、删除、销毁


  • 链表的插入
    考虑创建两个辅助指针,pCurrent指针记录oldval,pPrev指针记录oldval前一个节点。将新的结点插入pCurrent和oldval两个指针之间
    代码和注释如下:
void InsertByValue_Linklist(struct LinkNode* header, int oldval, int newval)
{
	if (NULL == header)
	{
		return;
	}
	//创建两个辅助指针变量
	struct LinkNode* pCurrent = header->next;
	struct LinkNode* pPrev = header;
	while (pCurrent != NULL)
	{
		if (pCurrent->data == oldval)
		{
			break;
		}
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	}
	//如果pCurrent为NULL说明链表中不存在值为oldval的结点

	/*if (pCurrent == NULL)//将该代码注释掉后如果链表中不存在值为oldval的结点,则将newval插入到最后
	{
		return;
	}*/

	//创建新节点
	struct LinkNode* newnode = malloc(sizeof(struct LinkNode));
	newnode->data = newval;//将数据赋给新结点
	newnode->next = NULL;//先初始化为NULL

	//新结点插入链表中
	newnode->next = pCurrent;
	pPrev->next = newnode;

}
  • 链表的清空(只剩下头结点,其余部分释放掉)
    代码和注释如下:
void Clear_LinkList(struct LinkNode* header)
{
	if (NULL == header)
	{
		return;
	}
	struct LinkNode* pCurrent = header->next;//因为头结点不释放,所以将pPcurrent指向下一节点
	//但是释放前还要保存pPcurrent的指针域,所以还要再创建一个辅助指针
	if (pCurrent != NULL)
	{
		//先保存当前结点的下一结点地址
		struct LinkNode* pNext = pCurrent->next;
		//释放当前结点内存
		free(pCurrent);
		pCurrent = pNext;//pCurrent指针往后移
	}
	header->next = NULL;
}
  • 链表的删除(删除某个结点)
void RemoveByValue_Linklist(struct LinkNode* header, int delValue)
{
	if (NULL == header)
	{
		return;
	}
	//创建两个辅助指针
	struct LinkNode* pPrev = header;
	struct LinkNode* pCurrent = header->next;
	while (pCurrent != NULL)
	{
		if (pCurrent->data == delValue)
		{
			break;
		}
		//移动两个辅助指针
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	}
	if (pCurrent == NULL)//没找到delValue
	{
		return;
	}
	//重新建立待删除结点的前驱和后继结点关系
	pPrev->next = pCurrent->next;
	//释放删除结点内存
	free(pCurrent);
	pCurrent = NULL;
}
  • 链表的销毁(全部释放掉,包括头结点)
void Destroy_Linkrist(struct LinkNode* header)//连头结点都销毁
{
	if (NULL == header)
	{
		return;
	}
	//创建两个辅助指针
	struct LinkNode* pCurrent = header;//与链表的清空区别在这里
	while (pCurrent != NULL)
	{
		struct LinkNode* pNext = pCurrent->next;
		//释放当前结点内存
		free(pCurrent);
		pCurrent = pNext;//pCurrent指针往后移
	}
}

最后感谢大家的支持!!!
也祝大家圣诞快乐!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱写代码的刚子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值