⽬录
1. 双向链表的结构
2. 实现双向链表
3. 顺序表和双向链表的分析
正⽂开始
1. 双向链表的结构
注意:这⾥的“带头”跟前⾯我们说的“头节点”是两个概念,实际前⾯的在单链表阶段称呼不严
谨,但是为了同学们更好的理解就直接称为单链表的头节点。
带头链表⾥的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,它其实就是编译器生成的随机值然后存这前驱节点和后继节点,只是站在这⾥“放哨
的”
“哨兵位”存在的意义:
遍历循环链表避免死循环。
2. 双向链表的实现
2.1完整代码
typedef int LTDataType;typedef struct ListNode{struct ListNode* next; //指针保存下⼀个节点的地址struct ListNode* prev; //指针保存前⼀个节点的地址LTDataType data;}LTNode;//void LTInit(LTNode** pphead);
LTNode* LTInit();
void LTDestroy(LTNode* phead);
void LTPrint(LTNode* phead);
bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
//在pos位置之后插⼊数据
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
LTNode *LTFind(LTNode* phead,LTDataType x);
2.2具体实现
老样子先创建三个文件
在头文件这里定义我们双向循环链表的结构
2.2.1双向链表结构体
2.2.2LTInit实现
这里我们传入的是二级指针因为我们原本传入的就是指针,我们需要对头指针进行修改所以这里的形参属于二级指针
这里初始化需要用到malloc,这里再我们写的头文件里面加上这三个需要用到的头文件
这里可以看到next和prev都已经指向了自己
这种方法也是可以的
我们现在有了一个这样的双向链表那么我们就可以插入数据了接下来就是头插入和尾插了
2.2.3LTPushBack实现
思考:下面的参数哪一个是正确的
正确答案是第二个
为什么?为什么? 为什么?为什么?为什么? 为什么?
这里可能会有疑问为什么写单项链表的时候传入的是二级指针呢
概念:双向循环链表只存在一个哨兵位(头节点)那么我们叫这个链表为空链表,上文我们说过我们传入二级指针只不过是为了改变指针的数值,但这里的头节点我们在初始化的时候就已经设置好的数值且后期不在需要更改,我们只需要修改头节点(哨兵位)的prev和next就行所以这里只需要传入指针就可。二级指针的作用就是为了改变实参的值而已
尾插需要值插入到尾部需要创建新的节点这里把创建节点封装成一个函数
2.2.3.1LTBuyNode
LTPusBack
2.2.4LTPrint实现
2.2.5LTPushFront实现
2.2.6LTPopBack的实现
2.2.7LTPopFront的实现
2.2.8 LTFind的实现
2.2.9LTInsert的实现
2.2.9LTDel的实现
2.2.9LTDesTroy的实现