数据结构与算法-链表

一、数据结构与算法-单向链表

  优点:删除和插入方便。
  缺点:查找某元素时需遍历链表中的所有结点。

1.1 简单单向链表

   1.1.1 简单链表的存储结构

     简单单向链表的存储结构如下图所示,
在这里插入图片描述

   1.1.2 在链表指定位置处插入元素

     具体步骤如下所示:
        1) 创建指向头结点的辅助结点(LinkNode *pCurrent)
        2) 将辅助结点移动至指定位置处
        3) 插入元素;注意:插入元素时需要新开辟结点(LinkNode *newNode)存放需要插入的数据。
     插入步骤如下图所示,

在这里插入图片描述
     实现代码如下所示:

// 在给定位置处插入元素
void Insert_LinkList(LinkList* list, void* data, int pos) {
	// 判断链表和数据是否为空
	if (list == NULL || data == NULL) return;
	// 判断插入位置是否正常
	if (pos < 0 || pos>list->size) pos = list->size; // 若插入位置异常,则在链表尾部插入元素
	// 创建辅助结点,定位到pos指向的结点
	LinkNode* pCurrentLinkNode = list->head;
	for (int i = 0; i < pos; i++) {
		pCurrentLinkNode = pCurrentLinkNode->next;
	}
	// 插入数据
	//   1) 开辟新结点(newNode),保存数据
	LinkNode* newNode = (LinkNode*)malloc(sizeof(LinkNode));
	newNode->data = data;
	//  2) 更改结点的next指针,插入数据
	newNode->next = pCurrentLinkNode->next;
	pCurrentLinkNode->next = newNode;
	// 元素个数加1
	list->size++;
}
   1.1.3 在指定位置删除元素

     具体步骤如下所示:
        1) 创建指向头结点的辅助结点(LinkNode pCurrent)
        2) 将辅助结点移动至指定位置处
        3) 删除结点中的元素;注意:删除元素时需要创建一个指向被删除结点的临时指针(tempNode
),结点中元素删除完毕后,需要释放该结点所占用的内存,否则会造成内存泄漏。
     删除步骤如下图所示,在这里插入图片描述

// 删除指定位置的元素
void RemoveValueByPos_LinkList(LinkList* list, int pos) {
	// 判断链表和数据是否为空
	if (list == NULL) return;
	if (pos < 0 || pos>list->size) return;
	
	// 创建辅助结点,查找pos位置处的结点
	LinkNode* pCurrentLinkNode = list->head;
	for (int i = 0; i < pos; i++) {
		pCurrentLinkNode = pCurrentLinkNode->next;
	}
	// 缓存当前结点,否则会造成内存泄露
	LinkNode* newNode = pCurrentLinkNode->next;
	pCurrentLinkNode->next = newNode->next;
	// 释放缓存结点
	free(newNode);
	list->size--;
}
   1.1.4 完整代码
// 头文件 LinkList.h
#ifndef LINKLIST_H
#define LINKLIST_H
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

// 打印函数
typedef void (*PRINT)(void*);
// 比较函数
typedef int (*COMPARE)(void*, void*);

// 定义链表结点
typedef struct LINKNODE {
	// 数据域
	void* data;
	// 指针域
	struct LINKNODE* next;
}LinkNode;

// 链表
typedef struct LINKLIST {
	// 头结点
	LinkNode* head;
	// 元素的个数
	int size;
}LinkList;

// 链表初始化
LinkList* Init_LinkList();

// 在给定位置处插入元素,插入成功返回1,插入失败返回-1
void Insert_LinkList(LinkList* list, void* data, int pos);

// 删除指定位置的元素
void RemoveValueByPos_LinkList(LinkList* list, int pos);

// 删除特定值的元素
void RemoveValueByValue_LinkList(LinkList* list, void* data, COMPARE compare);

// 查找,根据传入的值查找,并返回第一个为data的位置pos
int Find_LinkList(LinkList* list, void* data, COMPARE compare);

// 第一个元素的结点
void* Front_LinkLsit(LinkList* list);

// 返回链表中元素的个数
int Size_LinkList(LinkList* list);

// 打印链表
void Print_LinkList(LinkList* list, PRINT print);
// 释放链表空间
void FreeSpace_LinkList(LinkList* list);

