数据结构单链表 - C语言实现

链表的概念及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
在这里插入图片描述
注:以上概念以及图片均来自比特科技。

  • 这里建议使用模块化方式测试。这样比较方便调试以及提高代码的可读性和可维护性。

接口声明

                                        **SList.h**
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


typedef int SLTDataType;

typedef struct SListNote
{
    
    
	SLTDataType data;

	struct SListNote* next;

}SLTNote;



//打印
void SListPrint(SLTNote* phead);

//尾插
void SListPushBack(SLTNote** pphead, SLTDataType x);

//尾删
void SListPopBack(SLTNote** pphead);

//头插
void SListPushFront(SLTNote** pphead, SLTDataType x);

//头删
void SListPopFront(SLTNote** pphead);

//查找
SLTNote* SListFind(SLTNote* phead, SLTDataType x);

//在pos位置之前插入
void SListInsert(SLTNote** pphead, SLTNote* pos, SLTDataType x);

//删除pos位置的值
void SListErase(SLTNote** pphead, SLTNote* pos);

// 单链表插入pos位置之后的值
void SListInsertAfter(SLTNote* pos, SLTDataType x);

// 单链表删除pos位置之后的值
void SListEraseAfter(SLTNote* pos);

接口实现

                                        **SList.c**
#include"SList.h"


//打印
void SListPrint(const SLTNote* phead)
{
    
    
	SLTNote* cur = phead;

	while (cur != NULL)
	{
    
    
		printf("%d -> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}


SLTNote* BuySListNote(SLTDataType x)
{
    
    
	//创建结点
	SLTNote* newnode = (SLTNote*)malloc(sizeof(SLTNote));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}


//尾插
void SListPushBack(SLTNote** pphead, SLTDataType x)
{
    
    
	assert(pphead);//注:所有二级指针都必须要检查,因为就算链表为NULL,二级指针也不能为NULL;

	SLTNote* newnode = BuySListNote(x);

	if (*pphead == NULL)
	{
    
    
		//没有结点
		*pphead = newnode;
	}
	else
	{
    
    
		//找尾结点
		SLTNote* tail = *pphead;
		while (tail->next != NULL)
		{
    
    
			tail = tail->next;
		}

		tail->next = newnode;

		//为什么*pphead 改变用二级指针,而tail改变next不需要二级指针?
		//PushBack改变实参plist,实参直接传给形参pphead,pphead改变不会影响实参
		//因为在这里形参pphead是实参plist的一份临时拷贝,如果想要改变实参,这里需要传址

		//而这里tail之所以不用二级指针,是因为这里改变的是结构体tail所指向的next,而不是改变tail这个指针变量

		//综述,pphead是想改变一级指针,所以使用二级指针
		//tail是想改变结构体

	}

}



//头插
void SListPushFront(SLTNote** pphead, SLTDataType x)
{
    
    
	assert(pphead);

	SLTNote* newnode = BuySListNote(x);

	newnode->next = *pphead;
	*pphead = newnode;
}


//尾删
void SListPopBack(SLTNote** pphead)
{
    
    
	assert(*pphead);

	//只有一个结点
	if ((*pphead)->next == NULL)
	{
    
    
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
    
    
		//多个结点 方法①
		SLTNote* tail = *pphead;
		SLTNote* tailPrev = NULL;//记录尾结点前一结点

		while (tail->next != NULL)
		{
    
    
			tailPrev = tail;
			tail = tail->next;
		}

		free(tail);//释放掉尾结点

		tailPrev->next = NULL;//尾结点的前一结点next指向设置为NULL

		//方法②
		//SLTNote* tail = *pphead;
		//while (tail->next->next != NULL)
		//{
    
    
		//	tail = tail->next;
		//}

		//free(tail->next);
		//tail->next = NULL;
	}

}



//头删
void SListPopFront(SLTNote** pphead)
{
    
    
	assert(*pphead);
	assert(pphead);

	SLTNote* second = (*pphead)->next; //保留下一个结点位置
	
	free(*pphead);//释放头部结点

	*pphead = second;//将下一个结点地址传给plist
	
}


//查找 /修改
SLTNote* SListFind(SLTNote* phead, SLTDataType x)
{
    
    
	assert(phead);

	SLTNote* cur = phead;
	while (cur)
	{
    
    
		if (cur->data == x)
			return cur;
		
		cur = cur->next;
	}

	return NULL;
}



//在pos位置之前插入
void SListInsert(SLTNote** pphead, SLTNote* pos, SLTDataType x)
{
    
    
	assert(pos);
	assert(pphead);

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

		SLTNote* newnode = BuySListNote(x);//创建结点
		prev->next = newnode;
		newnode->next = pos;
	}


	//为什么需要传一个二级的头指针?
   // ①:因为插入的时候有可能pos是第一个结点,这里可以直接调用头插
   // ②:这里是为了方便找到pos前一个结点的位置

   //为什么pos传的不是下标,而是结点的指针?
   //因为一般SListInsert是与查找SListFind函数联合使用,日常使用场景是在某个val值前面插入x。
}



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

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

		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}

}



// 单链表插入pos位置之后的值
void SListInsertAfter(SLTNote* pos, SLTDataType x)
{
    
    
	assert(pos);


	SLTNote* newnode = BuySListNote(x);

	//方法① :注意顺序
	//newnode->next = pos->next;
	//pos->next = newnode->next;

	//不在乎顺序 方法②
	SLTNote* scond = pos->next;
	pos->next = newnode->next;
	newnode->next = scond;

}


// 单链表删除pos位置之后的值
void SListEraseAfter(SLTNote* pos)
{
    
    
	assert(pos);

	//尾结点
	if (pos->next == NULL)
	{
    
    
		return;
	}


	SLTNote* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}



接口测试

                                        **test.c**
#include"SList.h"

void TestSList1()
{
    
    
	//struct SListNote*
	SLTNote* n1 = (SLTNote*)malloc(sizeof(SLTNote));
	assert(n1);

	SLTNote* n2 = (SLTNote*)malloc(sizeof(SLTNote));
	assert(n2);

	SLTNote* n3 = (SLTNote*)malloc(sizeof(SLTNote));
	assert(n3);

	SLTNote* n4 = (SLTNote*)malloc(sizeof(SLTNote));
	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;

	//SListPrint(n1);

	SLTNote* plist = n1;
	SListPrint(n1);

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


}


void TestSList2()//测试尾插
{
    
    

	SLTNote* plist = NULL;
	SListPushBack(&plist, 1); //改变变量,传变量的地址,解引用改变
	SListPushBack(&plist, 2); //该变指针变量,传指针变量的地址,解引用改变 (二级指针)
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPrint(plist);

}



void TestSList3()//测试头插
{
    
    
	SLTNote* plist = NULL;

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

	
	SListPushFront(&plist, 0);

	SListPrint(plist);

	SListPopFront(&plist);
	SListPrint(plist);

}


void TestSList4()//测试尾删
{
    
    
	SLTNote* 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 TestSList5()//测试
{
    
    
	SLTNote* plist = NULL;

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

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

	SListPrint(plist);

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

}


void TestSList6()//测试
{
    
    
	SLTNote* plist = NULL;

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


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

}


int main()
{
    
    
	TestSList6();

	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_73969113/article/details/131484530