实现双向链表的增删查改

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函数的条件十分重要,不能只剩下一个带头结点,因为链表必须要有效.

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-22 14:34:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-22 14:34:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-22 14:34:02       87 阅读
  4. Python语言-面向对象

    2024-04-22 14:34:02       96 阅读

热门阅读

  1. 类和对象(下)

    2024-04-22 14:34:02       39 阅读
  2. C++静态变量

    2024-04-22 14:34:02       42 阅读
  3. 【python】一文读懂python函数

    2024-04-22 14:34:02       125 阅读
  4. ADC通道检测功能-单片机通用模板

    2024-04-22 14:34:02       87 阅读
  5. AI原生技术分享活动:引领智能科技新浪潮

    2024-04-22 14:34:02       48 阅读
  6. JVM中都有哪些引用类型

    2024-04-22 14:34:02       52 阅读
  7. 用爬虫玩转石墨文档

    2024-04-22 14:34:02       43 阅读
  8. 【eladmin项目拆解】注解@RequiredArgsConstructor详解

    2024-04-22 14:34:02       89 阅读
  9. 江西公安公布多起网络安全案例

    2024-04-22 14:34:02       45 阅读
  10. SpringBoot的 jar 可以直接运行 怎么解释

    2024-04-22 14:34:02       38 阅读