【数据结构】【C语言】------ 单链表

前言

大家好,我是Abert。

这篇文章和大家分享用C语言实现数据结构中的单链表。

单链表可以看作是“拆分开”的顺序表。在顺序表的基础上,单链表做了一些优化,但同时也丧失了一些顺序表有的功能。

我们一起来学习单链表吧!!!


文章目录

一、单链表的讲解

二、程序基本框架的搭建与讲解

        2.1 文件的创建

        2.2 变量的创建

三、各种功能的实现及逻辑关系的讲解

        3.1 尾插

        3.2 头插

        3.3 尾删

        3.4 头删

        3.5 查询

        3.6 在pos位置之前插入

        3.7 删除pos位置的值

        3.8 在pos位置之后插入x

        3.9 删除pos之后的值

四、完整代码

        4.1  SLlist.h    

        4.2  SLlist.c

        4.3  test.c

五、优缺点的分析 


正文

一、单链表的讲解

【数据结构】【C语言】------ 单链表_第1张图片

上图可以看作是单链表的一个简单结构,多个节点链接起来(实际在内存中的存储可能不是这样的一条规则的链)

链表插入数据不会像顺序表一样一次开辟很多空间,插入一个类型的数据,开辟一个节点,通过改变指针的指向来把新数据与原数据串接起来,如下图 

【数据结构】【C语言】------ 单链表_第2张图片

同理,单链表删除数据也只需要改变指针的指向的空间,然后free掉要删除的空间。 

如下图:

【数据结构】【C语言】------ 单链表_第3张图片

 


二、程序基本框架的搭建与讲解

2.1 文件的创建

该项目要创建三个文件。

头文件 SLlist.h    (存放程序中需要的头文件以及声明实现功能的函数)

源文件 SLlist.c    (存放实现程序功能的代码)

源文件 test.c          (存放主函数及测试函数功能的代码)

 2.2 变量的创建

typedef int SLDataType;

typedef struct SLlistNode
{
	SLDataType data;//存放数据
	struct SLlistNode* next;//节点

}SLTNode;

这里创建好结构体来成为单链表的子结构,假设创建了一些结构体变量:

SLTNode* n1, *n2, *n3, *n4;

把它们链接起来只需要改变它们内部指针变量的指向。如下:

n1->next = n2;

n2->next = n3;

n3->next = n4;

n4->next = NULL;

如此这些节点会链接起来,就是一个简单的单链表。


三、各种功能的实现及逻辑关系的讲解 

  3.1 尾插

这里利用了二级指针,改变结构体指针需要传入该指针的地址,即为二级指针。

下面需要改变头节点的功能皆需要二级指针

(可以不使用二级指针,但必须return 一个 SLTNode* 的头节点指针。)

本文使用二级指针的方式。

//尾插
void SLlistPushBack(SLTNode** pphead, SLDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySLlistNode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾节点
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}

}

   3.2 头插

//头插
void SLlistPushFront(SLTNode** pphead, SLDataType x)
{
	assert(pphead);

	SLTNode* newnode = BuySLlistNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

3.3 尾删

//尾删
void SLlistPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	//1.只有一个节点
	//2.有多个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

}

3.4 头删

//头删
void SLlistPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;

}

3.5 查询

//查询
SLTNode* SLlistFind(SLTNode* phead, SLDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data = x)
		{
			return cur;
			cur = cur->next;
		}
	}

	return NULL;
}

3.6 在pos位置之前插入

//在pos位置之前插入
void SLlistInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
	assert(pos);
	assert(pphead);

	//头插
	if (pos == *pphead)
	{
		SLlistPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

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

}

3.7 删除pos位置的值 

//删除pos位置的值
void SLlistErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	//头删
	if (pos = *pphead)
	{
		SLlistPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;

		}
		prev->next = pos->next;

		free(pos);
		pos = NULL;
	}

}

3.8 在pos位置之后插入x

//在pos位置之后插入x
void SLlistInsertAfter(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
	assert(pos);

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

}

3.9 删除pos之后的值

//删除pos之后的值
void SLlistEraseAfter(SLTNode* pos)
{
	assert(pos);

	if (pos->next == NULL)
	{
		return;
	}
	SLTNode* del = pos->next;
	pos->next = del->next;

	free(del);
	del = NULL;
}

四、完整代码

        4.1  SLlist.h    

#pragma once
#include 
#include 
#include 

typedef int SLDataType;

typedef struct SLlistNode
{
	SLDataType data;
	struct SLlistNode* next;

}SLTNode;

//初始化单链表
SLTNode* BuySLlistNode(SLDataType x);

//打印单链表
void SLPrint(SLTNode* phead);

