『初阶数据结构 • C语言』⑨ - 基于结点的数据结构——链表(单链表&&双向循环链表)附完整源码

这篇具有很好参考价值的文章主要介绍了『初阶数据结构 • C语言』⑨ - 基于结点的数据结构——链表(单链表&&双向循环链表)附完整源码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

 文章来源地址https://www.toymoban.com/news/detail-598091.html

本章内容

1.什么是链表

2.链表常见几种形式

3.无头单向非循环链表的实现

3.1结点结构的定义

3.2函数接口的实现

3.2.1尾插

3.2.2尾删

4. 带头双向循环链表的实现

4.1结点结构的定义

4.2函数接口的实现

5.两种链表的差异

①尾插与尾删的时间复杂度

②头插与头删的时间复杂度

③函数形参为何一个是二级指针,一个是一级指针?

完整源码

无头单向非循环链表

SList.h

SList.c

test.c

带头双向循环链表

List.h

List.c

test.c


链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言 

 

1.什么是链表

像数组一样,链表也用来表示一系列的元素。事实上,能用数组来做的事情,一般也可以用链表来做。然而,链表的实现跟数组是不一样的,在不同场景它们会有不同的性能表现。

计算机的内存就像一大堆格子,每格都可以用来保存比特形式的数据。当要创建数组时,程序会在内存中找出一组连续的空格子,给它们起个名字,以便你的应用存放数据。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

我们之前说过,计算机能够直接跳到数组的某一索引上。如果代码要求它读取索引 4的值,那么计算机只需一步就可以完成任务。重申一次,之所以能够这样,是因为程序事先知道了数组开头所在的内存地址——例如地址是 1000——当它想去索引 4时,便会自动跳到 1004处。

与数组不同的是,组成链表的格子不是连续的。它们可以分布在内存的各个地方。这种不相邻的格子,就叫作结点

那么问题来了,计算机怎么知道这些分散的结点里,哪些属于这个链表,哪些属于其他链表呢?这就是链表的关键了:每个结点除了保存数据,它还保存着链表里的下一结点的内存地址。

这份用来指示下一结点的内存地址的额外数据,被称为链。链表如下图所示。 

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

注意:

1.从上图可以看出,链式结构在逻辑上是来连续的,但是在物理上不一定连续。

2.现实中的结点一般都是从堆上来申请的。

3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。


2.链表常见几种形式

①单向或者双向

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

②带头或者不带头 

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言 ③循环或者非循环

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

虽然有这么多的链表的结构,但是我们实际中最常用到的还是这两种结构:

1. 无头单向非循环链表

结构简单,一般不会用来单独进行存储数据。实际中更多是作为其它数据结构的子结构,如哈希表、图的邻接表等等。另外这种结构在笔试面试中出现比较多。链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

2. 带头双向循环链表

结构最复杂,一般用于单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后发现结构会带来很多优势,实现反而简单了。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言


3.无头单向非循环链表的实现

就我个人而言,我认为虽然无头单向非循环链表是这几个链表中结构最简单的,但是实现却是最复杂的。我们学会了头单向非循环链表的实现,别的链表实现应当不在话下。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

3.1结点结构的定义

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

如上文所讲,一个结点包含的内容有数据data和保存下一个结点的地址的指针。

链表正式由一个个这样的结点串连起来的,所以我们只需要记住排在最前面的头结点的位置,就能访问链表里任意一个结点。

注意:无头单向非循环链表里的头结点指的是链表中的第一个结点,它本身也存储着有效数据。而后面所讲的带头双向循环链表中的头结点仅仅是作为链表起始的标志,并不会存储有效数据。

3.2函数接口的实现

//申请一个结点
SLTNode* BuySLTNode(SLTDataType data);
//创建一个链表,包含数据为0~n
SLTNode* CreateSList(int n);
//释放内存
void SLTDestroy(SLTNode** pphead);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType data);
//尾删
void SLTPopBack(SLTNode** pphead);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType data);
//头删
void SLTPopFront(SLTNode** pphead);
//查找data的结点
SLTNode* SLTFind(SLTNode** pphead, SLTDataType data);
//在pos位置前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType data);
//在pos位置后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType data);
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//打印链表内容
void SLTPrint(SLTNode* phead);

由于代码量过大,为了避免影响阅读体验太差,我将完整代码置于文章末尾。此处,我们来细致的学习两个函数尾插与尾删,其它函数与其原理几乎相同。

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType data);
//尾删
void SLTPopBack(SLTNode** pphead);

3.2.1尾插