#endif // !LINKLIST_H
// LinkList.c
#include"LinkList.h"
// 链表初始化
LinkList* Init_LinkList() {
	LinkList* list = (LinkList*)malloc(sizeof(LinkList));
	if (list != NULL) {
		list->size = 0;
		list->head = (LinkNode*)malloc(sizeof(LinkNode));
		if (list->head != NULL) {
			list->head->data = NULL;
			list->head->next = NULL;
		}
	}
	else {
		printf("内存分配失败!");
		return NULL;
	}
	return list;
}


// 在给定位置处插入元素,插入成功返回1,插入失败返回-1
void Insert_LinkList(LinkList* list, void* data, int pos) {
	// 判断链表和数据是否为空
	if (list == NULL || data == NULL) return;
	// 判断插入位置是否正常
	if (pos < 0 || pos>list->size) pos = list->size; // 若插入位置异常,则在链表尾部插入元素
	// 创建辅助结点,定位到pos指向的结点
	LinkNode* pCurrentLinkNode = list->head;
	for (int i = 0; i < pos; i++) {
		pCurrentLinkNode = pCurrentLinkNode->next;
	}
	// 插入数据
	//     开辟新结点,保存数据
	LinkNode* newNode = (LinkNode*)malloc(sizeof(LinkNode));
	newNode->data = data;
	newNode->next = pCurrentLinkNode->next;
	pCurrentLinkNode->next = newNode;
	// 元素个数加1
	list->size++;
}

// 删除指定位置的元素
void RemoveValueByPos_LinkList(LinkList* list, int pos) {
	// 判断链表和数据是否为空
	if (list == NULL) return;
	if (pos < 0 || pos>list->size) return;
	
	// 创建辅助结点,查找pos位置处的结点
	LinkNode* pCurrentLinkNode = list->head;
	for (int i = 0; i < pos; i++) {
		pCurrentLinkNode = pCurrentLinkNode->next;
	}
	// 缓存当前结点,否则会造成内存泄露
	LinkNode* newNode = pCurrentLinkNode->next;
	pCurrentLinkNode->next = newNode->next;
	// 释放缓存结点
	free(newNode);
	list->size--;
}

// 删除特定值的元素
void RemoveValueByValue_LinkList(LinkList* list, void* data, COMPARE compare) {
	if (list == NULL || data == NULL) return;
	// 创建辅助结点,遍历整个链表,删除第一个与data相等的元素
	LinkNode* pCurrent = list->head;
	while (pCurrent) {
		if (compare(pCurrent->next->data, data) == 1) {
			// 删除pCurrent->next结点
			// 简历缓存结点
			LinkNode* newNode = pCurrent->next;
			pCurrent->next = newNode->next;
			// 释放缓存结点
			free(newNode);
			list->size--;
			return;
		}
		// 结点指针指向下一个结点
		pCurrent = pCurrent->next;
	}
}

// 查找,根据传入的值查找,并返回第一个为data的位置pos
int Find_LinkList(LinkList* list, void* data, COMPARE compare) {
	if (list == NULL || data == NULL) return -1;
	// 创建辅助结点,跳过头结点
	LinkNode* pCurrent = list->head->next;
	int pos = 0, flag = -1;
	while (pCurrent) {
		if (compare(pCurrent->data, data) == 1) {
			return flag = pos;
		}
		// 移动位置
		pos++;
		// 将指向当前结点的指针移向下一个结点
		pCurrent = pCurrent->next;
	}
	return flag;
}
// 第一个元素的结点
void* Front_LinkLsit(LinkList* list) {
	if (list == NULL) return NULL;
	return list->head->next->data;
}

// 返回链表中元素的个数
int Size_LinkList(LinkList* list) {
	if (list == NULL) return -1;
	return list->size;
}

// 打印链表
void Print_LinkList(LinkList* list, PRINT print) {
	if (list == NULL) return;
	// 创建辅助结点
	LinkNode* pCurrent = list->head->next;
	while (pCurrent) {
		// 打印结点中的数据
		print(pCurrent->data);
		// 移动辅助当前结点指向的位置
		pCurrent = pCurrent->next;
	}
}

// 释放链表空间
void FreeSpace_LinkList(LinkList* list) {
	if (list == NULL) return;
	// 先释放链表中每个结点的空间
	LinkNode* pCurrent = list->head;
	while (pCurrent) {
		// 缓存下一个结点
		LinkNode* pNext = pCurrent->next;
		// 释放当前结点
		free(pCurrent);
		pCurrent = pNext->next;
	}
	free(list);
}
// main.c
#include"LinkList.h"
#include<string.h>