//尾插
void SLlistPushBack(SLTNode** pphead, SLDataType x);

//头插
void SLlistPushFront(SLTNode** pphead, SLDataType x);

//尾删
void SLlistPopBack(SLTNode** pphead);

//头删
void SLlistPopFront(SLTNode** pphead);

//查询
SLTNode* SLlistFind(SLTNode* phead, SLDataType x);

//在pos位置之前插入
void SLlistInsert(SLTNode** pphead, SLTNode* pos, SLDataType x);

//删除pos位置的值
void SLlistErase(SLTNode** pphead, SLTNode* pos);

//在pos位置之后插入x
void SLlistInsertAfter(SLTNode** pphead, SLTNode* pos, SLDataType x);

//删除pos之后的值
void SLlistEraseAfter(SLTNode* pos);

        4.2  SLlist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SLlist.h"


//创建一个节点
SLTNode* BuySLlistNode(SLDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}


//打印单链表
void SLPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
	
}


//尾插
void SLlistPushBack(SLTNode** pphead, SLDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySLlistNode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾节点
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}

}


//头插
void SLlistPushFront(SLTNode** pphead, SLDataType x)
{
	assert(pphead);

	SLTNode* newnode = BuySLlistNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}


//尾删
void SLlistPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	//1.只有一个节点
	//2.有多个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

}


//头删
void SLlistPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;

}


//查询
SLTNode* SLlistFind(SLTNode* phead, SLDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data = x)
		{
			return cur;
			cur = cur->next;
		}
	}

	return NULL;
}


//在pos位置之前插入
void SLlistInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
	assert(pos);
	assert(pphead);

	//头插
	if (pos == *pphead)
	{
		SLlistPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

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

}


//删除pos位置的值
void SLlistErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	//头删
	if (pos = *pphead)
	{
		SLlistPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;

		}
		prev->next = pos->next;

		free(pos);
		pos = NULL;
	}

}


//在pos位置之后插入x
void SLlistInsertAfter(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
	assert(pos);

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

}


//删除pos之后的值
void SLlistEraseAfter(SLTNode* pos)
{
	assert(pos);

	if (pos->next == NULL)
	{
		return;
	}
	SLTNode* del = pos->next;
	pos->next = del->next;

	free(del);
	del = NULL;
}

        4.3  test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SLlist.h"

//测试尾插
void TestSList1()
{
	//struct SListNode* 
	SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));
	assert(n1);
	SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));
	assert(n2);
	SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));
	assert(n3);
	SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));
	assert(n4);

	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;

	SLTNode* plist = n1;
	SListPrint(plist);

	SListPushBack(&plist, 5);
	SListPushBack(&plist, 6);
	SListPushBack(&plist, 7);
	SListPushBack(&plist, 8);
	SListPrint(plist);
}



//测试头插
void TestSList2()
{
	SLTNode* plist = NULL;
	SListPushFront(&plist, 0);
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);
	//SListPopFront(&plist);
	SListPrint(plist);
}


//测试尾插与尾删
void TestSList3()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);

	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	//SListPopBack(&plist);
	//SListPrint(plist);
}


//测试查询
void TestSList4()
{
	SLTNode* plist = NULL;
	SListPrint(plist);

	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPrint(plist);

	SLTNode* ret = SListFind(plist, 3);
	if (ret)
	{
		printf("找到了\n");
		ret->data = 30;
	}
	SListPrint(plist);


	SLTNode* pos = SListFind(plist, 4);
	if (pos)
	{
		SListInsert(&plist, pos, 40);
	}
	SListPrint(plist);

	//SListPushBack(NULL, 1);

	//SListInsert(&plist, NULL, 40);
	//SListPrint(plist);

	pos = SListFind(plist, 4);
	if (pos)
	{
		SListErase(&plist, pos);
	}
	SListPrint(plist);
}

int main()
{
	//测试哪个模块的功能,只需解引用该模块的函数

	/*TestSList1();
	TestSList2();
	TestSList3();*/
	TestSList4();

	return 0;
}

五、优缺点的分析

单链表的优点:

        单链表中插入一个类型的数据仅需开辟一个结点的空间,空间利用率高。

        删除数据只需释放结点的空间,不需要挪动数据。

        任意删除或添加数据,时间复杂度尾O(1)

单链表的缺点:

        单链表中任意删除/添加/查找/修改的功能不能使用(不支持数据的随机访问)。


总结

单链表相比顺序表有些许提升,但同时也存在一系列问题。

总体来说,需要不断插入删除数据的时候,采用单链表会比顺序表效率高。

以上就是这篇文章的全部内容,希望对大家有帮助。 

你可能感兴趣的:(数据结构,c语言,开发语言)