void SLTPushBack(SLTNode** pphead, SLTDataType data)
{
	SLTNode* newNode = BuySLTNode(data);
	//若为第一次插入,则分情况处理
	if (*pphead==NULL)
	{
		*pphead = newNode;
		return;
	}

	//找尾
	SLTNode* tail = *pphead;
	while(tail->next)
	{
		tail = tail->next;
	}
	//找到了,进行尾插
	tail->next = newNode;
}

 首先将tail也指向头结点,通过变化tail来找到尾结点,如下图所示为找尾的过程:

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

此时,tail所指向的结点的next为NULL,则循环结束,进行尾插。尾插只需要将tail所指向的结点的next指针赋值为newNode即可。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

3.2.2尾删

void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	//分情况处理
	if (*pphead == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}

	//找尾
	SLTNode* tail = *pphead;
	while (tail->next->next)
	{
		tail = tail->next;
	}
	//找到了,进行尾删
	free(tail->next);
	tail->next = NULL;
}

同样的尾删也是先找尾,但与尾插的找尾有一点不同的是while循环的判断条件不相同。原因是我们删掉尾结点后,还有重要的一步是将尾结点的前一个结点(也就是新的尾结点)的next置为NULL。所以这里的tail指向的是尾结点的前一个结点。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

此时tail->next->next 为NULL,所以循环结束,进行尾删。尾删只需要释放掉尾结点,并将新的尾结点的next置为NULL。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言


4. 带头双向循环链表的实现

带头双向循环链表看似结构复杂,其实在写代码时你会感到很轻松。其关键就在于它的头结点不一般。此处的头结点不存储有效数据。

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

4.1结点结构的定义

typedef int LTDataType;

typedef struct ListNode
{
	LTDataType data;
	struct ListNode* prev;//指向前一个结点
	struct ListNode* next;//指向后一个结点
}LTNode;

既然是双向循环链表,那就得要求每个结点既保存下一个结点的地址,又要保存上一个结点的地址。结构中,prev指向前一个结点,next指向后一个结点。

4.2函数接口的实现

//申请一个结点
LTNode* BuyListNode(LTDataType data);
//初始化头结点
LTNode* LTInit();
//释放申请的内存
void LTDestroy(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType data);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDataType data);
//头删
void LTPopFront(LTNode* phead);
//查找data
LTNode* LTFind(LTNode* phead, LTDataType data);
//在pos位置前插入
void LTInsert(LTNode* phead, LTNode* pos, LTDataType data);
//删除pos位置
void LTErase(LTNode* phead, LTNode* pos);
//判断链表是否为空
bool LTEmpty(LTNode* phead);
//统计链表中数据的个数
size_t LTSize(LTNode* phead);
//打印链表存储的数据
void LTPrint(LTNode* phead);

同样的,由于代码过长,为了避免影响阅读体验,我将完整源码置于文章末尾。带头双向循环链表的各个函数实现都比不带头的单链表简单很多。因此学会了之前单链表的实现,双向链表的实现自然轻松无比。这里就不做赘述。


5.两种链表的差异

①尾插与尾删的时间复杂度

对于单链表而言,尾插与尾删都是它的劣势。因为计算机无法直接访问到链表的尾结点,必须遍历完整个链表才能找到尾结点。所以单链表的尾插与尾删都为O(N)。

对于带头双向循环链表,找尾是极其方便的,因为尾结点就在头结点的前一个,可以一步就访问到。所以,带头双向循环链表的尾插与尾删都为O(1)。

②头插与头删的时间复杂度

两种链表头插与头删都为O(1)。对于链表这种数据结构而言,头插与头删都是其优势。上一章中提到顺序表的尾插与尾删效率极高,但是头插与头删却比较劣势。而链表正好相反,头插与头删效率很高。因此面对不同的场景,要学会使用合适的数据结构。

③函数形参为何一个是二级指针,一个是一级指针?

单链表,我们需要对phead的值做修改。那么就得利用phead的指针。而phead本身就是一个指针,所以函数的形参为pphead的二级指针。

双向循环链表,并不需要对phead做修改,而只是需要访问它prev和next所指向的结点,所以只用一级指针即可。


完整源码

无头单向非循环链表

SList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

//申请一个结点
SLTNode* BuySLTNode(SLTDataType data);
//创建一个链表,包含数据为0~n
SLTNode* CreateSList(int n);
//释放内存
void SLTDestroy(SLTNode** pphead);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType data);
//尾删
void SLTPopBack(SLTNode** pphead);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType data);
//头删
void SLTPopFront(SLTNode** pphead);
//查找data的结点
SLTNode* SLTFind(SLTNode** pphead, SLTDataType data);
//在pos位置前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType data);
//在pos位置后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType data);
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//打印链表内容
void SLTPrint(SLTNode* phead);

