上篇文章我们实现了单链表

#3单链表的实现#_努力的小恒的博客

这次我们来实现双向链表

先回顾一下双向链表结构

1.双向链表的概念及其结构

我们将实现带头双向循环链表的增删查改 最常用的双向链表

带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

python 双向链表代码 双向链表实现_#include

 

2.准备工作

VS2022下创建

List.h:函数声明

List.c:函数实现

test.c:功能测试

3.双向链表结构体的定义和头文件的引用

List.h

#pragma once

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

typedef int LTDateType;

typedef struct ListNode
{
	LTDateType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

data:放数据

next:指向下一个地址

prev:指向上一个

4.初始化

1°思路

开辟一个哨兵位的头节点(带头)

实际上不放数据 当只有哨兵位的头节点时实现尾插和头插不需要分类 因为已经有头了

这个头的prev指向自己 next也要指向自己

python 双向链表代码 双向链表实现_#include_02

 

当只有head的时候 相当于head->next要指向head自己 head->prev也要指向head自己

2.实现

List.h

//初始化
LTNode* ListInit();

List.c

//初始化
LTNode* ListInit()
{
	//哨兵位头节点
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

先得到一个哨兵位的头节点phead返回

测试的时候用一个指针接受头部 这样就拿到了哨兵位的头节点

5.打印

1.思路

遍历整个链表 通过访问data打印数据

和单链表的遍历一样 循环条件改一下

图解可见:

#3单链表的实现#_努力的小恒的博客

2.实现

List.h

//打印
void ListPrint(LTNode* phead);

List.c

//打印
void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

assert断言 哨兵位头节点为空的话打印不了

以cur为phead->next往下走 当走了一圈回来后到phead就停

6.尾插

1°思路

开辟一个新节点newnode 放数据

用malloc函数开辟存成newnode

data放数据 prev和next都制空

返回newnode

然后在其他函数内部开辟的时候

用newnode接收

连接phead tail(原来尾部) newnode

tail的next指向newnode的prev

newnode的prev指向tail

newnode的next指向phead

phead的prev指向newnode

python 双向链表代码 双向链表实现_python 双向链表代码_03

2°实现

List.h

//尾插
void ListPushBack(LTNode* phead, LTDateType x);

List.c

//开辟新节点并放入数据
LTNode* BuyListNode(LTDateType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

//尾插
void ListPushBack(LTNode* phead, LTDateType x)
{
	//哨兵位头节点还在
	assert(phead);
	//尾部就是头部的prev
	LTNode* tail = phead->prev;
	LTNode* newnode = BuyListNode(x);
	//tail和newnode链接
	tail->next = newnode;
	newnode->prev = tail;
	//newnode和phead链接
	newnode->next = phead;
	phead->prev = newnode;
}

开辟后返回newnode

assert断言说明至少有一个哨兵位的头节点

3°测试

test.c

#include "List.h"

void TestList1()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//打印1 2 3 4
	ListPrint(plist);
}

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

运行结果 

python 双向链表代码 双向链表实现_数据结构_04

 

7.尾删

1°思路

先通过phead的prev找到尾部tail

再通过尾部tail的prev找到tailPrev(也就是尾部的前一个)

再做好phead和tailPrev的连接:

phead->prev=tailPrev

tailPrev->next=phead

最后释放tail

python 双向链表代码 双向链表实现_python 双向链表代码_05

2°实现

List.h

//尾删
void ListPopBack(LTNode* phead);

List.c

//尾删
void ListPopBack(LTNode* phead)
{
	//phead不为空
	assert(phead);
	//说明链表为空 phead->next指向自己
	//只有一个哨兵位的头节点 不能再删了
	assert(phead->next != phead);
	LTNode* tail = phead->prev;
	tail->prev->next = phead;
	phead->prev = tail->prev;
	free(tail);
}

assert断言 phead不为空 有哨兵位的头节点

assert断言 phead->next不指向自己 说明链表不只有phead 还有其他数据 就可以删除

如果只有phead 那就不能删除

3°测试

test.c

#include "List.h"

void TestList2()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//尾删2次
	ListPopBack(plist);
	ListPopBack(plist);
	//打印1 2
	ListPrint(plist);
}

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

运行结果 

python 双向链表代码 双向链表实现_List_06

8.头插

1°思路

注意我们已经有了一个哨兵位的头

再头插相当于在phead和phead的下一个之间插入一个新节点

先开辟一个新节点newnode

先通过phead的next找到pheadNext

再做好phead newnode pheadNext的连接:

phead->next=newnode

newnode->prev=phead

newnode->next=pheadNext

pheadNext->prev=newnode

python 双向链表代码 双向链表实现_List_07

 
2°实现

List.h

//头插
void ListPushFront(LTNode* phead, LTDateType x);

List.c

//头插
void ListPushFront(LTNode* phead, LTDateType x)
{
	assert(phead);
	LTNode* next = phead->next;
	LTNode* newnode = BuyListNode(x);
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;
}

assert断言 phead不为空 至少有哨兵位的头节点

3°测试

test.c

#include "List.h"

TestList3()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//头插1 2 3 4
	ListPushFront(plist, 1);
	ListPushFront(plist, 2);
	ListPushFront(plist, 3);
	ListPushFront(plist, 4);
	//打印4 3 2 1
	ListPrint(plist);
}

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

