线性表操作的实现--单链表(链式存储结构)

news/2025/2/23 6:03:58

本文参考朱战力老师的数据结构算法--使用C语言一书

目录

文章目录

前言

一、链表是什么?

二、具体实现

1.单链表的定义

2.初始化ListInitiate(SLNode **head)

3.求当前元素的个数ListLength(SLNode *head)

4.插入ListInsert(SLNode *head,int i,DataType x)

5.删除ListDelete(SLNode *head,int i,DataType *x)

6.取元素ListGet(SLNode *head,int i,DataType *x)

 7.撤销单链表Destroy(SLNode **head)

  8.链表排序ListSort(SLNode* head)

  9.链表查找ListSearch(SLNode* head, DataType target)

  10.小试牛刀

总结


前言

        本文所介绍的内容为数据结构算法的基础内容--顺序表操作的实现,笔者通过不断的深入学习发现,后续的数据结构中,包括堆栈、队列、串、数组、表、数和二叉树无不是通过线性表去实现,故很好的理解线性表的操作对于广大的读者来说非常的有必要!


一、链表是什么?

        本篇文章讨论线性表的链式存储结构和链式存储结构下操作的实现。链式存储结构存储线性表元素的方法是,把存储有元素的结点用指针域构造成链。指针是指向物理内存单元地址的变量。我们把一个由元素域及一个或若干个指针域组成的结构体称为一个结点。其中,数据域用来存放元素,指针域用来构造元素之间的关联关系。链式存储结构的特点是,元素间的逻辑关系表现在结点的链接关系上。链式存储结构的线性表称为链表。根据指针域的不同和结点构造链的方法不同,链表主要有单链表、循环单链表和双向循环链表三种。其中单链表是最经常使用的链表

二、具体实现

1.单链表的定义

代码如下(示例):

typedef struct Node {
    DataType data;
    struct Node* next;
}SLNode;

2.初始化ListInitiate(SLNode **head)

代码如下(示例):

void ListInitiate(SLNode** head) {	//初始化
	*head = (SLNode*)malloc(sizeof(SLNode));	//申请头结点,由head指示其地址
	(*head)->next = NULL;	//置结束标志NULL
}

说明:在初始化操作前,头指针参数head没有具体的地址值。在初始化操作时,头指针参数head才得到了具体的地址值,而这个地址值要返回给调用函数,所以,此时头指针参数head要设计成双重指针(指针的指针)类型。如果此时头指针参数head设计成指针类型,那么调用函数将无法得到在初始化函数中被赋值的头指针参数head的值。

3.求当前元素的个数ListLength(SLNode *head)

代码如下(示例):

int ListLength(SLNode* head) {
	SLNode* p = head;	//p指向头结点
	int size = 0;	//size初始为0

	while (p->next != NULL) {	//循环计数
		p = p->next;
		size++;
	}
	return size;
}

算法实现过程:

4.插入ListInsert(SLNode *head,int i,DataType x)

代码如下(示例):

int ListInsert(SLNode* head, int i, DataType x) {
	//在带头结点的单链表head的第i给结点前插入一个存放元素x的结点
	//插入成功则返回1,失败则返回0
	SLNode* p, * q;
	int j;
	p = head;
	j = -1;
	while (p->next != NULL && j < i - 1) {
		//最终让指针p指向第i-1个结点
		p = p->next;
		j++;
	}

	if (j != i - 1) {
		printf("插入元素位置参数错!");
		return 0;
	}

	q = (SLNode*)malloc(sizeof(SLNode));	//生成新结点
	q->data = x;							//新结点数据域赋值

	q->next = p->next;						//插入步骤1
	p->next = q;							//插入步骤2
	return 1;
}

算法实现过程:

5.删除ListDelete(SLNode *head,int i,DataType *x)

代码如下(示例):