SList.c

#define _CRT_SECURE_NO_DEPRECATE 1
#include"SList.h"

SLTNode* BuySLTNode(SLTDataType data)
{
	SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));
	//检查是否申请成功
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	//对newNode进行初始化
	newNode->data = data;
	newNode->next = NULL;

	//返回申请成功的结点
	return newNode;
}

SLTNode* CreateSList(int n)
{
	SLTNode* phead = NULL;
	SLTNode* ptail = NULL;

	for (int i = 0; i < n; i++)
	{
		SLTNode* newNode = BuySLTNode(i);
		//若为第一个插入,则分情况处理
		if (phead == NULL)
		{
			phead = ptail = newNode;
		}
		else
		{
			ptail->next = newNode;
			ptail = newNode;
		}
	}
	return phead;
}

void SLTDestroy(SLTNode** pphead)
{
	assert(*pphead);
	SLTNode* cur = *pphead;
	//cur判断何时结束
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

void SLTPushBack(SLTNode** pphead, SLTDataType data)
{
	SLTNode* newNode = BuySLTNode(data);
	//若为第一次插入,则分情况处理
	if (*pphead==NULL)
	{
		*pphead = newNode;
		return;
	}

	//找尾
	SLTNode* tail = *pphead;
	while(tail->next)
	{
		tail = tail->next;
	}
	//找到了,进行尾插
	tail->next = newNode;
}
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	//分情况处理
	if (*pphead == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}

	//找尾
	SLTNode* tail = *pphead;
	while (tail->next->next)
	{
		tail = tail->next;
	}
	//找到了,进行尾删
	free(tail->next);
	tail->next = NULL;
}

void SLTPushFront(SLTNode** pphead, SLTDataType data)
{
	SLTNode* newNode = BuySLTNode(data);
	//进行头插
	newNode->next = *pphead;
	*pphead = newNode;
}

void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);
	//进行头删
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

SLTNode* SLTFind(SLTNode** pphead, SLTDataType data)
{
	assert(*pphead);
	SLTNode* cur = *pphead;
	while (cur)
	{
		//找到了,返回cur
		if (cur->data == data)
		{
			return cur;
		}
		cur = cur->next;
	}
	//没找到,返回NULL
	return NULL;
}

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType data)
{
	assert(pos);
	SLTNode* newNode = BuySLTNode(data);
	//若pos恰好是phead,相当于进行头插
	if (pos == *pphead)
	{
		SLTPushFront(pphead, data);
		return;
	}
	//找pos前一个指针
	SLTNode* prev = *pphead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}
	//找到了,进行插入
	prev->next = newNode;
	newNode->next = pos;
}

void SLTInsertAfter(SLTNode* pos, SLTDataType data)
{
	assert(pos);
	SLTNode* newNode = BuySLTNode(data);
	SLTNode* next = pos->next;
	pos->next = newNode;
	newNode->next = next;
}

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	//若pos恰好就是phead,则相当于头删
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
		return;
	}
	//找pos前一个结点
	SLTNode* prev = *pphead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}
	//找到了,进行删除
	prev->next = pos->next;
	free(pos);
	pos = NULL;
}

void SLTPrint(SLTNode* phead)
{
	assert(phead);
	SLTNode* cur = phead;
	//cur控制何时结束
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

test.c

#define _CRT_SECURE_NO_DEPRECATE 1
#include"SList.h"

void test()
{
	SLTNode* phead = CreateSList(10);
	SLTPushBack(&phead, 0);
	SLTPushBack(&phead, 0);
	SLTPushBack(&phead, 0);
	SLTPushBack(&phead, 0);
	SLTPushBack(&phead, 0);
	SLTPrint(phead);

	SLTPushFront(&phead, 1);
	SLTPushFront(&phead, 1);
	SLTPushFront(&phead, 1);
	SLTPushFront(&phead, 1);
	SLTPushFront(&phead, 1);
	SLTPrint(phead);

	SLTNode* pos = SLTFind(&phead, 3);
	SLTInsert(&phead, pos, 300);
	SLTInsertAfter(pos, 30);
	SLTPrint(phead);
	SLTPopBack(&phead);
	SLTPopBack(&phead);
	SLTPopBack(&phead);
	SLTPopBack(&phead);
	SLTPopBack(&phead);

	SLTPopFront(&phead);
	SLTPopFront(&phead);
	SLTPopFront(&phead);
	SLTPopFront(&phead);
	SLTPopFront(&phead);
	SLTPrint(phead);
	SLTDestroy(&phead);
}
int main()
{
	test();
	return 0;
}

带头双向循环链表

List.h

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

typedef int LTDataType;

typedef struct ListNode
{
	LTDataType data;
	struct ListNode* prev;//指向前一个结点
	struct ListNode* next;//指向后一个结点
}LTNode;

//申请一个结点
LTNode* BuyListNode(LTDataType data);
//初始化头结点
LTNode* LTInit();
//释放申请的内存
void LTDestroy(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType data);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDataType data);
//头删
void LTPopFront(LTNode* phead);
//查找data
LTNode* LTFind(LTNode* phead, LTDataType data);
//在pos位置前插入
void LTInsert(LTNode* phead, LTNode* pos, LTDataType data);
//删除pos位置
void LTErase(LTNode* phead, LTNode* pos);
//判断链表是否为空
bool LTEmpty(LTNode* phead);
//统计链表中数据的个数
size_t LTSize(LTNode* phead);
//打印链表存储的数据
void LTPrint(LTNode* phead);

