【C语言】链表

【C语言】链表



前言

本篇文章将详细讲到有关链表的概念,链表节点,链表分类,链表的基本操作包括初始化链表,遍历链表,插入节点,删除节点,清空链表,销毁链表。


一、链表的基本概念

在这里插入图片描述

  • 链表是一种常用的数据结构,它通过指针将一些列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性(非顺序存储)。
  • 数据域用来存储数据,指针域用于建立与下一个结点的联系。
  • 建立链表时无需预先知道数据总量的,可以随机的分配空间,可以高效的在链表中的任意位置实时插入或删除数据。
  • 链表的开销,主要是访问顺序性和组织链的空间损失

链表与数组的区别

数组:一次性分配一块连续的存储区域。
优点:随机访问元素效率高
缺点:1) 需要分配一块连续的存储区域(很大区域,有可能分配失败)
2) 删除和插入某个元素效率低
链表:无需一次性分配一块连续的存储区域,只需分配n块节点存储区域,通过指针建立关系。
优点:1) 不需要一块连续的存储区域
2) 删除和插入某个元素效率高
缺点:随机访问元素效率低


二、链表节点

大家思考一下,我们说链表是由一系列的节点组成,那么如何表示一个包含了数据域和指针域的节点呢?
链表的节点类型实际上是结构体变量,此结构体包含数据域和指针域:
数据域用来存储数据
指针域用于建立与下一个结点的联系,当此节点为尾节点时,指针域的值为NULL;

typedef struct Node 
{
	//数据域
	int id;
	char name[50];

	//指针域
	struct Node *next;       
}Node;

在这里插入图片描述


三、链表的分类

链表分为:静态链表和动态链表
静态链表和动态链表是线性表链式存储结构的两种不同的表示方式:

  • 所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“静态链表”。
  • 所谓动态链表,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。

静态链表

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//节点的结构体
struct LinkNode
{
	int num;//数据域
	struct LinkNode* next;//指针域
};

void test01()
{
	//创建节点
	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\n", pCurrent->num);
		pCurrent = pCurrent->next;
	}
}


int main()
{
	test01();

	return 0;
}

动态链表

struct LinkNode
{
	int num;
	struct LinkNode* next;
};


void test01()
{
	//创建节点
	struct LinkNode* node1 = malloc(sizeof(struct LinkNode));
	struct LinkNode* node2 = malloc(sizeof(struct LinkNode));
	struct LinkNode* node3 = malloc(sizeof(struct LinkNode));
	struct LinkNode* node4 = malloc(sizeof(struct LinkNode));
	struct LinkNode* node5 = malloc(sizeof(struct LinkNode));

	//给数据域赋值
	node1->num = 100;
	node2->num = 200;
	node3->num = 300;
	node4->num = 400;
	node5->num = 500;

	//建立关系
	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = node5;
	node5->next = NULL;

	//遍历链表
	struct LinkNode* pCurrent = node1;
	while (pCurrent != NULL)
	{
		printf("%d\n", pCurrent->num);
		pCurrent = pCurrent->next;
	}

	free(node1);
	free(node2);
	free(node3);
	free(node4);
	free(node5);
	node1 = NULL;
	node2 = NULL;
	node3 = NULL;
	node4 = NULL;
	node5 = NULL;
}


int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

带头和不带头链表

带头链表:固定一个节点作为头结点(数据域不保存有效数据),起一个标志位的作用,以后不管链表节点如果改变,此头结点固定不变。
在这里插入图片描述
不带头链表:头结点不固定,根据实际需要变换头结点(如在原来头结点前插入新节点,然后,新节点重新作为链表的头结点)。
在这里插入图片描述

单向链表、双向链表、循环链表

  • 单向链表
    -

  • 双向链表
    在这里插入图片描述

  • 循环链表
    在这里插入图片描述


四、链表的基本操作

  • 初始化链表
  • 遍历链表
  • 插入节点
  • 删除节点
  • 清空链表
  • 销毁链表

linkList.h

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct LinkNode
{
	int num;
	struct LinkNode* next;
};

//初始化链表
struct LinkNode* initLinkList();

//遍历链表
void foreach_LinkList(struct LinkNode* pHeader);

//插入链表
void insert_LinkList(struct LinkNode* pHeader, int oldVal, int newVal);

//删除链表
void delete_LinkList(struct LinkNode* pHeader, int val);

//清空链表
void clear_LinkList(struct LinkNode* pHeader);

//销毁链表
void destroy_LinkList(struct LinkNode* pHeader);

linkList.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include "linkList.h"


//初始化链表
struct LinkNode* initLinkList()
{
	//创建头节点
	struct LinkNode* pHeader = malloc(sizeof(struct LinkNode));

	if (pHeader == NULL)
	{
		return NULL;
	}

	//初始化头节点
	//pHeader->num = -1;  //头节点 不维护数据域
	pHeader->next = NULL;

