双向链表的实现

1、双向链表的结构

在这里插入图片描述
注意:
这⾥的“带头”跟前⾯我们说的“头节点”是两个概念,实际前⾯的在单链表阶段称呼不严谨,但是为了同学们更好的理解就直接称为单链表的头节点。带头链表⾥的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这⾥“放哨的”。

“哨兵位”存在的意义:
遍历循环链表避免死循环。

2、双向链表的实现

1) 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;

//对链表初始化
//1)
void LTInit(LTNode** pphead); //前提是需要传入一个头结点
//2) 我们使用第二种方法
LTNode* LTInit2(); //直接返回一个头结点

//链表的数据打印
void LTprint(LTNode* phead);

//尾插
void LTPushBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead);

//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);

//查找pos节点
LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置的节点
void LTErase(LTNode* phead);

//链表的销毁
void LTDestroy(LTNode* phead);

2) List.c ——文件

#include"List.h"

//初始化①方法
void LTInit(LTNode** pphead) {
	*pphead = (LTNode*)malloc(sizeof(LTNode));
	if (*pphead == NULL) {
		perror("malloc error!");
		return;
	}
	(*pphead)->data = -1;
	(*pphead)->next = (*pphead)->prev = *pphead;
}
//初始化②方法——————使用这个方法
LTNode* LTInit2() {
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	if (phead == NULL) {
		perror("malloc error!");
		return;
	}
	phead->data = -1;
	phead->next = phead->prev = phead;
	return phead;
}

//对链表的打印
void LTprint(LTNode* phead) {
	assert(phead);
	LTNode* tmp = phead->next;
	while (tmp != phead) {
		printf("%d->", tmp->data);
		tmp = tmp->next;
	}
	printf("\n");
}


//对所需插入数据的初始化
LTNode* ListBuyNode(LTDataType x) {
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL) {
		perror("newnode malloc error!");
		return;
	}
	node->data = x;
	node->next = node->prev = NULL;
	return node;
}

//尾插
void LTPushBack(LTNode* phead,LTDataType x) {
	assert(phead);
	LTNode* node = ListBuyNode(x);
	//先是对node节点的链接
	node->next = phead;
	node->prev = phead->prev;

	//在处理之前的phead->prev 和 phead
	phead->prev->next = node;
	phead->prev = node;
}

//头插
void LTPushFront(LTNode* phead, LTDataType x) {
	assert(phead);
	LTNode* node = ListBuyNode(x);
	//先是对node处理
	node->next = phead->next;
	node->prev = phead;

	//再对phead->next 和 phead
	phead->next->prev = node;
	phead->next = node;
}

//尾删
void LTPopBack(LTNode* phead) {
	assert(phead);
	//只剩下一个哨兵位时
	assert(phead->prev != phead);

	LTNode* del = phead->prev;
	//先处理del->prev
	del->prev->next = phead;
	//在处理phead
	phead->prev = del->prev;
	
	free(del);
	del = NULL;
}

//头删
void LTPopFront(LTNode* phead) {
	assert(phead);
	assert(phead->next != phead);
	LTNode* del = phead->next;
	//先是对del->next处理
	del->next->prev = phead;
	//再对phead处理
	phead->next = del->next;

	free(del);
	del = NULL;
}

//查找pos节点,以data 作为查找对象
LTNode* LTFind(LTNode* phead, LTDataType x) {
	assert(phead);
	assert(phead->next != phead);
	LTNode* find = phead->next;
	while (find != phead) {
		if (find->data == x) {
			return find;
		}
		find = find->next;
	}
	printf("所查找的数据不存在!\n");
	return NULL;
}
 
//在pos之后插入数据
void LTInsert(LTNode* pos, LTDataType x) {
	assert(pos);
	LTNode* node = ListBuyNode(x);
	//先是对node处理
	node->next = pos->next;
	node->prev = pos;
	//再对pos->next 和 pos 处理
	pos->next->prev = node;
	pos->next = node;
}

//删除pos位置的节点
void LTErase(LTNode* pos) {
	assert(pos);
	LTNode* del = pos;
	del->prev->next = del->next;
	del->next->prev = del->prev;
	
	free(del);
	del = NULL;
}

//链表的销毁 必须是二级指针,不然没影响到对plist的销毁
void LTDestroy(LTNode** pphead) {
	assert(pphead && *pphead);
	LTNode* cur = (*pphead)->next;
	while (cur != *pphead) {
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	//循环完之后哨兵位还没释放
	free(*pphead);
	*pphead = NULL;
}

3) test.c ——文件

#include"List.h"

void testList() {
	//初始化给个哨兵位
	LTNode* plist = LTInit2();
	//尾插
	LTPushBack(plist,1);
	LTPushBack(plist,2);
	LTPushBack(plist,3); // 1 2 3
	LTprint(plist);

	//头插
	LTPushFront(plist, 4);
	LTPushFront(plist, 5);
	LTPushFront(plist, 6); // 6 5 4 1 2 3
	LTprint(plist);

	//尾删
	LTPopBack(plist);
	LTprint(plist);

	//头删
	LTPopFront(plist);
	LTprint(plist);

	//先查找data == 1 的节点pos
	LTNode* pos = LTFind(plist, 1);
	//再往 pos节点后插入新的节点
	LTInsert(pos, 100);
	LTprint(plist);

	//先查找data == 2 的节点pos
	LTNode* pos2 = LTFind(plist, 4);
	//在删除pos节点
	LTErase(pos2);
	LTprint(plist);
	
	//对链表的销毁法①
	LTDestroy(&plist);
	//如果上面这里传的是一级指针  法②
	//LTDEstroy(plist) 	则需要手动把plist置为 NULL
	//plist= NULL;     里面free会有影响,但不影响plist所对应的地址
	//之所以注意这些,为了使上面的代码都是传 一级指针,考虑到保持一致性
	//还是推荐 法②
}

int main()
{
	testList();
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值