List.c

#define _CRT_SECURE_NO_DEPRECATE 1
#include"List.h"

LTNode* BuyListNode(LTDataType data)
{
	LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newNode->data = data;
	newNode->prev = NULL;
	newNode->next = NULL;

	return newNode;
}

LTNode* LTInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->prev = phead;
	phead->next = phead;
	return phead;
}

void LTDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	free(phead);
}

void LTPushBack(LTNode* phead, LTDataType data)
{
	assert(phead);
	LTNode* newNode = BuyListNode(data);
	LTNode* tail = phead->prev;

	newNode->next = phead;
	newNode->prev = tail;
	tail->next = newNode;
	phead->prev = newNode;
}

void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));
	LTNode* tail = phead->prev;
	tail->prev->next = phead;
	phead->prev = tail->prev;

	free(tail);
}

void LTPushFront(LTNode* phead, LTDataType data)
{
	assert(phead);
	LTNode* newNode = BuyListNode(data);
	
	newNode->next = phead->next;
	phead->next->prev = newNode;

	phead->next = newNode;
	newNode->prev = phead;
}

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));
	LTNode* cur = phead->next;
	phead->next = cur->next;
	cur->next->prev = phead;

	free(cur);
}

LTNode* LTFind(LTNode* phead, LTDataType data)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data==data)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void LTInsert(LTNode* phead, LTNode* pos, LTDataType data)
{
	assert(phead);
	LTNode* newNode = BuyListNode(data);
	pos->prev->next = newNode;
	newNode->prev = pos->prev;

	newNode->next = pos;
	pos->prev = newNode;
}

void LTErase(LTNode* phead,LTNode* pos)
{
	assert(phead);
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;

	free(pos);
}

bool LTEmpty(LTNode* phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		return true;
	}
	return false;
}

