【数据结构 04】单链表

一、链表简介

链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表在结构上的分类
1. 带头结点或无头结点 2. 单向或双向 3. 循环或非循环

虽然链表有多种结构类型,但是我么在实际开发中常用的只有两种结构:

  1. 无头单向非循环链表:结构简单,通常不单独使用,而是作为其他数据结构的子结构,如哈希桶、图的邻接表......
  2. 带头双向循环链表:结构最复杂,功能最全面,使用效率高

++下例代码是无头单向非循环链表的实现,设计思路:++

  1. 每个ListNode节点都包含一个数据和一个next指针,next指针指向下一个节点
  2. 当pList == NULL 的时候,代表这个链表为空,没有任何数据
  3. 链表最后一个节点的next指针一定是NULL
  4. 当函数涉及数据增删时,传入的参数为二级指针 ListNode** ppList

二、SingleList.h

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int DataType;

typedef struct ListNode
{
	DataType data;
	struct ListNode* next;
}ListNode;

bool Empty(ListNode* plist)
{
	return plist == NULL;
}

void Print(ListNode* plist)
{
	ListNode* cur = plist;
	while (cur != NULL)
	{
		printf("%2d -> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

// 动态申请一个节点
ListNode* BuyNode(DataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->data = x;
	node->next = NULL;
	return node;
}

// 尾插
void PushBack(ListNode** pplist, DataType x)
{
	assert(pplist);
	ListNode* node = BuyNode(x);

	// 插入链表的第一个节点
	if (*pplist == NULL)
	{
		*pplist = node;
		return;
	}

	// cur指针通过循环遍历找到链表的尾结点
	ListNode* cur = *pplist;
	while (cur->next != NULL)
	{
		cur = cur->next;
	}
	cur->next = node;
}

// 头插
void PushFront(ListNode** pplist, DataType x)
{
	assert(pplist);
	ListNode* node = BuyNode(x);

	if (*pplist == NULL)
	{
		*pplist = node;
		return;
	}

	node->next = *pplist;
	*pplist = node;
}

// 尾删
void PopBack(ListNode** pplist)
{
	if (Empty(*pplist))
	{
		printf("链表为空,尾删失败\n");
		return;
	}

	ListNode* cur = *pplist;
	ListNode* prev = cur;
	while (cur->next != NULL)
	{
		prev = cur;
		cur = cur->next;
	}
	free(cur);
	cur = NULL;
	prev->next = NULL;
}

// 头删
void PopFront(ListNode** pplist)
{
	if (Empty(*pplist))
	{
		printf("链表为空,尾删失败\n");
		return;
	}

	ListNode* cur = *pplist;
	*pplist = cur->next;
	free(cur);
	cur = NULL;
}

// 查找,返回第一个元素x的节点
ListNode* Find(ListNode* plist, DataType x)
{
	ListNode* cur = plist;
	while (cur != NULL)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

// 在pos后面插入新节点,pos节点由Find函数获得
void InsertAfter(ListNode* pos, DataType x)
{
	if (pos == NULL)
	{
		printf("pos为空,数据插入失败\n");
		return;
	}
	ListNode* node = BuyNode(x);
	node->next = pos->next;
	pos->next = node;
}

// 删除pos节点
void Delete(ListNode** pplist, ListNode* pos)
{
	if (pos == NULL)
	{
		printf("pos为空,数据删除失败\n");
		return;
	}
	if (Empty(*pplist))
	{
		printf("单链表已为空,Delete失败\n");
		return;
	}

	ListNode* cur = *pplist;
	ListNode* prev = NULL;
	while (cur)
	{
		if (cur->data == pos->data && prev == NULL)
		{
			// 删除第一个节点
			*pplist = pos->next;
			free(pos);
			pos = NULL;
			return;
		}
		if (cur->data == pos->data)
		{
			prev->next = pos->next;
			free(pos);
			pos = NULL;
			return;
		}

		prev = cur;
		cur = cur->next;
	}
}

// 销毁链表
void Destroy(ListNode** pplist)
{
	while (*pplist)
	{
		ListNode* cur = *pplist;
		*pplist = cur->next;
		free(cur);
		cur = NULL;
	}
	printf("链表销毁成功\n");
}

三、test.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include "SingleList.h"

int main()
{
	ListNode* plist = NULL;

	// 尾插数据
	PushBack(&plist, 1);
	PushBack(&plist, 3);
	PushBack(&plist, 5);
	PushBack(&plist, 7);
	Print(plist);

	// 头插数据
	PushFront(&plist, 2);
	PushFront(&plist, 4);
	PushFront(&plist, 6);
	PushFront(&plist, 8);
	Print(plist);

	// 尾删数据
	PopBack(&plist);
	PopBack(&plist);
	PopBack(&plist);
	Print(plist);

	// 头删数据
	PopFront(&plist);
	PopFront(&plist);
	PopFront(&plist);
	Print(plist);

	// 在查找的元素后面插入节点
	InsertAfter(Find(plist, 1), -1);
	InsertAfter(Find(plist, 2), -2);
	InsertAfter(Find(plist, -2), 22);
	Print(plist);

	// 删除查找到的节点
	Delete(&plist, Find(plist, -2));
	Delete(&plist, Find(plist, -1));
	Delete(&plist, Find(plist, 22));
	Delete(&plist, Find(plist, 100)); // pos为空,数据删除失败!
	Print(plist);

	// Delete删空链表
	Delete(&plist, Find(plist, 2));
	Delete(&plist, Find(plist, 1));
	Delete(&plist, Find(plist, 1)); // pos为空,数据删除失败!
	Print(plist);

	// 销毁链表,先插入数据测试
	PushBack(&plist, 1);
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	Print(plist); // 1 -> 2 -> 3 -> NULL
	Destroy(&plist); // 链表销毁成功
	Print(plist);
}
相关推荐
Cx330❀6 小时前
《C++ 多态》三大面向对象编程——多态:虚函数机制、重写规范与现代C++多态控制全概要
开发语言·数据结构·c++·算法·面试
_dindong6 小时前
【递归、回溯、搜索】专题六:记忆化搜索
数据结构·c++·笔记·学习·算法·深度优先·哈希算法
列逍7 小时前
list的模拟实现
数据结构·c++·list
小龙报7 小时前
《算法通关指南:数据结构和算法篇 --- 顺序表相关算法题》--- 1.移动零,2.颜色分类
c语言·开发语言·数据结构·c++·算法·学习方法·visual studio
再睡一夏就好8 小时前
【C++闯关笔记】使用红黑树简单模拟实现map与set
java·c语言·数据结构·c++·笔记·语法·1024程序员节
mifengxing8 小时前
力扣每日一题——接雨水
c语言·数据结构·算法·leetcode·动态规划·
小龙报9 小时前
《算法通关指南:数据结构和算法篇 --- 顺序表相关算法题》--- 询问学号,寄包柜,合并两个有序数组
c语言·开发语言·数据结构·c++·算法·学习方法·visual studio
晨非辰11 小时前
《数据结构风云》递归算法:二叉树遍历的精髓实现
c语言·数据结构·c++·人工智能·算法·leetcode·面试
wudl556611 小时前
Pandas-DataFrame 数据结构详解
数据结构·pandas
_dindong13 小时前
牛客101:二叉树
数据结构·c++·笔记·学习·算法