int ListDelete(SLNode* head, int i, DataType* x) {
	//删除带头结点单链表head的第i(0~size-1)个结点
	//被删除结点的数据域值由x带回,删除成功则返回1,失败则返回0
	SLNode* p, * s;
	int j;
	p = head;
	j = -1;

	while (p->next != NULL && p->next->next != NULL && j < i - 1) {
		//循环结束时指针p指向第i-1个结点
		p = p->next;
		j++;
	}

	if (j != i - 1) {
		printf("删除元素位置参数错!");
		return 0;
	}

	s = p->next;		//指针s指向第i个结点
	*x = s->data;		//把指针s所指结点的数据域值赋予x
	p->next = p->next->next;		//删除
	free(s);			//释放指针s所指结点的内存空间
	return 1;
}

算法实现过程:

6.取元素ListGet(SLNode *head,int i,DataType *x)

代码如下(示例):

int ListGet(SLNode* head, int i, DataType* x) {
	SLNode* p;
	int j;

	p = head;
	j = -1;
	while (p->next != NULL && j < i) {
		p = p->next;
		j++;
	}

	if (j != i) {
		printf("取元素位置参数错!");
		return 0;
	}

	*x = p->data;
	return 1;
}

 7.撤销单链表Destroy(SLNode **head)

代码如下(示例):

void Destroy(SLNode** head) {
	SLNode* p, * p1;

	p = *head;
	while (p != NULL) {
		p1 = p;
		p = p->next;
		free(p1);
	}
	*head = NULL;
}

  8.链表排序ListSort(SLNode* head)

代码如下(示例):

void ListSort(SLNode* head) {
	if (head == NULL || head->next == NULL) {
		return;		//空链表或只有一个结点,无需进行排序
	}

	SLNode* p, * q;
	DataType temp;
	int len = ListLength(head);

	for (int i = 0; i < len - 1; i++)
	{
		p = head->next;
		q = head->next->next;

		for (int j = 0; j < len - 1 - i; j++)
		{
			if (p->data > q->data)
			{
				temp = p->data;
				p->data = q->data;
				q->data = temp;
			}
			p = p->next;
			q = q->next;
		}
	}
}

  9.链表查找ListSearch(SLNode* head, DataType target)

代码如下(示例):

int ListSearch(SLNode* head, DataType target) {
	if (head == NULL)
	{
		return -1;	//空链表,查找失败
	}

	SLNode* p = head->next;
	int index = 0;

	while (p != NULL)
	{
		if (p->data == target)
		{
			return index;	//找到目标值,返回索引
		}
		p = p->next;
		index++;
	}
	return -1;
}

  10.小试牛刀

代码如下(示例):

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>	//包含printf()
#include<malloc.h>	//包含malloc()等函数
typedef int DataType;	//定义DataType为int
#include"LinList.h"	//包含LinList.h头文件
void main() {
	SLNode* head;	//定义头指针变量
	int i, x;

	ListInitiate(&head);	//初始化
	for (i = 0; i < 10; i++)	//插入10个元素
	{
		ListInsert(head, i, i + 1);
	}
	ListDelete(head, 3, &x);	//删除元素4
	for (i = 0; i < ListLength(head); i++) {	//显示当前的元素
		ListGet(head, i, &x);	//取元素
		printf("%d	", x);		//显示
	}

	// 测试排序函数
	printf("\n\n排序后的链表:\n");
	ListSort(head);
	for (i = 0; i < ListLength(head); i++) {	//显示当前的元素
		ListGet(head, i, &x);	//取元素
		printf("%d	", x);		//显示
	}

	// 测试查找函数
	int target = 7;
	int index = ListSearch(head, target);
	if (index != -1) {
		printf("\n\n目标值 %d 在链表中的索引位置为 %d\n", target, index);
	}
	else {
		printf("\n\n目标值 %d 不在链表中\n", target);
	}

	Destroy(&head);				//撤销单链表
}

运行结果:


总结

        

        单链表是一种常见的数据结构,它由节点组成,每个节点包含数据和指向下一个节点的指针。在本文中,我们实现了单链表的一些基本操作,包括插入、删除、排序和查找等。

        通过插入操作,我们可以在链表中添加新的节点。删除操作可以帮助我们删除指定节点。排序操作可以根据特定的规则对链表进行排序,使得节点按照一定的顺序排列。查找操作可以在链表中寻找特定的节点,并返回其位置或者相关信息。

        这些操作的实现需要注意指针的运用,以确保链表的结构正确。在排序和查找过程中,需要考虑不同的算法和复杂度,选择合适的方法来提高效率。

        总的来说,通过实现这些操作,我们可以灵活地对单链表进行操作,实现了对节点的添加、删除、排序和查找等功能。熟练掌握这些操作为我们日后学习哈希表和二叉树,都打下了非常坚实的基础。


http://www.niftyadmin.cn/n/5120172.html

相关文章

Python 算法高级篇:分治算法的原理与应用

Python 算法高级篇&#xff1a;分治算法的原理与应用 1. 什么是分治算法&#xff1f;2. 分治算法的应用2.1 归并排序2.2 快速排序2.3 最大子数组问题2.4 汉诺塔问题 3. 代码示例3.1 分治算法求幂 4. 总结 分治算法是一种重要的算法设计技巧&#xff0c;它将一个大问题分解为多个…

SYS/BIOS 开发教程: 创建自定义平台

目录 SYS/BIOS 开发教程: 创建自定义平台创建自定义平台新建工程并指定自定义平台修改现有工程使用自定义平台 参考: TI SYS/BIOS v6.35 Real-time Operating System User’s Guide 6.2节 本示例基于 EVMC6678L 开发板, 创建自定义平台, 并将代码段的位置指定到C6678器件内部的…

使用vue3 搭建一个H5手机端访问的项目

首先说明&#xff0c;我本地之前运行过vue的项目&#xff0c;所以具有一些基础的运行环境&#xff0c;这里直接按步骤讲我项目框架搭建的过程。 这个不建议使用驼峰&#xff0c;按规范单词中间加横杠就可以。一般会出现选择项&#xff0c;按方向键选择&#xff0c;我这边选择了…

【PG】PostgreSQL 模式(Schema)

一个PostgreSQL数据库集群中包含一个或更多的数据库。 角色和一些其他对象类型被整个集群共享&#xff0c;连接到服务器的客户端只能访问单个数据库中的数据&#xff0c;在连接请求中指定的那一个。 一个数据库包含一个或多个模式&#xff0c;模式中包含着表。模式还包含其他类…

flutter 使用FlutterJsonBeanFactory工具遇到的问题

如下图&#xff0c;使用FlutterJsonBeanFactory工具生成的数据类 但是其中 生成的 import package:null/&#xff0c;导致的错误&#xff1a;Target of URI doesn’t exist: ‘package:null/generated/json/asd.g.dart’ 尝试过的方法&#xff1a; 手动添加包名&#xff0c;…

Redis基于布隆过滤器解决缓存穿透问题(15)

Redis基于布隆过滤器解决缓存穿透问题 1.布隆过滤器基本介绍2.布隆过滤器的优缺点3.布隆过滤器的原理4.缓存穿透问题5.解决Redis缓存穿透问题 1.布隆过滤器基本介绍 布隆过滤器适用于判断某个数据是否在集合中存在&#xff0c;可能存在一定的误判&#xff0c; Bloom Filter基本…

13 结构性模式-装饰器模式

1 装饰器模式介绍 在软件设计中,装饰器模式是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态的增加职责,使用对象之间的关联关系取代类之间的继承关系. 2 装饰器模式原理 //抽象构件类 public abstract class Component{public abstract void operation(); }…

14 结构性模式-适配器模式

1 适配器模式介绍 适配器模式(adapter pattern )的原始定义是&#xff1a;将类的接口转换为客户期望的另一个接口&#xff0c;适配器可以让不兼容的两个类一起协同工作。 2 适配器模式原理 3 适配器模式应用实例 /*** SD卡接口**/ public interface SDCard {//读取SD卡Strin…