(数据结构)C语言实现单链表,双向带头循环链表

目录

一、链表的概念及结构

1.链表概念

2.链表结构

3.常见的几种形式

a.单向或双向

b.带头或不带

c.循环或非循环

二、单链表

1.创建三个文件,SList.h,SList.c,text.c

2.头文件函数声明

3.尾插

4.打印

5.头插

6.尾删

7.头删

8.查找

9.指定位置删除

10.插入

11.销毁

12.assert(),断言的使用

13.完整代码

a.SList.h

b.SList.c

c.text.c

 三、双向带头循环链表

1.List.h

2.List.c

3.text.c


一、链表的概念及结构

1.链表概念:

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

2.链表结构

物理结构: 

逻辑结构:

3.常见的几种形式

a.单向或双向

b.带头或不带

 

c.循环或非循环

 

本篇选择两种常用的链表书写代码:单链表,带头双向循环链表

二、单链表

1.创建三个文件,SList.h,SList.c,text.c

2.头文件函数声明

关于传参:可见传参时使用了二级指针

我们在进行测试时,定义的肯定是一个结构体指针充当后续操作的头节点,为此我们需要传指针的地址才能做到改变实参,那么传指针的地址自然就需要使用二级指针了。

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

typedef int SLTDateType;//方便修改数据类型
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* phead);
// 单链表尾插
void SListPushBack(SListNode** pphead, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pphead, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pphead);
// 单链表头删
void SListPopFront(SListNode** pphead);
// 单链表查找
SListNode* SListFind(SListNode* phead, SLTDateType x);
// 单链表在pos位置之前插入x
void SListInsert(SListNode**pphead,SListNode* pos, SLTDateType x);
// 单链表删除pos位置
void SListErase(SListNode** pphead,SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** pphead);

 3.尾插

可知这时候需要一个新的节点,且之后也需新增节点,所以分装一个函数

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

void SListPushBack(SListNode** pphead, SLTDateType x)
{
	assert(pphead);
	SListNode* newnode = BuySListNode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//使用tail逐步找尾
		SListNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

4.打印

void SListPrint(SListNode* phead)
{
	SListNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL");
	printf("\n");
}

5.头插

注意修改顺序,防止找不到1号结点位置 

void SListPushFront(SListNode** pphead, SLTDateType x)
{
	assert(pphead);


	SListNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

6.尾删

注意:需要找到倒数第二个结点,而不是倒数第一个,因为单链表找不到上一个节点位置,只能找到下一个结点 。如图,此时应该对2号结点操作,而不是3号

void SListPopBack(SListNode** pphead)
{
	assert(pphead);
	assert(*pphead);


	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}

	else
	{
		SListNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

7.头删

void SListPopFront(SListNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	SListNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}

8.查找

SListNode* SListFind(SListNode* phead, SLTDateType x)
{
	SListNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

9.指定位置删除

void SListErase(SListNode** pphead,SListNode* pos)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* cur = *pphead;
		while (cur->next != pos)
		{
			cur=cur->next;
		}
		cur->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

10.插入

void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pos);


	if (pos == *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* cur = *pphead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		SListNode* newnode = BuySListNode(x);
		cur->next = newnode;
		newnode->next = pos;
	}
}

11.销毁

void SListDestroy(SListNode** pphead)
{
	SListNode* cur;
	while (*pphead != NULL)    
	{
		cur = *pphead;
		*pphead = cur->next;    
		free(cur);    
	}
}

12.assert(),断言的使用

由上可见,函数中大量的使用了assert断言,assert()括号中若为假则会提示我们出错的位置,

此举可以快速地让我们定位到错误的位置。

根据前文提及pphead为传参过来的指针的地址,那么地址就一定不能为空

删除操作时,链表如果为空就没必要删除了,因此断言*pphead就表示链表不能为空

13.完整代码

a.SList.h

#pragma once

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

typedef int SLTDateType;
typedef struct SlistNode
{
	SLTDateType data;
	struct SlistNode* next;
}SListNode;

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* phead);
// 单链表尾插
void SListPushBack(SListNode** pphead, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pphead, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pphead);
// 单链表头删
void SListPopFront(SListNode** pphead);
// 单链表查找
SListNode* SListFind(SListNode* phead, SLTDateType x);
// 单链表在pos位置之前插入x
void SListInsert(SListNode**pphead,SListNode* pos, SLTDateType x);
// 单链表删除pos位置
void SListErase(SListNode** pphead,SListNode* pos);

// 单链表的销毁)
void SListDestroy(SListNode** pphead);

b.SList.c

#define _CRT_SECURE_NO_WARNINGS  1

#include"SList.h"


SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

void SListPrint(SListNode* phead)
{
	SListNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL");
	printf("\n");
}

void SListPushBack(SListNode** pphead, SLTDateType x)
{
	assert(pphead);
	SListNode* newnode = BuySListNode(x);

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

		tail->next = newnode;
	}
}