运行结果 

python 双向链表代码 双向链表实现_List_08

9.头删

1°思路

先通过phead的next找到pheadNext

再通过phead的next的next找到pheadnextNext

再做好phead和pheadnextNext的连接:

phead->next=pheadnextNext

pheadnextNext->prev=phead

最后释放pheadnext

python 双向链表代码 双向链表实现_数据结构_09

2°实现

List.h

//头删
void ListPopFront(LTNode* phead);

List.c

//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);
	//防止删除哨兵位
	assert(phead->next != phead);
	LTNode* next = phead->next;
	LTNode* nextNext = next->next;
	phead->next = nextNext;
	nextNext->prev = phead;
	free(next);
}

assert(phead)至少有一个哨兵位节点

assert(phead->next!=phead)说明不只有哨兵位 还有其他节点

如果只有哨兵位就防止删除哨兵位

3°测试

test.c

#include "List.h"
TestList4()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//头删2次
	ListPopFront(plist);
	ListPopFront(plist);
	//打印3 4
	ListPrint(plist);
}

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

运行结果 

python 双向链表代码 双向链表实现_python 双向链表代码_10

10.查找/修改

1°思路

给一个cur指针 从phead的下一个(真正的头部)开始遍历整个链表

找到了就返回cur 找不到就返回NULL

2°实现

List.h

//找
LTNode* ListFind(LTNode* phead, LTDateType x);

List.c

//找
LTNode* ListFind(LTNode* phead, LTDateType x)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

调用的时候定义一个pos指针接收

修改可以通过pos指针访问data进行修改

3°测试

test.c

#include "List.h"
TestList5()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 2 4 2
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 2);
	ListPushBack(plist, 4);
	ListPushBack(plist, 2);
	//查找3
	//把第一个3改成30
	LTNode* pos = ListFind(plist, 3);
	int i = 1;
	if (pos)
	{
		printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);
		LTNode* ppos = pos;
		while (ppos)
		{
			//后一个位置继续开始找
			ppos = ListFind(ppos->next, 3);
			if (ppos == pos)
				break;
			printf("第%d个pos节点:%p->%d\n", i++, ppos, pos->data);
		}
		if (pos)
			pos->data = 30;
		else
			printf("未找到此数据 修改失败\n");
	}
	else
	{
		printf("未找到此数据\n");
	}
	//打印1 2 30 2 4 2
	ListPrint(plist);
	printf("\n");
	//查找2
	//把第二个2改成20
	//3个2的地址
	pos = ListFind(plist, 2);
	if (pos)
	{
		i = 1;
		printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);
		LTNode* ppos = pos;
		while (ppos)
		{
			//后一个位置继续开始找
			ppos = ListFind(ppos->next, 2);
			if (ppos == pos)
				break;
			if (ppos && i == 2)
				ppos->data = 20;
			printf("第%d个pos节点:%p->%d\n", i++, ppos, pos->data);
		}
	}
	//打印1 2 30 20 4 2
	ListPrint(plist);
	printf("\n");
	//查找100
	pos = ListFind(plist, 100);
	i = 1;
	if (pos)
	{
		printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);
		LTNode* ppos = pos;
		while (ppos)
		{
			//后一个位置继续开始找
			ppos = ListFind(ppos->next, 3);
			if (ppos == pos)
				break;
			printf("第%d个pos节点:%p->%d\n", i++, ppos, pos->data);
		}
	}
	else
	{
		printf("未找到此数据\n");
	}
}

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

