数据结构-手撕单链表+代码详解

⭐️ 往期相关文章

✨ 链接1:数据结构-手撕顺序表(动态版)+代码详解
✨ 链接2:数据结构和算法的概念以及时间复杂度空间复杂度详解


⭐️ 链表

🌠 什么是链表?

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针域来指向下一个结点的地址来实现的。

🌠 为什么会有链表?

链表是因为顺序表的缺陷而产生的。那么顺序表有哪些缺陷呢?

  • 中间/头部的插入删除,需要挪动数据,时间复杂度为 O ( N ) O(N) O(N)
  • 增容需要申请新的空间,也就面临着效率问题,如果扩容的位置后续的位置够新的空间,那么则在尾部进行扩容。若后续位置不足新的空间,那么会找一片足够的空间,在把原来的数据拷贝过去,释放旧空间。
  • 增容多了势必就有空间上的浪费,增容少了就会有频繁的增容,导致效率不够高。

而链表中的结点都是按需申请内存,不会存在浪费和频繁的扩容。而效率也根据链表的分类不同,时间复杂度也有一定的区别。而单链表,相对于其他类型的链表有很多的缺陷,所以也是面试的重点。

🌠 链表的分类

实际中链表的结构非常多样。

  • 单向带头循环链表
  • 单向不带头循环链表
  • 单向带头不循环链表
  • 单向不带头不循环链表
  • 双向带头循环链表
  • 双向不带头循环链表
  • 双向带头不循环链表
  • 双向不带头不循环链表

本期主要讲解的是单向不带头不循环链表也称单链表。

单链表示意图:
在这里插入图片描述
单链表的结构主要分为:数据域、指针域。数据域用来存放数据。指针域用来存放下一个结点的指针。最后一个结点的指针域存放 NULL

🌠 手撕单链表

单链表的结构:

// 相关头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

// 单链表的类型
typedef int SingleListType;

typedef struct SingleList {
	SingleListType data;		// 数据域
	struct SingleList* next;	// 指针域
}SingleList;

typedef int SingleListType; 单链表的类型不能写死,因为可能存储的数据是 chardoubleint...的其他类型。

单链表的接口实现

// 单链表的打印
void SingleListPrint(SingleList* phead);
// 创建新结点
SingleList* SingleListCreateNode(SingleListType node);
// 单链表的尾插
void SingleListPushBack(SingleList** pphead , SingleListType node);
// 单链表的头插
void SingleListPushFront(SingleList** pphead , SingleListType node);
// 单链表的头删
void SingleListPopFront(SingleList** pphead);
// 单链表的尾删
void SingleListPopBack(SingleList** pphead);
// 单链表的查找
SingleList* SingleListFind(SingleList* phead , SingleListType node);
// 单链表 pos 位置前插入
void SingleListInsert(SingleList** pphead , SingleList* pos , SingleListType node);
// 单链表对应位置删除
void SingleListErase(SingleList** pphead, SingleList* pos);
// 单链表 pos 位置后插入
void SingleListInsertAfter(SingleList* pos, SingleListType node);
// 单链表 pos 位置后删除
void SingleListEraseAfter(SingleList* pos);

⭕️ 单链表的打印

void SingleListPrint(SingleList* phead) {
	SingleList* cur = phead;

	while (cur != NULL) {
		printf("%d->", cur->data);
		cur = cur->next;
	}

	printf("NULL\n");
}

⭕️ 创建新结点

SingleList* SingleListCreateNode(SingleListType node) {
	SingleList* newNode = (SingleList*)malloc(sizeof(SingleList));
	assert(newNode);
	newNode->data = node;
	newNode->next = NULL;

	return newNode;
}

⭕️ 单链表的尾插

