网站增加权重吗,网站内页做排名,深圳大胜上海,专业做包包的网站好#x1d649;#x1d65e;#x1d658;#x1d65a;!!#x1f44f;#x1f3fb;‧✧̣̥̇‧✦#x1f44f;#x1f3fb;‧✧̣̥̇‧✦ #x1f44f;#x1f3fb;‧✧̣̥̇: Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - #xff1a;来于“云”的“羽球人”… !!‧✧̣̥̇‧✦‧✧̣̥̇‧✦ ‧✧̣̥̇: Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - 来于“云”的“羽球人”。 Talk is cheap. Show me the code ┗━━━━━━━ ➴ ⷯ 本人座右铭 欲达高峰,必忍其痛;欲戴王冠,必承其重。 自 信 希望在看完我的此篇博客后可以对你有帮助哟 此外希望各位大佬们在看完后可以互赞互关一下看到必回 目录 1. 双向链表的初始化
2. 双向链表的销毁
3. 双向链表的尾插
4. 双向链表的头插
5. 双向链表的尾删
6.双向链表的头删
7. 双向链表的指定位置之后的插入
8. 双向链表的指定位置的删除
9. 双向链表的按值查找
10.链表打印 首先在我们进行各项具体任务之前咱还是需要把前期工作准备好的 1.先进行自定义链表的声明 2.一些函数的声明 3.必要头文件的引入 首先我们要知道什么是双向链表顾名思义对于一个结点而言既有指向自己的指针也有指向其他结点的指针域
如图所示 链表结构体类型的定义代码如下
typedef struct ListNode
{DataType data;//数据域struct ListNode* pre;//前驱域struct ListNode*next;//后继域
}ListNode;1.初始化 注意这里的带头双向链表不同于前面单链表的头节点 为了好区分这里我们称之为哨兵位 哨兵位只是占一个位置并不存储任何有效的数据 当我们只有哨兵位这个结点思考一下如何变成一个双向循环链表 你想对了吗 phead-pre phead-next phead;//让他变成双向循环链表初始化代码之不采用 传参的方法
ListNode* Init()//初始化采用不传参的形式但是需把哨兵位这个结点返回
{//自己需要创建一个哨兵位ListNode* phead (ListNode*)malloc(sizeof(ListNode));assert(phead);//可能开辟失败phead-data -1;phead-pre phead-next phead;//记住这里要让他变成双向循环链表return phead;
}
初始化代码之采用 传参的方法 注意这里我们形参是用二级指针来接收的 因为实参我们需要传链表的地址否则我们是无法完成初始化的 因为函数形参只是实参的一份临时拷贝对形参的临时修改并不会影响我们的实参 void Init(ListNode** pphead)//初始化采用传参的形式
{//注意一下操作都是基于 phead是哨兵位来进行的 他只占一个位置并不存储任何有效数据assert(pphead);*pphead (ListNode*)malloc(sizeof(ListNode));if (*pphead NULL){perror(malloc fail\n);return 1;//既然开辟失败没必要执行下面代码}(*pphead)-data -1;//注意优先级顺序(*pphead)-next (*pphead)-pre NULL;//构建一个双向循环链表
}
2.销毁 说白了其实就是一个结点一个结点进行删除 这自然就需要遍历了 我们在删除之前需要先找到删除结点后一个结点 关键是我们实参到底是指针还是指针地址 让要删除的结点为 del phead-next;
那么要删除的结点下一个结点为 del-next
我们不妨试一下~~~
实参为指针
void Destroy(ListNode* phead)//链表销毁想一下传一级指针还是二级? 在这里我们传入一级指针为了保持接口一致性
{//销毁我们是一个一个进行删除自然就需要遍历assert(phead);ListNode* del phead-next;while (del ! phead){ListNode* next del-next;free(del);/*del NULL;*/ // ?del next;}//来到这说明此时只有一个哨兵位free(phead)phead NULL;} 注意当我们只是简单调用销毁函数的时候代码并不是我们所设想的一样 正常来说我们的plist的值应该为NULL 为了解决这个问题我们就需要在传一级指针的时候手动把plist 置为空 实参为指针地址 代码如下但是问题又来了
void LTDestroy(ListNode** pphead)
{assert(pphead *pphead);ListNode* cur (*pphead)-next;while ( cur!*pphead ){ListNode* next cur-next;free(cur);cur next;}free(*pphead);*pphead NULL;} 对于这个问题容本up主先买个关子欲知后事如何 且听下回分解
为了保持接口一致性我们这里就使用一级指针来接收
3.尾插 顾名思义是在尾结点后面进行插入注意这里在哨兵位的前面进行插入也是可以滴~~~ 接下来就是改变指针的指向 1首先既然是尾插就需要为要插进来的数据开辟结点 这里涉及到创建结点的函数 2其次是处理指针指向 3先处理node的pre ,next 4)处理 原来尾结点哨兵位 尾插草图如下 结点创建函数的代码如下
ListNode* ListBuyNode(x)//创建结点
{ListNode* node (ListNode*)malloc(sizeof(ListNode));if (node NULL){perror(malloc fail\n);return;}//来到这说明空间开辟成功node-data x;node-next node-pre NULL;return node;
} 尾插完整代码
void PushBack(ListNode* phead,DataType x)//尾插
{assert(phead);//为插入的数据开辟空间ListNode* node ListBuyNode(x);//先处理node 的前驱后继node-pre phead-pre; // phead-pre-next原来尾结点的后继,node-pre phead-pre-next这里出现了野指针的问题 phead-pre-next没有具体指向node-next phead;//接下来在处理 原来尾结点哨兵位/*,这样写是错的以下2句话不可以颠倒已经把node这个结点给覆盖掉了在执行65行代码时node 是未知的因为这个结点已经被覆盖了phead-pre-next此时他的指向也就是未知的调试时不会报错但是遍历读取他就会报错了就像你在打印函数里进行打印时出现野指针phead-pre node;phead-pre-next node;*/phead-pre-next node;phead-pre node;//别忘了每尾插进来的结点最终都是一个新的尾结点
}
4.头插 首先是在哨兵位是后面进行的每插入进来应该结点此节点就会成为一个新的头节点 同上需要先创建一个结点 其次找到原来头节点 phead-next 头插草图如下 头插完整代码如下
void PushFront(ListNode* phead,DataType x)//头插
{//头插是在哨兵位的后面进行插入assert(phead);//为插入的数据开辟空间ListNode* node ListBuyNode(x);//处理node 的后继前驱node-pre phead;node-next phead-next;//处理phead ,phead-next /*这样写也是对的phead-next-pre node;*/phead-next node;//头节点要进行更新phead-next-pre node;}
5.尾删 注意我们不能上来就行删除 需要先找到要删除结点 (del)的前一个也就是 del-pre 这里我们一定要注意结点的先后顺序 完整代码如下
void PopBack(ListNode* phead)//尾删
{//注意不能直接删除需要先找到尾结点的前一个结点assert(phead);//先判断以下是否为空,有2种写法/*if (phead-pre phead){return 9;}*/assert(phead-next ! phead);//是否为空ListNode* del phead-pre;//把要删除的结点先保存起来//删除之前先要构成一个新的双向循环链表del-pre-next phead;phead-pre del-pre;/*错误的顺序不能颠倒因为此时 del-pre已经被覆盖了phead-pre del-pre;//新的尾结点del-pre-next phead;*/free(del);del NULL;} 7. 指定位置之后的插入
草图如下 1要为插入的数据创建结点 2找到指定位置pos之后的结点 pos-next和之前的结点pos-pre 3) 改变指针指向 指定位置之后插入对应完整代码
void InsertAfter(ListNode* pos, DataType x)//指定位置之后插入
{assert(pos);//为插入的数据开辟空间ListNode* node ListBuyNode(x);//处理 node 的前驱后继node-next pos-next;node-pre pos;//处理 pos 和 pos-next的prepos-next node;pos-next-pre node;}8. 双向链表的指定位置的删除
对应草图如下 直接进行删除是不行滴~~~ 先要找到指定位置pos之后的结点 pos-next和之前的结点pos-pre 其次改变指针走向 void Earse(ListNode* pos)//指定位置删除
{assert(pos);//需要找到pos之前的一个结点和之后的一个结点pos-next-pre pos-pre;pos-pre-next pos-next;free(pos);pos NULL;
}9. 双向链表的按值查找 这里自然就需要一个一个进行遍历了 按值查找对应完整代码
ListNode* Find(ListNode* phead, DataType x)//在链表中按值查找,若是找到则返回对应的结点
{assert(phead);ListNode* pcur phead-next;//定义一个用来循环遍历的指针while (pcur ! phead){if (pcur-data x){return pcur;//直接返回对应结点}pcur pcur-next;}printf(没有找到\n);
}10链表打印
方法同上对链表进行遍历
void Print(ListNode* phead)//链表打印
{assert(phead);ListNode* pcur phead-next;//定义一个用来 遍历的指针注意他初始值是 phead-nextwhile (pcur ! phead){printf(%d- , pcur-data);pcur pcur-next;//记得更新}printf(\n);
}List.c对应完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#includeList.h//void Init(ListNode** pphead)//初始化采用传参的形式
//{
// //注意一下操作都是基于 phead是哨兵位来进行的 他只占一个位置并不存储任何有效数据
// assert(pphead);
// *pphead (ListNode*)malloc(sizeof(ListNode));
// if (*pphead NULL)
// {
// perror(malloc fail\n);
// return 1;//既然开辟失败没必要执行下面代码
// }
//
// (*pphead)-data -1;//注意优先级顺序
// (*pphead)-next (*pphead)-pre NULL;//构建一个双向循环链表
//}ListNode* Init()//初始化采用不传参的形式但是需把哨兵位这个结点返回
{//自己需要创建一个哨兵位ListNode* phead (ListNode*)malloc(sizeof(ListNode));assert(phead);//可能开辟失败phead-data -1;phead-pre phead-next phead;//记住这里要让他变成双向循环链表return phead;
}
ListNode* ListBuyNode(x)//创建结点
{ListNode* node (ListNode*)malloc(sizeof(ListNode));if (node NULL){perror(malloc fail\n);return;}//来到这说明空间开辟成功node-data x;node-next node-pre NULL;return node;
}
void Print(ListNode* phead)//链表打印
{assert(phead);ListNode* pcur phead-next;//定义一个用来 遍历的指针注意他初始值是 phead-nextwhile (pcur ! phead){printf(%d- , pcur-data);pcur pcur-next;//记得更新}printf(\n);
}void PushBack(ListNode* phead,DataType x)//尾插
{assert(phead);//为插入的数据开辟空间ListNode* node ListBuyNode(x);//先处理node 的前驱后继node-pre phead-pre; // phead-pre-next原来尾结点的后继,node-pre phead-pre-next这里出现了野指针的问题 phead-pre-next没有具体指向node-next phead;//接下来在处理 原来尾结点哨兵位/*,这样写是错的以下2句话不可以颠倒已经把node这个结点给覆盖掉了在执行65行代码时node 是未知的因为这个结点已经被覆盖了phead-pre-next此时他的指向也就是未知的调试时不会报错但是遍历读取他就会报错了就像你在打印函数里进行打印时出现野指针phead-pre node;phead-pre-next node;*/phead-pre-next node;phead-pre node;//别忘了每尾插进来的结点最终都是一个新的尾结点
}
void PushFront(ListNode* phead,DataType x)//头插
{//头插是在哨兵位的后面进行插入assert(phead);//为插入的数据开辟空间ListNode* node ListBuyNode(x);//处理node 的后继前驱node-pre phead;node-next phead-next;//处理phead ,phead-next /*这样写也是对的phead-next-pre node;*/phead-next node;//头节点要进行更新phead-next-pre node;}
void PopBack(ListNode* phead)//尾删
{//注意不能直接删除需要先找到尾结点的前一个结点assert(phead);//先判断以下是否为空,有2种写法/*if (phead-pre phead){return 9;}*/assert(phead-next ! phead);//是否为空ListNode* del phead-pre;//把要删除的结点先保存起来//删除之前先要构成一个新的双向循环链表del-pre-next phead;phead-pre del-pre;/*错误的顺序不能颠倒因为此时 del-pre已经被覆盖了phead-pre del-pre;//新的尾结点del-pre-next phead;*/free(del);del NULL;}
void PopFront(ListNode* phead)//头删
{assert(phead);assert(phead-next ! phead);//确保不为空ListNode* del phead-next;//以下2句没有先后顺序之分del-next-pre phead;phead-next del-next;free(del);del NULL;}
void InsertAfter(ListNode* pos, DataType x)//指定位置之后插入
{assert(pos);//为插入的数据开辟空间ListNode* node ListBuyNode(x);//处理 node 的前驱后继node-next pos-next;node-pre pos;//处理 pos 和 pos-next的prepos-next node;pos-next-pre node;}
void Earse(ListNode* pos)//指定位置删除
{assert(pos);//需要找到pos之前的一个结点和之后的一个结点pos-next-pre pos-pre;pos-pre-next pos-next;free(pos);pos NULL;
}
ListNode* Find(ListNode* phead, DataType x)//在链表中按值查找,若是找到则返回对应的结点
{assert(phead);ListNode* pcur phead-next;//定义一个用来循环遍历的指针while (pcur ! phead){if (pcur-data x){return pcur;//直接返回对应结点}pcur pcur-next;}printf(没有找到\n);
}
void Destroy(ListNode* phead)//链表销毁想一下传一级指针还是二级? 在这里我们传入一级指针为了保持接口一致性
{//销毁我们是一个一个进行删除自然就需要遍历assert(phead);ListNode* del phead-next;while (del ! phead){ListNode* next del-next;free(del);/*del NULL;*/ // ?del next;}//来到这说明此时只有一个哨兵位free(phead);phead NULL;}
void LTDestroy(ListNode** pphead)
{assert(pphead *pphead);ListNode* cur (*pphead)-next;while ( cur!*pphead ){ListNode* next cur-next;free(cur);cur next;}free(*pphead);*pphead NULL;}List.h对应完整代码
#pragma once
#includestdio.h
#includeassert.h
#includestdbool.h#includestdlib.h
//双向链表的实现
typedef int DataType;
typedef struct ListNode {DataType data;//数据域struct ListNode* pre;//前驱域struct ListNode*next;//后继域
}ListNode;//接口函数的声明
//void Init(ListNode** pphead);//初始化采用传参的形式
ListNode* Init();//初始化采用不传参的形式但是需把哨兵位这个结点返回
void Print(ListNode* phead);//链表打印
void PushBack(ListNode* phead,DataType x);//尾插,这里传入一级指针即可因为返回的头节点我们不需要进行更改
void PushFront(ListNode* phead, DataType x);//头插
void PopBack(ListNode* phead);//尾删
void PopFront(ListNode* phead);//头删
void InsertAfter(ListNode* pos,DataType x);//指定位置之后插入
void Earse(ListNode* pos);//指定位置删除
ListNode* Find(ListNode*phead,DataType x);//按值查找,若是找到则返回对应的结点
void Destroy(ListNode* phead);//链表销毁想一下传一级指针还是二级为了保持接口一致性我们传入一级指针
ok,以上就是我要为大家进行share的一些基本内容都来到这里了要是感觉我写的还不错的话各位大佬烦劳点个赞互关以下呗~~~