相较于单链表中的查找/修改有了升级

1:查找会打印出地址

2:单链表中只实现了查找多个数据 不能修改后面的数据

这里可以修改第二个2

运行结果

python 双向链表代码 双向链表实现_List_11

 

11.在pos之前插入

1°思路

开辟一个新节点newnode

先通过pos的prev找到posPrev

再连接posPrev newnode pos:

posPrev->next=newnode

newnode->prev=posPrev

newnode->next=pos

pos->prev=newnode

python 双向链表代码 双向链表实现_python 双向链表代码_12

2°实现

List.h

//在pos之前插入x
void ListInsert(LTNode* pos, LTDateType x);

List.c

//在pos之前插入x
void ListInsert(LTNode* pos, LTDateType x)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* newnode = BuyListNode(x);
	//posPrev newnode pos
	posPrev->next = newnode;
	newnode->prev = posPrev;
	newnode->next = pos;
	pos->prev = newnode;
}

assert(pos) pos不为NULL

如果pos为NULL posPrev=pos->prev pos的解引用 空指针解引用非法访问

3°测试

test.c

#include "List.h"

TestList6()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//在2前面插入10
	LTNode* pos = ListFind(plist, 2);
	if (pos)
		ListInsert(pos, 10);
	else
		printf("无该数据 插入失败\n");
	//打印1 10 2 3 4
	ListPrint(plist);
	printf("\n");
	
	//在100前面插入10
	pos = ListFind(plist, 100);
	if (pos)
		ListInsert(pos, 10);
	else
		printf("无该数据 插入失败\n");
	//打印1 10 2 3 4
	ListPrint(plist);
}

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

运行结果

python 双向链表代码 双向链表实现_List_13

既然实现在pos之前插入

头插和尾插可以复用

改进如下:

头插:

//头插
void ListPushFront(LTNode* phead, LTDateType x)
{
	ListInsert(phead->next, x);
}

pos就是phead的下一个

尾插:

//尾插
void ListPushBack(LTNode* phead, LTDateType x)
{
	ListInsert(phead, x);
}

phead的上一个就是尾部 所以phead就是pos

测试情况test.c

#include "List.h"

TestList7()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插3 2 1
	ListPushBack(plist, 3);
	ListPushBack(plist, 2);
	ListPushBack(plist, 1);
	//打印3 2 1
	ListPrint(plist);
	//头插4 5 6
	ListPushFront(plist, 4);
	ListPushFront(plist, 5);
	ListPushFront(plist, 6);
	//打印6 5 4 3 2 1
	ListPrint(plist);
}
int main()
{
	TestList7();
	return 0;
}

运行结果 

python 双向链表代码 双向链表实现_List_14

12.在pos位置删除

1°思路

先通过pos的prev找到posPrev

再通过pos的next找到posNext

再连接posPrev posNext:

posPrev->next=posNext

posNext->prev=posPrev

最后释放pos

python 双向链表代码 双向链表实现_#include_15

2°实现

List.h

//在pos位置删除
void ListErase(LTNode* pos);

List.c

//在pos位置删除
void ListErase(LTNode* pos)
{
	assert(pos);
	assert(pos->next != pos);
	LTNode* posPrev = pos->prev;
	LTNode* posNext = pos->next;
	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
	pos = NULL;
}

