【数据结构】单链表

2024-06-06 20:54:28 浏览数 (1)

一、链表的概念及结构

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 与顺序表不同的是,链表里的每个存储单元叫做节点,都是独立申请下来的空间,节点由两部分组成:当前节点要保存的数据和下一个节点的指针变量

我们创建一个变量为plist指向第一个数据 链表中每个节点都是独立申请的,我们需要通过指针变量来保存下一个节点位置才能从当前节点找到下一个节点 我们用结构体来实现这个单链表 当节点数据类型为整形时:

代码语言:javascript复制
struct SListNode
{
 int data; //节点数据
 struct SListNode* next; //指针变量⽤保存下⼀个节点的地址
};

链式结构在逻辑上是连续的,在物理结构上不一定连续 节点一般是在堆上申请的 从堆上申请来的空间,可能连续可能不连续

二、单链表的实现

project.h

代码语言:javascript复制
#pragma once
#include <stdio.h>
#include <assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
 SLTDataType data; //节点数据
 struct SListNode* next; //指针保存下⼀个节点的地址
}SLTNode;

void SLTPrint(SLTNode* phead);
//头部插⼊删除/尾部插⼊删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);

project.c

代码语言:javascript复制
#define _CRT_SECURE_NO_WARNINGS

#include "project.h"
//打印
void SLTPrint(SLTNode* phead)
{
	assert(phead);//防止phead为空
	while (phead)
	{
		printf("%d ", phead->data);
		phead = phead->next;
	}
}
//创建节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* newcode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newcode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newcode->data = x;
	newcode->next = NULL;//指向下一节点的指针置为NULL
	return newcode;
}
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* new = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = new;
	}
	else
	{
		SLTNode* pur = *pphead;
		while (pur->next)
		{
			pur = pur->next;//遍历数组,当pur在尾部时停止
		}
		pur->next = new;//插入
	}
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* new = SLTBuyNode(x);
	new->next = *pphead;//将新创建的节点指针指向原链表的头
	*pphead = new;//新节点为新头
}
//尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pur = *pphead;
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* prev = *pphead;
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		ptail = NULL;
		free(ptail);
		//双指针,当ptail遍历数组为尾时,prev在它前边的这个节点
	}
}
//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* node = (*pphead)->next;//定义node存放下一个节点
	(*pphead)->next = NULL;
	free(*pphead);
	*pphead = node;//头指针重新赋值,此时node为头
}
//查找某个量是否在链表中,若是,返回地址,若不是,返回NULL
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* pur = phead;
	while (pur)
	{
		if (pur->data == x)
		{
			return pur;
		}
		pur = pur->next;//遍历链表找出来即可
	}
	return NULL;
}
//在pos之后插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* pur = *pphead;//给一个pur记录头节点位置
	SLTNode* new = SLTBuyNode(x);//创建一个新的节点
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);//若只有一个节点,则直接头插
	}
	else
	{
		while (pur->next != pos)
		{
			pur = pur->next;
		}
		new->next = pos;//不止一个节点就遍历链表找到pos,修改后边节点和新节点指针
		pur->next = new;
	}
}
//删除pos
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(*pphead);
	if (*pphead == pos)
	{
		SLTNode* prev = *pphead;
		*pphead = (*pphead)->next;
		free(prev);//若只有一个节点,直接删除
	}
	else
	{
		SLTNode* pur = *pphead;
		while (pur->next != pos)
		{
			pur = pur->next;
		}
		SLTNode* pucr = pos->next;
		pur->next = pucr;
		pos->next = NULL;
		free(pos);//不止一个节点,找到pos节点,可以直接使用SLTFind函数,改变前后指针然后删除
	}
}
//在指定位置后插入节点
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	SLTNode* new = SLTBuyNode(x);
	SLTNode* pur = pos->next;
	pos->next = new;
	new->next = pur;//这个比较简单,之间改变这个位置后与新节点的指针即可
}
//删除pos节点之后那个节点
void SLTEraseAfter(SLTNode* pos)
{
	SLTNode* pur = pos->next;//记录pos后的节点
	SLTNode* pucr = pur->next;//记录pos之后的之后的节点
	pos->next = pucr;
	pur->next = NULL;//将pos和pucr连接
	free(pur);
}
//销毁链表
void SListDesTroy(SLTNode** pphead)
{
	SLTNode* pur = *pphead;
	while ((*pphead)->next != NULL)
	{
		pur = *pphead;
		*pphead = (*pphead)->next;
		pur->next = NULL;
		free(pur);
	}//遍历链表将堆上内存回收,将指针置为空
	free(*pphead);
}

test.c

代码语言:javascript复制
#include "project.h"

void test1()//
{
		//链表是由一个一个的节点组成
		//创建几个节点
		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 test2()
{
	//尾插
	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);
	SLTPopBack(&plist);
	SLTPrint(plist);*/

	//指定位置后插
	/*SLTInsertAfter(plist->next,9);
	SLTPrint(plist);*/

	//指定位置前插
	//寻找指定位置的数据
	/*SLTNode* find = SLTFind(plist, 1);
	SLTInsert(&plist, find, 11);
	SLTPrint(plist);*/

	//指定位置后插
	/*SLTNode* find = SLTFind(plist, 1);
	SLTInsertAfter(find, 11);
	SLTPrint(plist);*/

	//指定位置删除
	/*SLTNode* find = SLTFind(plist, 1);
	SLTErase(&plist, find);
	SLTPrint(plist);*/

	//删除pos之后的节点
	/*SLTNode* find = SLTFind(plist, 1);
	SLTEraseAfter(find);
	SLTPrint(plist);*/

	//销毁链表
	/*SListDesTroy(&plist);*/
} 
int main()
{
	//test1();
	test2();
	return 0;
}

今天的分享就到这里了~

0 人点赞