暴力数据结构之单链表专题

1. 单链表的初始化

首先定义节点的结构,然后动态内存申请一部分空间,每一个节点都有一个值以及指向下一个节点的指针,称作值域和指针域。

cs 复制代码
//定义节点的结构
//数据 + 指向下一个节点的指针

typedef int SLTDataType;

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

初始化需要为链表节点动态申请一块空间,然后对值域和指针域初始化。

cs 复制代码
//初始化函数
SLTNode* SLTBuyNode(SLTDataType x) 
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

2. 单链表的相关函数

2.1 头插和头删

首先断言头结点不能为空,然后直接从表头插入节点。

cs 复制代码
//头插函数
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	//newnode *pphead
	//将链表依次后移,从头插入
	newnode->next = *pphead;
	*pphead = newnode;
}

断言判断头结点及其地址都不为空,然后将原头结点的下一个节点保存起来,释放原来的头结点,则原头结点的下一个节点成为新的头结点。

cs 复制代码
//头删函数
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}
2.2 尾插和尾删

断言头结点不能为空,同样插入,但是要判断原链表是否为空链表,是空链表则直接插入,反之遍历链表找到尾节点再插入。

cs 复制代码
//尾插函数
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	//*pphead 就是指向第一个节点的指针
	//空链表和非空链表
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		//ptail指向的是尾节点
		ptail->next = newnode;
	}
}

断言判断头结点及其地址均不为空,同样的原链表如果只有一个节点则直接释放,反之找到尾节点后释放尾节点。

cs 复制代码
//尾删函数
void SLTPopBack(SLTNode** pphead)
{
	//链表不能为空
	assert(pphead && *pphead);
	//考虑链表只有一个节点的情况
	if ((*pphead)->next == NULL)
	{
		free(pphead);
		pphead = NULL;
	}
	else
	{
		SLTNode* prev = *pphead;
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		ptail = NULL;
		prev->next = NULL;
	}
}
2.3 在指定位置的前后插入数据

断言待插入节点位置、头结点及其地址均不为空,如果pos为头结点就直接头插,反之遍历链表找到pos的上一个节点,然后在二者中间插入。

cs 复制代码
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead && *pphead);
	SLTNode* newnode = SLTBuyNode(x);
	//判断是否为头节点
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}

断言待插入节点位置、头结点及其地址均不为空,找到pos的上一个节点,然后在二者中间插入。

cs 复制代码
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
2.4 删除当前节点或后面节点

断言待插入节点位置、头结点及其地址均不为空,如果pos为头结点直接头删,反之遍历链表找 到pos节点后保存相邻节点,释放pos节点,将相邻节点连接起来形成新链表。

cs 复制代码
//删除pos当前指向的节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	//pos是头结点/不是头结点
	if (pos == *pphead)
	{
		//头删
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

断言待插入节点位置及其下一个节点均不为空, 保存pos下一个节点即待删除节点,释放pos节点,将相邻节点连接起来形成新链表。

cs 复制代码
//删除pos之后的节点
void SLTErase(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos = del->next;
	free(del);
	del = NULL;
}
2.5 销毁和查找

遍历链表找符合的节点,然后直接返回该节点。

cs 复制代码
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* pcur = phead;
	while (pcur != NULL)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//pcur == NULL;
	return NULL;
}

由于前面有动态内存申请,所以要进行释放free掉,就有了销毁函数。

cs 复制代码
//销毁链表
void SLTDesTroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

3. 代码展示(可自行复制调试)

3.1 test.c
cs 复制代码
#include"SList.h"

void SListTest01()
{
	//链表是由一个一个的节点组成
	//创建几个节点
	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
	node1->data = 1;

	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
	node2->data = 2;

	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
	node3->data = 3;

	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
	node4->data = 4;

	//将四个节点连接起来
	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = NULL;

	//调用链表的打印
	SLTNode* plist = node1;
	SLTPrint(plist);
}

void SListTest02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);
	//SLTPushBack(NULL, 5);
	//
	//测试头插
	//SLTPushFront(&plist, 6);
	//SLTPrint(plist);
	//SLTPushFront(&plist, 7);
	//SLTPrint(plist);
	//SLTPushFront(&plist, 8);
	//SLTPrint(plist);

	//测试尾删
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);

}

int main()
{
	//SListTest01();
	SListTest02();
	return 0;
}
3.2 SList.h
cs 复制代码
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


//定义节点的结构
//数据 + 指向下一个节点的指针

typedef int SLTDataType;

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

//打印函数
void SLTPrint(SLTNode* phead);

//初始化函数
SLTNode* SLTBuyNode(SLTDataType x);

//头插函数
void SLTPushFront(SLTNode** pphead, SLTDataType x);

//尾插函数
void SLTPushBack(SLTNode** pphead, SLTDataType x);

//头删函数
void SLTPopFront(SLTNode** pphead);

//尾删函数
void SLTPopBack(SLTNode** pphead);


//删除pos之后的节点
void SLTErase(SLTNode* pos);


//销毁链表
void SLTDesTroy(SLTNode** pphead);
3.3 SList.c
cs 复制代码
#include"SList.h"


//打印函数
void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("->%d", pcur->data);
		pcur = pcur->next;
	}
	printf("->NULL\n");
}


//初始化函数
SLTNode* SLTBuyNode(SLTDataType x) 
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

//头插函数
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	//newnode *pphead
	//将链表依次后移,从头插入
	newnode->next = *pphead;
	*pphead = newnode;
}


//尾插函数
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	//*pphead 就是指向第一个节点的指针
	//空链表和非空链表
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		//ptail指向的是尾节点
		ptail->next = newnode;
	}
}



//尾删函数
void SLTPopBack(SLTNode** pphead)
{
	//链表不能为空
	assert(pphead && *pphead);
	//考虑链表只有一个节点的情况
	if ((*pphead)->next == NULL)
	{
		free(pphead);
		pphead = NULL;
	}
	else
	{
		SLTNode* prev = *pphead;
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		ptail = NULL;
		prev->next = NULL;
	}
}


//头删函数
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}



//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* pcur = phead;
	while (pcur != NULL)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//pcur == NULL;
	return NULL;
}

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead && *pphead);
	SLTNode* newnode = SLTBuyNode(x);
	//判断是否为头节点
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}



//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}


//删除pos当前指向的节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	//pos是头结点/不是头结点
	if (pos == *pphead)
	{
		//头删
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}


//删除pos之后的节点
void SLTErase(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos = del->next;
	free(del);
	del = NULL;
}

//销毁链表
void SLTDesTroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}
相关推荐
Amor风信子7 分钟前
华为OD机试真题---跳房子II
java·数据结构·算法
Ljubim.te1 小时前
软件设计师——数据结构
数据结构·笔记
_GR2 小时前
每日OJ题_牛客_牛牛冲钻五_模拟_C++_Java
java·数据结构·c++·算法·动态规划
无限大.2 小时前
c语言实例
c语言·数据结构·算法
@haihi3 小时前
冒泡排序,插入排序,快速排序,选择排序
数据结构·算法·排序算法
丢掉幻想准备斗争3 小时前
数据结构(栈和队列的实现)
数据结构
zengy54 小时前
Effective C++中文版学习记录(三)
数据结构·c++·学习·stl
&梧桐树夏7 小时前
【算法系列-链表】删除链表的倒数第N个结点
数据结构·算法·链表
QuantumStack7 小时前
【C++ 真题】B2037 奇偶数判断
数据结构·c++·算法
wclass-zhengge8 小时前
数据结构篇(绪论)
java·数据结构·算法