assert(pos) pos不为空 空指针不能删除

assert(pos->next!=pos) pos不为phead phead不能删除

最后释放

3°测试

test.c

#include "List.h"

TestList8()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//删除2
	LTNode* pos = ListFind(plist, 2);
	if (pos)
	{
		ListErase(pos);
	}
	else
		printf("无此数据 删除失败\n");
	//打印1 3 4
	ListPrint(plist);
	printf("\n");
	//删除100
	pos = ListFind(plist, 100);
	if (pos)
	{
		ListErase(pos);
	}
	else
		printf("无此数据 删除失败\n");
	//打印1 3 4
	ListPrint(plist);
}

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

运行结果

python 双向链表代码 双向链表实现_链表_16

实现在pos位置删除

那么头删和尾删可以复用

改进如下:

头删:

//头删
void ListPopFront(LTNode* phead)
{
	ListErase(phead->next);
}

phead的下一个就是pos

尾删:

//尾删
void ListPopBack(LTNode* phead)
{
	ListErase(phead->prev);
}

phead的上一个就是pos

测试结果

#include "List.h"
TestList9()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//打印1 2 3 4
	ListPrint(plist);
	//头删1次
	ListPopFront(plist);
	//打印2 3 4
	ListPrint(plist);
	//尾删1次
	ListPopBack(plist);
	//打印2 3
	ListPrint(plist);
}

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

运行结果

python 双向链表代码 双向链表实现_数据结构_17

13.销毁

1°思路

给一个cur指针为phead->next开始遍历链表

循环条件cur!=phead

先把下一个存着再释放

定义next=cur->next

释放cur

再把next给到cur

python 双向链表代码 双向链表实现_python 双向链表代码_18

2°实现

List.h

//销毁加制空
void ListDestroy(LTNode* phead);

List.c

//销毁
void Destroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	//里面制空要传二级指针才能改变
	//所以要在外面制空 或者写一个大函数里面制空
 	phead = NULL;
}

//销毁加制空
void ListDestroy(LTNode* phead)
{
	Destroy(phead);
	phead = NULL;
}

为了使用的时候不用再制空

写一个大函数在大函数里面制空

test.c

#include "List.h"
TestList10()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//打印1 2 3 4
	ListPrint(plist);
	//销毁
	ListDestroy(plist);
	plist = NULL;
	//链表为空 打印不了 assert断言
	ListPrint(plist);
}

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

运行结果 

python 双向链表代码 双向链表实现_python 双向链表代码_19

 

14.完整代码

List.h

#pragma once

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

typedef int LTDateType;