size_t LTSize(LTNode* phead)
{
	assert(phead);
	size_t size = 0;
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

test.c

#define _CRT_SECURE_NO_DEPRECATE 1
#include"List.h"

void test()
{
	LTNode* phead = LTInit();
	LTPushBack(phead, 2);
	LTPushBack(phead, 1);
	LTPushBack(phead, 1);
	LTPushBack(phead, 1);
	LTPushBack(phead, 1);
	LTPushBack(phead, 1);
	LTPushFront(phead, 0);
	LTPushFront(phead, 0);
	LTPushFront(phead, 0);
	LTPushFront(phead, 0);
	LTPushFront(phead, 0);
	LTPrint(phead);
	LTPopFront(phead);
	LTPopFront(phead);
	LTPopFront(phead);
	LTPrint(phead);
	LTPopBack(phead);
	LTPopBack(phead);
	LTPopBack(phead);
	LTPrint(phead);
	LTNode* pos = LTFind(phead, 2);
	LTInsert(phead, pos, 200);
	LTInsert(phead, pos, 200);
	LTInsert(phead, pos, 200);
	LTPrint(phead);
	LTErase(phead, pos);
	LTPrint(phead);
	LTDestroy(phead);
}

int main()
{
	test();
	return 0;
}

链表源码,新星计划免费学习专栏·数据结构与算法,数据结构,链表,c语言

 

到了这里,关于『初阶数据结构 • C语言』⑨ - 基于结点的数据结构——链表(单链表&&双向循环链表)附完整源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构和算法初阶(C语言)】复杂链表(随机指针,随机链表的复制)题目详解+链表顺序表结尾

    目录  1.随机链表的复制 1.2题目描述  1.3题目分析 1.4解题: 2.顺序表和链表对比 2.1cpu高速缓存利用率 3.结语 一个长度为  n  的链表,每个节点包含一个额外增加的随机指针  random   该指针可以指向链表中的任何节点或空节点。        构造这个链表的  深拷贝 。 深拷贝

    2024年03月10日
    浏览(87)
  • 【数据结构初阶】三、 线性表里的链表(无头+单向+非循环链表 -- C语言实现)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】二、 线性表里的顺序表(C语言实现顺序表)-CSDN博客  =========================

    2024年02月08日
    浏览(56)
  • 【数据结构OJ题】链表中倒数第k个结点

    原题链接:https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13tqId=11167rp=2ru=/activity/ojqru=/ta/coding-interviews/question-ranking 目录 1. 题目描述 2. 思路分析 3. 代码实现 快慢指针法   (如果有小伙伴不了解快慢指针法,可以看看这篇文章:https://blog.csdn.net/m0_62531913/article/details/

    2024年02月12日
    浏览(50)
  • 初阶数据结构:链表

    经过学习与练习,已经掌握了顺序表的相关知识并且能够自我实现。在这一过程中,通过对其结构的实现,发现顺序表的尾部插入删除数据效率很高,可是,在 头部与随机插入的场景下效率低下 而且 存在扩容消耗 等一些问题。而有这样一种数据结构可以很好的解决顺序表存

    2024年01月20日
    浏览(51)
  • 【数据结构】链表OJ面试题(《删除定值、反转、返回中间结点、倒数第k节点、合并链表》)+解析)

    还不清楚链表的码喵们可以看看前篇关于链表的详解 既然已经懂得了链表该如何实现,那么现在就趁热打铁开始练习!这里给码喵们整理了相对不错的一些OJ题来练习  力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 思路:遍历整个表,访问每个表的值并且删除再将nex

    2024年02月22日
    浏览(51)
  • 初阶数据结构(三)链表

    💓博主csdn个人主页:小小unicorn💓 ⏩专栏分类:c++ 🚚代码仓库:小小unicorn的学习足迹🚚 🌹🌹🌹关注我带你学习编程知识 前面我们讲的线性表的 顺序存储结构 。它是有缺点的,最大的缺点就是插入和删除时需要移动大量元素,这显然就需要 耗费时间 ,那能不能想办法

    2024年02月09日
    浏览(60)
  • 【数据结构初阶】链表OJ

    OJ 方案一: 题目解析: 方案二: 题目解析:把原链表遍历一遍,插入新链表 OJ 题目解析: OJ 题目解析: OJ 题目解析: OJ 题目解析: OJ 题目解析: OJ 题目解析: OJ 题目解析: 定义快慢指针,使快指针先走与慢指针同步。然后同时走看是否相交 OJ 题目解析: OJ 题目解析:

    2024年02月05日
    浏览(50)
  • 初阶数据结构之---二叉树链式结构(二叉树的构建,二叉树的前序,中序,后序和层序遍历,计算二叉树结点个数,第k层结点个数,叶子结点个数,判断是否为完全二叉树)

    本篇博客是初阶数据结构树的收尾,将会讲掉 基本二叉树链式结构的具体内容和实现,包括二叉树的构建,前序遍历,中序遍历,后序遍历和层序遍历,计算二叉树结点个数,第k层结点个数,二叉树叶子结点个数,以及判断一个二叉树是否为完全二叉树 。话不多说,开始我

    2024年03月24日
    浏览(50)
  • 【数据结构初阶】第四节.链表详讲

    前言 一、单链表的概念 二、链表的创建 2.1链表的初始化 2.2 打印链表 2.3 获取链表的长度 2.4 判断链表是否为空 三、新增结点 3.1头插 3.2 指定下标插入 四、删除结点 4.1 头删 4.2 指定下标的删除 4.3 删除链表中的指定元素 五、单链表查找 六、附录 总代码 测试代码 总结 前

    2023年04月15日
    浏览(71)
  • 贪吃蛇项目(基于C语言和数据结构中的链表)

    首先先建立3个文件。 Snake.h 函数的声明 Snake.c 函数的定义 Test.c     贪吃蛇的测试    我们分析这整个项目 首先在我们实现游戏开始的部分之前,我们要先创建贪吃蛇的节点,再由此创建整个贪吃蛇所包含的一些信息: 我们枚举一下这个贪吃蛇中所有的一些状态: 然后我们

    2024年02月20日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包