void SingleListPushBack(SingleList** pphead, SingleListType node) {
	assert(pphead);

	// 创建新结点
	SingleList* newNode = SingleListCreateNode(node);

	// 空链表的情况
	if (*pphead == NULL) {
		*pphead = newNode;
	}
	else {
		// 非空链表的情况
		SingleList* tail = *pphead;
		while (tail->next != NULL) {
			tail = tail->next;
		}

		tail->next = newNode;
	}
}

⭕️ 单链表的头插

void SingleListPushFront(SingleList** pphead, SingleListType node) {

	assert(pphead);

	// 创建新结点
	SingleList* newNode = SingleListCreateNode(node);

	newNode->next = *pphead;
	*pphead = newNode;
}

⭕️ 单链表的头删

void SingleListPopFront(SingleList** pphead) {
	assert(pphead);

	// 空链表
	assert(*pphead);

	SingleList* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

⭕️ 单链表的尾删

void SingleListPopBack(SingleList** pphead) {
	assert(pphead);
	
	// 空链表
	assert(*pphead);

	// 只有一个结点的情况
	if ((*pphead)->next == NULL) {
		free(*pphead);
		*pphead = NULL;
	}
	else {
		// 多个结点的情况
		SingleList* tail = *pphead;
		SingleList* tailPrev = NULL;

		while (tail->next != NULL) {
			tailPrev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		tailPrev->next = NULL;
	}
}

⭕️ 单链表的查找

SingleList* SingleListFind(SingleList* phead, SingleListType node) {
	SingleList* cur = phead;

	while (cur != NULL) {
		if (cur->data == node) {
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}

⭕️ 单链表 pos 位置前插入

void SingleListInsert(SingleList** pphead, SingleList* pos, SingleListType node) {

	assert(pos);
	assert(pphead);

	// 创建新结点
	SingleList* newNode = SingleListCreateNode(node);

	if (*pphead == pos) {
		newNode->next = *pphead;
		*pphead = newNode;

		// 复用
		// SingleListPushFront(pphead , node);
	}
	else {
		SingleList* cur = *pphead;

		while (cur->next != pos) {
			cur = cur->next;
		}
		newNode->next = cur->next;
		cur->next = newNode;
	}
}

⭕️ 单链表 pos 位置删除

void SingleListErase(SingleList** pphead, SingleList* pos) {
	assert(pphead);

	assert(pos && *pphead);

	if (*pphead == pos) {
		SingleList* next = (*pphead)->next;
		free(*pphead);
		*pphead = next;

		// 复用
		// SingleListPopFront(pphead);
	}
	else {
		SingleList* cur = *pphead;

		while (cur->next != pos) {
			cur = cur->next;
		}

		cur->next = pos->next;
		free(pos);

	}
}

⭕️ 单链表 pos 位置后插入

void SingleListInsertAfter(SingleList* pos, SingleListType node) {
	assert(pos);

	SingleList* newNode = SingleListCreateNode(node);
	newNode->next = pos->next;
	pos->next = newNode;
}

⭕️ 单链表 pos 位置后删除

void SingleListEraseAfter(SingleList* pos) {
	assert(pos);

	// 尾没有后一个结点
	assert(pos->next != NULL);


	SingleList* del = pos->next;
	pos->next = del->next;
	free(del);
}

🌠 单链表测试

void SingleListTest() {
	SingleList* pList = NULL;

	SingleListPushFront(&pList, 1);
	SingleListPushFront(&pList, 2);
	SingleListPushFront(&pList, 3);
	SingleListPushFront(&pList, 4);
	SingleListPrint(pList);

	SingleList* result = SingleListFind(pList , 2);
	if (result != NULL) {
		result->data = 20;
	}
	SingleListPrint(pList);

	result = SingleListFind(pList, 20);
	if (result != NULL) {
		SingleListInsert(&pList , result , 40);
	}
	SingleListPrint(pList);

	result = SingleListFind(pList, 1);
	if (result != NULL) {
		SingleListErase(&pList , result);
	}
	SingleListPrint(pList);
}

int main() {

	SingleListTest();

	return 0;
}

在这里插入图片描述


  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值