	//记录尾节点位置,方便插入新的数据
	struct LinkNode* pTail = pHeader;
	int val = -1;
	while (1)
	{
		//让用户初始化几个节点,如果用户输入的是-1,代表插入结束
		printf("请初始化链表,如果输入-1代表结束\n");
		scanf("%d", &val);

		if (val == -1)
		{
			break;
		}

		//如果输入不是-1  插入节点到链表中
		struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
		newNode->num = val;
		newNode->next = NULL;

		//更改指针的指向
		pTail->next = newNode;
		//更新新的尾节点的指向
		pTail = newNode;

	}


	return pHeader;
}

//遍历链表
void foreach_LinkList(struct LinkNode* pHeader)
{
	if (pHeader == NULL)
	{
		return;
	}

	struct LinkNode* pCurrent = pHeader->next; //指定第一个有真实数据的节点

	while (pCurrent != NULL)
	{
		printf("%d\n", pCurrent->num);
		pCurrent = pCurrent->next;
	}

}

//插入链表
void insert_LinkList(struct LinkNode* pHeader, int oldVal, int newVal)
{
	if (pHeader == NULL)
	{
		return;
	}

	//创建两个临时的节点
	struct LinkNode* pPrve = pHeader;
	struct LinkNode* pCurrent = pHeader->next;

	while (pCurrent != NULL)
	{
		if (pCurrent->num == oldVal)
		{
			break;
		}
		//如果没找到对应的位置,辅助指针向后移动
		pPrve = pCurrent;
		pCurrent = pCurrent->next;
	}

	//创建新节点
	struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
	newNode->num = newVal;
	newNode->next = NULL;


	//建立关系
	newNode->next = pCurrent;
	pPrve->next = newNode;


}


//删除链表
void delete_LinkList(struct LinkNode* pHeader, int val)
{
	if (pHeader == NULL)
	{
		return;
	}

	//创建两个辅助指针变量
	struct LinkNode* pPrev = pHeader;
	struct LinkNode* pCurrent = pHeader->next;

	while (pCurrent != NULL)
	{
		if (pCurrent->num == val)
		{
			break;
		}
		//没有找到数据,辅助指针向后移动
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	}

	if (pCurrent == NULL) //没有找到用户要删除的数据
	{
		return;
	}

	//更改指针的指向进行删除
	pPrev->next = pCurrent->next;

	//删除掉待删除的节点
	free(pCurrent);
	pCurrent = NULL;

}


//清空链表
void clear_LinkList(struct LinkNode* pHeader)
{
	if (pHeader == NULL)
	{
		return;
	}

	struct LinkNode* pCurrent = pHeader->next;

	while (pCurrent != NULL)
	{
		//先保存住下一个节点的位置
		struct LinkNode* nextNode = pCurrent->next;

		free(pCurrent);

		pCurrent = nextNode;
	}

	pHeader->next = NULL;

}

//销毁链表
void destroy_LinkList(struct LinkNode* pHeader)
{

	if (pHeader == NULL)
	{
		return;
	}

	//先清空链表
	clear_LinkList(pHeader);

	//再释放头节点

	free(pHeader);
	pHeader = NULL;

}

链表基本操作.c

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "linkList.h"

void test01()
{
	//初始化链表
	struct LinkNode* pHeader = initLinkList();

	//遍历链表
	printf("遍历链表结果为:\n");
	foreach_LinkList(pHeader);

	//插入链表
	// 10 1000  2000 20 3000 30  500
	insert_LinkList(pHeader, 20, 1000);
	insert_LinkList(pHeader, 20, 2000);
	insert_LinkList(pHeader, -1, 500);
	insert_LinkList(pHeader, 30, 3000);
	printf("插入链表后,遍历链表结果为:\n");
	foreach_LinkList(pHeader);


	//删除链表
	// 10  20  30  500
	delete_LinkList(pHeader, 2000);
	delete_LinkList(pHeader, 3000);
	delete_LinkList(pHeader, 1000);
	delete_LinkList(pHeader, -1);
	printf("删除链表后,遍历链表结果为:\n");
	foreach_LinkList(pHeader);

	//清空链表
	clear_LinkList(pHeader);
	printf("清空链表后,遍历链表结果为:\n");
	insert_LinkList(pHeader, 111, 111);
	insert_LinkList(pHeader, 222, 222);
	insert_LinkList(pHeader, 333, 333);
	foreach_LinkList(pHeader);

	//销毁链表
	destroy_LinkList(pHeader);
	pHeader = NULL;

}

int main() {

	test01();

	//printf("%d\n", test01);

	system("pause");
	return EXIT_SUCCESS;
}

总结

到这里这篇文章的内容就结束了,谢谢大家的观看,如果有好的建议可以留言喔,谢谢大家啦!

评论 46
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值