typedef struct ListNode
{
	LTDateType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

//初始化
LTNode* ListInit();

//打印
void ListPrint(LTNode* phead);

//尾插
void ListPushBack(LTNode* phead, LTDateType x);

//尾删
void ListPopBack(LTNode* phead);

//头插
void ListPushFront(LTNode* phead, LTDateType x);

//头删
void ListPopFront(LTNode* phead);

//找
LTNode* ListFind(LTNode* phead, LTDateType x);

//在pos之前插入x
void ListInsert(LTNode* pos, LTDateType x);

//在pos位置删除
void ListErase(LTNode* pos);

//销毁加制空
void ListDestroy(LTNode* phead);

List.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "List.h"

//初始化
LTNode* ListInit()
{
	//哨兵位头节点
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

//开辟新节点并放入数据
LTNode* BuyListNode(LTDateType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

//打印
void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

//尾插
void ListPushBack(LTNode* phead, LTDateType x)
{
	哨兵位头节点还在
	//assert(phead);
	尾部就是头部的prev
	//LTNode* tail = phead->prev;
	//LTNode* newnode = BuyListNode(x);
	tail和newnode链接
	//tail->next = newnode;
	//newnode->prev = tail;
	newnode和phead链接
	//newnode->next = phead;
	//phead->prev = newnode;
	ListInsert(phead, x);
}

//尾删
void ListPopBack(LTNode* phead)
{
	phead不为空
	//assert(phead);
	说明链表为空 phead->next指向自己
	只有一个哨兵位的头节点 不能再删了
	//assert(phead->next != phead);
	//LTNode* tail = phead->prev;
	//tail->prev->next = phead;
	//phead->prev = tail->prev;
	//free(tail);
	ListErase(phead->prev);
}

//头插
void ListPushFront(LTNode* phead, LTDateType x)
{
	//assert(phead);
	//LTNode* next = phead->next;
	//LTNode* newnode = BuyListNode(x);
	//phead->next = newnode;
	//newnode->prev = phead;
	//newnode->next = next;
	//next->prev = newnode;
	ListInsert(phead->next, x);
}

//头删
void ListPopFront(LTNode* phead)
{
	//assert(phead);
	防止删除哨兵位
	//assert(phead->next != phead);
	//LTNode* next = phead->next;
	//LTNode* nextNext = next->next;
	//phead->next = nextNext;
	//nextNext->prev = phead;
	//free(next);
	ListErase(phead->next);
}

//找
LTNode* ListFind(LTNode* phead, LTDateType x)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//在pos之前插入x
void ListInsert(LTNode* pos, LTDateType x)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* newnode = BuyListNode(x);
	//posPrev newnode pos
	posPrev->next = newnode;
	newnode->prev = posPrev;
	newnode->next = pos;
	pos->prev = newnode;
}

//在pos位置删除
void ListErase(LTNode* pos)
{
	assert(pos);
	assert(pos->next != pos);
	LTNode* posPrev = pos->prev;
	LTNode* posNext = pos->next;
	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
	pos = NULL;
}

//销毁
void Destroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	//里面制空要传二级指针才能改变
	//所以要在外面制空 或者写一个大函数里面制空
 	phead = NULL;
}

//销毁加制空
void ListDestroy(LTNode* phead)
{
	Destroy(phead);
	phead = NULL;
}

test.c

#include "List.h"

void TestList1()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//打印1 2 3 4
	ListPrint(plist);
}

void TestList2()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//尾删2次
	ListPopBack(plist);
	ListPopBack(plist);
	//打印1 2
	ListPrint(plist);
}

TestList3()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//头插1 2 3 4
	ListPushFront(plist, 1);
	ListPushFront(plist, 2);
	ListPushFront(plist, 3);
	ListPushFront(plist, 4);
	//打印4 3 2 1
	ListPrint(plist);
}

TestList4()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//头删2次
	ListPopFront(plist);
	ListPopFront(plist);
	//打印3 4
	ListPrint(plist);
}

TestList5()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 2 4 2
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 2);
	ListPushBack(plist, 4);
	ListPushBack(plist, 2);
	//查找3
	//把第一个3改成30
	LTNode* pos = ListFind(plist, 3);
	int i = 1;
	if (pos)
	{
		printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);
		LTNode* ppos = pos;
		while (ppos)
		{
			//后一个位置继续开始找
			ppos = ListFind(ppos->next, 3);
			if (ppos == pos)
				break;
			printf("第%d个pos节点:%p->%d\n", i++, ppos, pos->data);
		}
		if (pos)
			pos->data = 30;
		else
			printf("未找到此数据 修改失败\n");
	}
	else
	{
		printf("未找到此数据\n");
	}
	//打印1 2 30 2 4 2
	ListPrint(plist);
	printf("\n");
	//查找2
	//把第二个2改成20
	//3个2的地址
	pos = ListFind(plist, 2);
	if (pos)
	{
		i = 1;
		printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);
		LTNode* ppos = pos;
		while (ppos)
		{
			//后一个位置继续开始找
			ppos = ListFind(ppos->next, 2);
			if (ppos == pos)
				break;
			if (ppos && i == 2)
				ppos->data = 20;
			printf("第%d个pos节点:%p->%d\n", i++, ppos, pos->data);
		}
	}
	//打印1 2 30 20 4 2
	ListPrint(plist);
	printf("\n");
	//查找100
	pos = ListFind(plist, 100);
	i = 1;
	if (pos)
	{
		printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);
		LTNode* ppos = pos;
		while (ppos)
		{
			//后一个位置继续开始找
			ppos = ListFind(ppos->next, 3);
			if (ppos == pos)
				break;
			printf("第%d个pos节点:%p->%d\n", i++, ppos, pos->data);
		}
	}
	else
	{
		printf("未找到此数据\n");
	}
}

