实现双向链表的增删查改

List.h

复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

LTNode* LTInit();

void LTPushBack(LTNode* phead, LTDataType x);

void LTPushFront(LTNode* phead, LTDataType x);

void LTPrint(LTNode* phead);

void LTPopBack(LTNode* phead, LTDataType x);

void LTInsert(LTNode* pos, LTDataType x);

void LTErase(LTNode* pos);

LTNode* LTFind(LTNode* phead, LTDataType);

void LTDestTroy(LTNode* phead);

List.c

复制代码
#include"List.h"

LTNode* LTBuyNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	node->data = x;
	node->next = node->prev = node;

	return node;
}

//void LTInit(LTNode** pphead)
//{
//	*pphead = LTBuyNode(-1);
//}

LTNode* LTInit()
{
	LTNode* phead = LTBuyNode(-1);
	return phead;
}

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);
	newnode->prev = phead->prev;
	newnode->next = phead;

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

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);

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

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

void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}
void LTPopBack(LTNode* phead, LTDataType x)
{	
	//链表必须有效且链表不能为空(只有一个哨兵位)
	assert(phead&&phead->next!=phead);	
	LTNode* del = phead->prev;
	del->prev->next = phead;
	phead->prev = del->prev;

	free(del);
	del = NULL;
}

void LTPopFront(LTNode* phead, LTDataType x)
{
	assert(phead && phead->next != phead);
	LTNode* del = phead->next;

	phead->next = del->next;
	del->next->prev = phead;

	free(del);
	del = NULL;
}

LTNode* LTFind(LTNode* phead, LTDataType x)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = LTBuyNode(x);
	newnode->next = pos->next;
	newnode->prev = pos;

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

void LTErase(LTNode* pos)
{
	assert(pos);

	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

void LTDestTroy(LTNode* phead)
{
	assert(phead);

	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	//此时pcur指向phead,而phead还没被销毁
	free(phead);
	phead = NULL;

}

test.c

复制代码
#include"List.h"

void ListTest01()
{
	//LTNode* plist = NULL;
	//LTInit(&plist);
	LTNode* plist = LTInit();

	LTPushBack(plist, 1);
	LTPrint(plist);
	LTPushBack(plist, 2);
	LTPrint(plist);
	LTPushBack(plist, 3);
	LTPrint(plist);

	LTNode* find = LTFind(plist, 3);
	if (find == NULL)
	{
		printf("找不到\n");
	}
	else
		printf("找到了\n");

	LTInsert(find, 66);
	LTPrint(plist);

	LTPushFront(plist, 4);
	LTPrint(plist);

	LTErase(find);
	find = NULL;

	LTPrint(plist);

	LTDestTroy(plist);
	plist = NULL;
	
}

int main()
{
	ListTest01();
	return 0;
}

双向链表是一个带头链表,带头结点又俗称哨兵结点,是用来标记指针位置的作用的。

这里我们给哨兵结点赋予一个标记值-1,当然也可以用其他值代替,这里只起到一个标记作用。实际上我们访问链表是从phead->next也就是pcur结点开始访问。

创建一个结点,我们就得申请一个结点的内存空间:

返回创建的node值,并在以后头插尾插的时候创建一个newnode结点来接受node结点的内存空间,这里我们以尾插为例:

这里插入一个新数据时,我们改变指针的指向就能实现尾插了,并注意这里的newnode与phead的指针指向的顺序不能改变,否则phead的next会被修改,导致找不到phead的next指针。

在这里虽然有些函数传入了形参,但用的是一级指针,无法改变实参,我们需要在test函数中额外来释放节点。

函数都设置为一级指针,是因为更方便我们来观察函数,不然一会一级一行二级指针,是真的很会让人抓狂的。

在这一块我们设置一个查询节点的函数,类型为LTNode*,返回一个结点,即->data==3的那个结点,并返回。

在后续的插入和消除一个值的时候,传入该函数的返回值find。

注意这里Find函数while循环中的条件,即从pcur也就是phead的笑一个结点开始扫描,由于循环的原因,当扫到带头结点即结束扫描,从而访问到了整个链表.
而这块代码当删除头或尾结点时,为什么不可以直接调用PopBack或PopFront函数呢:

是因为该函数没有传入plist指针,无法访问到plist数组。
这里的Pop函数的条件十分重要,不能只剩下一个带头结点,因为链表必须要有效.

相关推荐
Chloeis Syntax3 小时前
栈和队列笔记2025-10-12
java·数据结构·笔记·
404未精通的狗3 小时前
(数据结构)线性表(下):链表分类及双向链表的实现
数据结构·链表
晨非辰5 小时前
【面试高频数据结构(四)】--《从单链到双链的进阶,读懂“双向奔赴”的算法之美与效率权衡》
java·数据结构·c++·人工智能·算法·机器学习·面试
im_AMBER5 小时前
数据结构 03 栈和队列
数据结构·学习·算法
卿言卿语7 小时前
CC1-二叉树的最小深度
java·数据结构·算法·leetcode·职场和发展
仰泳的熊猫7 小时前
LeetCode:889. 根据前序和后序遍历构造二叉树
数据结构·c++·算法
Emilia486.7 小时前
【Leetcode&nowcode&数据结构】单链表的应用(初阶)
c语言·数据结构·算法·leetcode
仰泳的熊猫8 小时前
LeetCode:700. 二叉搜索树中的搜索
数据结构·c++·算法·leetcode
杰 .9 小时前
C++ Hash
数据结构·c++·哈希算法
韧竹、10 小时前
数据结构之单链表
数据结构·链表