void SListPushFront(SListNode** pphead, SLTDateType x)
{
	assert(pphead);


	SListNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

void SListPopBack(SListNode** pphead)
{
	assert(pphead);
	assert(*pphead);


	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}

	else
	{
		SListNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
	
}

void SListPopFront(SListNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	SListNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}

SListNode* SListFind(SListNode* phead, SLTDateType x)
{
	SListNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pos);


	if (pos == *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* cur = *pphead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		SListNode* newnode = BuySListNode(x);
		cur->next = newnode;
		newnode->next = pos;
	}
}

void SListErase(SListNode** pphead,SListNode* pos)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* cur = *pphead;
		while (cur->next != pos)
		{
			cur=cur->next;
		}
		cur->next = pos->next;
		free(pos);
		pos = NULL;
	}
}


void SListDestroy(SListNode** pphead)
{
	SListNode* cur;
	while (*pphead != NULL)    
	{
		cur = *pphead;
		*pphead = cur->next;    
		free(cur);    
	}
}

c.text.c

#include"SList.h"

int main()
{
	SListNode* plist = NULL;
	SListPushBack(&plist,1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPushBack(&plist, 6);
	SListPushFront(&plist, 7);
	SListPushFront(&plist, 8);
	SListPushFront(&plist, 9);
	SListPopBack(&plist);
	SListPopFront(&plist);
	SListPrint(plist);

	SListNode*ret= SListFind(plist, 4);
	SListInsert(&plist, ret, 30);
	SListPrint(plist);

	SListErase(&plist, ret);
	SListPrint(plist);


	SListDestroy(&plist);
	return 0;
}

运行结果

 三、双向带头循环链表

掌握单链表其实这个就很简单了,下面直接放代码

1.List.h

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

// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;

//void LTInit(ListNode** pphead);
ListNode* LTInit();
// 创建返回链表的头结点.
ListNode* BuyListNode(LTDataType x);
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);

bool ListEmpty(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

2.List.c

#include"List.h"

bool ListEmpty(ListNode* pHead)
{
	assert(pHead);
	return pHead->next = pHead;
}

ListNode* LTInit()
{
	ListNode* pHead = BuyListNode(-1);
	pHead->next = pHead;
	pHead->prev = pHead;

	return pHead;
}

void ListDestory(ListNode* pHead)
{
	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur!=pHead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(pHead);
}

ListNode* BuyListNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
}

void ListPushBack(ListNode* pHead, LTDataType x)
{
	ListNode* newnode = BuyListNode(x);
	ListNode* tail = pHead->prev;

	//pHead     tail  newnode
	tail->next = newnode;
	pHead->prev = newnode;
	newnode->prev = tail;
	newnode->next = pHead;
}

void ListPushFront(ListNode* pHead, LTDataType x)
{
	ListInsert(pHead->next,x);
}

void ListPopBack(ListNode* pHead)
{
	ListErase(pHead->prev);
}

void ListPopFront(ListNode* pHead)
{
	ListErase(pHead->next);
}

void ListPrint(ListNode* pHead)
{
	assert(pHead);
	ListNode* cur = pHead->next;
	printf("<=Head=>");
	while (cur!=pHead)
	{
		printf("<=%d=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* newnode = BuyListNode(x);
	ListNode* prev = pos->prev;

	prev->next = newnode;
	newnode->prev = prev;

	newnode->next = pos;
	pos->prev = newnode;
}

void ListErase(ListNode* pos)
{
	assert(pos);
	ListNode* next = pos->next;
	ListNode* prev = pos->prev;

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

ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur!=pHead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

3.text.c

#include"List.h"

int main()
{
	ListNode* plist = LTInit();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	ListPrint(plist);
	ListPushFront(plist, 6);
	ListPushFront(plist, 7);
	ListPushFront(plist, 8);
	ListPopBack(plist);
	ListPopBack(plist);
	ListPopFront(plist);
	ListPopFront(plist);
	ListPrint(plist);

	ListNode* pos = ListFind(plist, 2);
	if (pos)
	{
		ListErase(pos);
		pos = NULL;
	}
	ListPrint(plist);

	ListDestory(plist);
}

运行结果

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Saber_OvO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值