TestList6()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//在2前面插入10
	LTNode* pos = ListFind(plist, 2);
	if (pos)
		ListInsert(pos, 10);
	else
		printf("无该数据 插入失败\n");
	//打印1 10 2 3 4
	ListPrint(plist);
	printf("\n");
	
	//在100前面插入10
	pos = ListFind(plist, 100);
	if (pos)
		ListInsert(pos, 10);
	else
		printf("无该数据 插入失败\n");
	//打印1 10 2 3 4
	ListPrint(plist);
}

TestList7()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插3 2 1
	ListPushBack(plist, 3);
	ListPushBack(plist, 2);
	ListPushBack(plist, 1);
	//打印3 2 1
	ListPrint(plist);
	//头插4 5 6
	ListPushFront(plist, 4);
	ListPushFront(plist, 5);
	ListPushFront(plist, 6);
	//打印6 5 4 3 2 1
	ListPrint(plist);
}

TestList8()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//删除2
	LTNode* pos = ListFind(plist, 2);
	if (pos)
	{
		ListErase(pos);
	}
	else
		printf("无此数据 删除失败\n");
	//打印1 3 4
	ListPrint(plist);
	printf("\n");
	//删除100
	pos = ListFind(plist, 100);
	if (pos)
	{
		ListErase(pos);
	}
	else
		printf("无此数据 删除失败\n");
	//打印1 3 4
	ListPrint(plist);
}
TestList9()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//打印1 2 3 4
	ListPrint(plist);
	//头删1次
	ListPopFront(plist);
	//打印2 3 4
	ListPrint(plist);
	//尾删1次
	ListPopBack(plist);
	//打印2 3
	ListPrint(plist);
}

TestList10()
{
	//初始化 头部为plist
	LTNode* plist = ListInit();
	//尾插1 2 3 4 
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	//打印1 2 3 4
	ListPrint(plist);
	//销毁
	ListDestroy(plist);
	plist = NULL;
	//链表为空 打印不了 assert断言
	ListPrint(plist);
}

int main()
{
	//TestList1();//测试尾插
	//TestList2();//测试尾删
	//TestList3();//测试头插
	//TestList4();//测试头删
	//TestList5();//测试查找/修改
	//TestList6();//测试在pos之前插入
	//TestList7();//测试在pos之前插入实现后复用头插和尾插
	//TestList8();//测试在pos位置删除
	//TestList9();//测试在pos位置删除实现后复用头删和尾删
	//TestList10();//测试销毁加制空
	return 0;
}

菜单版和单链表原理一样

单链表的菜单版未实现多次插入同一个数据后修改第n个数据

通过一些输入和输出也可以实现

双向链表菜单版可参考单链表的菜单版 把函数名字改一下

详细见:#3单链表的实现#_努力的小恒的博客

15.总结

顺序表和链表的区别
这两个结构各有优势
很难说谁更优
严格来说他们说 相辅相成的两个结构

顺序表
优点:
1.支持随机访问 需要随机访问结构支持算法可以很好的适用
2.cpu高速缓存命中率更高

缺点:
1.头部中部插入删除时间效率低 O(N)
2.连续的物理空间 空间不够了以后需要增容

增容有一定程度消耗
为了避免频繁增容 一般我们都按倍数去增
用不完可能存在一定的空间浪费

链表(双向带头循环链表):
优点:
1.任意位置插入删除效率高 O(1)
2.按需申请释放空间

缺点:
1.不支持随机访问(用下标访问)
意味着:一些排序 二分查找等在这种
结构上不适用
2.链表存储一个值 同时要存储链接指针
也有一定的消耗
3.cpu高速缓存命中率更低

#4双向链表的实现及总结#完