Bootstrap

数据结构——单链表接口实现(C语言)

github代码下载

github代码:
https://github.com/Kyrie-leon/Data_Structures/tree/main/ListNode

一、链表的概念及结构

1.1 概念

链表是一种物理结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

1.2 结构

在这里插入图片描述

二、链表的接口实现

2.1 链表的存储定义

typedef int SLTDataType;

//单链表的定义
typedef struct SListNode
{
	SLTDataType data;	
	struct SListNode * next; //next指向当前节点的下一个节点
}SListNode;

2.2 动态申请一个节点

申请一个节点首先使用malloc开辟一块结构内存
然后将数据元素x放入data数据区
将next置空
在这里插入图片描述


//2.2 动态申请一个节点
SListNode * BuyListNode(SLTDataType x)
{
	
	SListNode * plist=(SListNode *)malloc(sizeof(SListNode));
	//判断申请是否成功
	if (plist==NULL)
	{
		printf("申请新节点失败\n");
		exit(-1);
	}
	plist->data = x;
	plist->next = NULL;
	return plist;
}

2.3 单链表打印

//2.3 单链表打印
void SListPrint(SListNode * plist)
{

	//打印链表
	while (plist)
	{
		printf("%d->", plist->data);
		plist = plist->next;	//节点指向下一个
	}
	printf("NULL\n");
}

2.4 单链表尾插

  • 首先申请一个新节点
    在这里插入图片描述

  • 判断链表是否为空,如果为空则直接将新节点插入

  • 不是空的话,遍历链表找到尾节点
    在这里插入图片描述

  • 找到后,申请新节点,让原链表尾节点指向新节点
    在这里插入图片描述
    注意:由于尾插可能存在空链表的情况,因此当链表为空时传入一级指针不会改变链表的状态,所以传入二级指针。

//2.4 单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
	//申请一个新节点
	SListNode* NewNode = BuyListNode(x);

	//判断链表是否为空,如果为空则申请节点直接插入,否则找到尾节点插入
	if ((*pplist) == NULL)
	{
		*pplist = NewNode;
	}
	else
	{
		//定义一个当前节点tail去找链表的尾节点
		SListNode* tail = *pplist;

		//遍历节点找尾节点
		while (tail->next)
		{
			tail = tail->next;
		}

		//尾节点的next指向新节点
		tail->next = NewNode;
	}

}

2.5 单链表头插

  • 首先申请新节点
  • 判断链表是否为空,若为空直接插入
  • 不为空,让NewNode的next指向Plist,然后让Plist指向NewNode
    在这里插入图片描述
//2.5 单链表头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
	//申请新节点
	SListNode * NewNode = BuyListNode(x);
	//判断链表是否空
	if (*pplist == NULL)
	{
		//链表为空直接插入
		*pplist = NewNode;

	}
	else 
	{
		NewNode->next = *pplist;	//新节点next指向pplist
		*pplist=NewNode;			//更新头结点
	}

}

2.6 单链表尾删

尾删有三种情况:

  • 单链表为空则返回

  • 单链表只有一个节点,直接删除
    在这里插入图片描述

  • 单链表有一个以上节点,定义两个指针,一个指向尾节点,一个指向尾节点前的一个节点。首先找到尾节点删除,
    在这里插入图片描述
    然后将prev的next置空
    在这里插入图片描述

//2.6 单链表尾删
void SListPopBack(SListNode** pplist)
{
	//1.单链表为空
	if (*pplist == NULL)
	{
		return;
	}
	//2.单链表只有一个节点
	else if((*pplist)->next==NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.单链表有一个以上节点
	else
	{
		//tali指向第1个节点,prev为空
		SListNode * prev = NULL;
		SListNode * tail = *pplist;

		
		//遍历链表找到尾节点
		while (tail->next!=NULL)
		{
			//注意先后顺序,prev先走才不会丢失节点
			//prev向后走一个节点
			prev = tail;
			//tail向后走一个节点
			tail = tail->next;
		}
		//删除尾节点
		free(tail);
		//prev作为新的尾节点
		prev->next = NULL;

	}
}

2.7 单链表头删

链表三种情况同上
首先找到头结点,
在这里插入图片描述
让*pplist指向下一个节点
删除头结点
在这里插入图片描述

//2.7 单链表头删
void SListPopFront(SListNode** pplist)
{
	//1.空单链表
	if (*pplist == NULL)
	{
		return;
	}
	//2.一个节点
	else if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.一个以上
	else
	{

		SListNode * phead = *pplist;
		//现移动删除后的头结点
		*pplist = phead->next;
		//再释放原来的头结点
		free(phead);
	}
}

2.8 单链表查找

//2.8 单链表查找
SListNode * SListFind(SListNode * plist, SLTDataType x)
{
	SListNode * cur = plist;
	//1.空链表直接返回
	//遍历链表
	while (cur)
	{
		//如果找节点值等于x则返回
		if (x == cur->data)
		{
			return cur;
		}
		//没找到继续遍历
		cur = cur->next;
	}
	return NULL;
}

2.9 单链表任意位置之后插入

在pos位置之后插入新节点容易实现,首先让新节点指向当前节点cur的下一个节点,然后让cur当前节点指向新节点
在这里插入图片描述

//2.9 单链表在任意位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode * newNode = BuyListNode(x);
	newNode->next = pos->next;
	pos->next = newNode;
}

2.10 单链表删除任意位置之后的值

在这里插入图片描述

//2.10 单链表删除任意位置之后的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	if (pos->next)
	{
		SListNode * next = pos->next;	//找到pos位置后面的节点保存起来,防止删除后丢失
		pos->next = next->next;			//pos位置节点指向next后的节点
		free(next);		//释放pos后的节点
	}
}