#define MAX_NUM 10

typedef struct PERSON {
	char name[64];
	int age;
}Person;


// 打印函数
void myPrint(void* data) {
	Person* p = (Person*)data;
	printf("姓名:%s,年龄:%d\n", p->name, p->age);
}

// 比较函数, data1为动态数组中的元素,data2为用户传入的数据
int compare(void* data1, void* data2) {
	Person* p1 = (Person*)data1;
	Person* p2 = (Person*)data2;
	// 如果姓名和年龄均相等,则判定data1与data2相等
	if (!strcmp(p1->name, p2->name) && p1->age == p2->age) return 1;
	return -1;
}

int main() {
	// 初始化结构体
	Person p1 = { "aaa", 20 }, p2 = { "bbb", 30 }, p3 = { "ccc", 40 }, p4 = { "ddd", 50 }, p5 = {"eee", 60};

	// 初始化数组
	LinkList* list = Init_LinkList();

	// 插入结构体数据
	Insert_LinkList(list, &p1, 0);
	Insert_LinkList(list, &p2, 0);
	Insert_LinkList(list, &p3, 0);
	Insert_LinkList(list, &p4, 0);
	Insert_LinkList(list, &p5, 0);
	// 删除前的数组
	printf("删除结点前的链表:\n");
	Print_LinkList(list, myPrint);

	// 根据位置删除元素
	RemoveValueByPos_LinkList(list, 0);
	// 删除后的数组
	printf("删除0号结点后的链表:\n");
	Print_LinkList(list, myPrint);

	// 根据值删除数组
	Person p6 = { "aaa", 20 };
	RemoveValueByValue_LinkList(list, &p6, compare);
	printf("删除数据为{name:aaa, age: 20}的结点后的链表:\n");
	Print_LinkList(list, myPrint);
	
	// 根据值查找位置
	printf("{name:ccc, age: 40}所在位置:%d\n", Find_LinkList(list, &p3, compare));
	//RemoveValueByPos_LinkList(list, Find_LinkList(list, &p3, compare));
	//Print_LinkList(list, myPrint);

	printf("\n=====================\n");
	// 数组大小
	printf("链表中元素个数:%d\n", Size_LinkList(list));

	//返回第一个结点
	Person* ret = (Person*)Front_LinkLsit(list);
	printf("姓名:%s, 年龄:%d", ret->name, ret->age);
	
	// 释放链表空间
	FreeSpace_LinkList(list);
	
	return 0;
}
   1.1.5 运行结果

     运行结果如下所示,
在这里插入图片描述

1.2 改进后的链表

   1.2.1 改进链表的存储结构

  改进后的链表结点(LinkNode)仅包含指针域(LinkNode *next),无数据域。用链表维(LinkList)护链表结点(LinkNode)和元素数量(size)。链表结构如下所示,
在这里插入图片描述
  值得注意的是,放入链表中的数据必须包含一个链表结点,通过链表结点将数据串联串联,具体如下所示。
在这里插入图片描述
  相较于1.1小节的链表,改进后的链表在插入元素无需开辟新结点存储数据,仅需改变结点的next指针即可。

   1.2.2 向改进后的链表插入元素

  向改进后的链表插入元素如下图所示,
在这里插入图片描述

// 向链表中指定位置插入元素
void Insert_LinkList(LinkList* list, LinkNode* node, int pos) {
	// 判断边界情况
	if (list == NULL || node == NULL) return;
	if (pos < 0 || pos >list->size) pos = list->size; // 插入到链表末尾
	// 找到pos位置处对应的结点
	//    1) 创建辅助结点
	LinkNode* pCurrent = &list->head;
	for (int i = 0; i < pos; i++) {
		pCurrent = pCurrent->next;
	}
	//    2) 将传入的node,插入到链表中
	node->next = pCurrent->next;
	pCurrent->next = node;
	// 与包含指针与和数据域的结点相比,更加节省空间,不用新开辟结点保存数据
	//    3) 将链表中的元素数量+1
	list->size++;
}

二、参考链接

   1、数据结构与算法知识点总结
   2、C++教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值