三、多文件实现

3.1 ListNode.h

#ifndef _LISTNODE_H
#define _LISTNODE_H

#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;

//单链表的定义
typedef struct SListNode
{
	SLTDataType data;	//data
	struct SListNode * next; //next指向当前节点的下一个节点
}SListNode;


//动态申请一个节点
SListNode * BuyListNode(SLTDataType x);
//单链表打印
void SListPrint(SListNode * plist);
//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x);
//单链表头插
void SListPushFront(SListNode** pplist, SLTDataType x);
//单链表尾删
void SListPopBack(SListNode** pplist);
//单链表头删
void SListPopFront(SListNode** pplist);
//单链表查找
SListNode * SListFind(SListNode * plist, SLTDataType x);
//单链表在任意位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x);
//单链表删除任意位置之后的值
void SListEraseAfter(SListNode* pos);
//单链表的销毁
void SListDestory(SListNode* plist);


#endif

3.2 ListNode.c

#include "ListNode.h"

//2.2 动态申请一个节点
SListNode * BuyListNode(SLTDataType x)
{
	
	SListNode * plist=(SListNode *)malloc(sizeof(SListNode));
	//判断申请是否成功
	if (plist==NULL)
	{
		printf("申请新节点失败\n");
		exit(-1);
	}
	plist->data = x;
	plist->next = NULL;
	return plist;
}

//2.3 单链表打印
void SListPrint(SListNode * plist)
{

	//打印链表
	while (plist)
	{
		printf("%d->", plist->data);
		plist = plist->next;	//节点指向下一个
	}
	printf("NULL\n");
}

//2.4 单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
	//申请一个新节点
	SListNode* NewNode = BuyListNode(x);

	//判断链表是否为空,如果为空则申请节点直接插入,否则找到尾节点插入
	if ((*pplist) == NULL)
	{
		*pplist = NewNode;
	}
	else
	{
		//定义一个当前节点tail去找链表的尾节点
		SListNode* tail = *pplist;

		//遍历节点找尾节点
		while (tail->next)
		{
			tail = tail->next;
		}

		//尾节点的next指向新节点
		tail->next = NewNode;
	}

}

//2.5 单链表头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
	//申请新节点
	SListNode * NewNode = BuyListNode(x);
	//判断链表是否空
	if (*pplist == NULL)
	{
		//链表为空直接插入
		*pplist = NewNode;

	}
	else 
	{
		NewNode->next = *pplist;	//新节点next指向pplist
		*pplist=NewNode;			//更新头结点
	}

}

//2.6 单链表尾删
void SListPopBack(SListNode** pplist)
{
	//1.单链表为空
	if (*pplist == NULL)
	{
		return;
	}
	//2.单链表只有一个节点
	else if((*pplist)->next==NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.单链表有一个以上节点
	else
	{
		//tali指向第1个节点,prev为空
		SListNode * prev = NULL;
		SListNode * tail = *pplist;

		
		//遍历链表找到尾节点
		while (tail->next!=NULL)
		{
			//注意先后顺序,prev先走才不会丢失节点
			//prev向后走一个节点
			prev = tail;
			//tail向后走一个节点
			tail = tail->next;
		}
		//删除尾节点
		free(tail);
		//prev作为新的尾节点
		prev->next = NULL;

	}
}

//2.7 单链表头删
void SListPopFront(SListNode** pplist)
{
	//1.空单链表
	if (*pplist == NULL)
	{
		return;
	}
	//2.一个节点
	else if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.一个以上
	else
	{

		SListNode * phead = *pplist;
		//现移动删除后的头结点
		*pplist = phead->next;
		//再释放原来的头结点
		free(phead);
	}
}

//2.8 单链表查找
SListNode * SListFind(SListNode * plist, SLTDataType x)
{
	SListNode * cur = plist;
	//1.空链表直接返回
	//遍历链表
	while (cur)
	{
		//如果找节点值等于x则返回
		if (x == cur->data)
		{
			return cur;
		}
		//没找到继续遍历
		cur = cur->next;
	}
	return NULL;
}

//2.9 单链表在任意位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode * newNode = BuyListNode(x);
	newNode->next = pos->next;
	pos->next = newNode;
}

//2.10 单链表删除任意位置之后的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	if (pos->next)
	{
		SListNode * next = pos->next;	//找到pos位置后面的节点保存起来,防止删除后丢失
		pos->next = next->next;			//pos位置节点指向next后的节点
		free(next);		//释放pos后的节点
	}
}

3.3 test.c

#include "ListNode.h"


//测试单链表的头尾删除
void TestListNode1()
{
	SListNode * Plist=NULL;	//定义一个单链表

	//测试申请一个节点
	Plist = BuyListNode(1);

	//打印链表测试
	SListPrint(Plist);

	//2.4 单链表尾插测试
	SListPushBack(&Plist, 2);
	SListPushBack(&Plist, 3);
	SListPushBack(&Plist, 4);
	SListPushBack(&Plist, 5);
	SListPrint(Plist);

	//2.5 单链表头插
	SListPushFront(&Plist, -1);
	SListPrint(Plist);

	//单链表尾删
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);	
	//SListPopBack(&Plist);	
	//SListPopBack(&Plist);
	//SListPrint(Plist);

	//单链表头删
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPrint(Plist);

	//2.8 单链表查找
	SListNode * pos = SListFind(Plist, 3);
	SListPrint(pos);

	//2.9 单链表在任意位置之后插入x
	SListInsertAfter(pos, 30);
	SListPrint(Plist);

	//2.10 单链表删除任意位置之后的值
	SListEraseAfter(pos);
	SListPrint(Plist);


}



int main()
{
	TestListNode1();
	system("pause");
	return 0;
}
;