目录
一、概念
循环链表虽然能够实现从任一结点出发沿着链能找到其前驱结点,但时间消耗0(n),双向链表可以实现快速查找到前驱。例如用链表实现逆置时需要很多前移和后移的操作,很麻烦,而引入双向链表,增加对当前结点前驱的存储就很便捷。
结点定义代码如下:
typedef struct DNode
{
int data;//数据
struct DNode* next;//后继指针
struct DNode* prior;//前驱指针
}DNode,*DList;
二、双向链表基本操作
只有涉及到前驱的头插、尾插、删除、函数需要修改,别的函数与单链表写法一致
①初始化
//初始化
//前驱和后继置空
void InitList(DList plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return;
}
plist->next = NULL;
plist->prio = NULL;
}
②头插法
步骤:
- 创建新结点
- 赋值
- 插入到头部
bool Insert_head(DList plist, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
//创建新结点
DNode* p = (DNode*)malloc(sizeof(DNode));
assert(p != NULL);
p->data = val;
//插入新结点
p->next = plist->next;//1
plist->next = p;//2
p->prio = plist;//把前驱结点也连接上,3
if (p->next != NULL)//判断很重要
{
p->next->prio = p;//4
}
return true;
}
代码解释:
- 1.把p结点插入到plist后面
- 断开原结点,将p连接到plist后面(把plist->next的地址500存入到p->next内)
- p->next=plist->next
- 2.连接p与plist(将p的地址800存入到plist->next内)
- plist->next=p
- 3.连接p前驱结点plist p->prior=plist;
- 4.连接p->next的前驱p
- p->next->prior=p;
- !!此处直接这样写会报错,因为p->next可能为NULL
- 比如这样的结点。因此需要对其判空操作。
③尾插
步骤:
- 创建新结点
- 赋值
- 找尾巴
- 插入到尾部处//只有这一步代码要修改,涉及到前驱结点
//尾插(考试重点)
bool Insert_tail(DList plist, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
//创建新结点
DNode* p = (DNode*)malloc(sizeof(DNode));
assert(p != NULL);
p->data = val;
//找尾巴
DNode* q;
for (q = plist; q->next != NULL; q = q->next)
{
;
}
//插入数据,将p插入到q的后面
p->next = q->next;//也可以写:p->next=NULL;
p->prio = q;
q->next = p;
return true;
}
④输出函数
遍历链表,输出数值
//输出plist的所有数据
void Show(DList plist)
{
for (DNode* p = plist->next; p != NULL; p = p->next)
{
printf("%d", p->data);
}
printf("\n");
}
⑤插入
插入数据,在plist链表的pos位置插入val
//插入数据,在plist链表的pos位置插入val;
bool Insert(DList plist, int pos, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
if (pos<0 || pos>GetLength(plist))
{
return false;
}
//申请结点
DNode* p = (DNode*)malloc(sizeof(DNode));
assert(p != NULL);
p->data = val;
//找位置
DNode* q;
int i = 0;
for (q = plist; i < pos; i++, q = q->next)
{
;
}
//插入数据
//将p插入到q的后面;
p->next = q->next;
q->next = p;
p->prio = q;
if (p->next != NULL)
{
p->next->prio = p;
}
return true;
}
⑥查找
遍历链表,在plist查找第一个key值,找到返回结点地址,没有找到返回NULL
//在plist查找第一个key值,找到返回结点地址,没有找到返回NULL;
DNode* Search(DList plist, int key)
{
assert(plist != NULL);
if (plist == NULL)
{
return NULL;
}
for (DNode* p = plist->next; p != NULL; p = p->next)
{
if(p->data==key)
{
return p;
}
}
return NULL;
}
⑦获取前驱
//返回key的前驱地址,如果不存在返回NULL;
DNode* GetPrio(DList plist, int key)
{
DNode* p = Search(plist, key);
return p == NULL ? NULL : p->prio;
}
⑧获取后继
//返回key的后继地址,如果不存在返回NULL;
DNode* GetNext(DList plist, int key)
{
DNode* p = Search(plist, key);
return p == NULL ? NULL : p->next;
}
⑨删除数据
调用上面search函数,返回查找到数据的结点,删除该结点
删除操作为:将该结点的前驱的后继指向该节点的后继,该节点后继的前驱指向该节点的前驱。
//删除第一个val的值(考试重点)
bool DelVal(DList plist, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
DNode* p = Search(plist, val);
if (p == NULL)
{
return false;
}
p->prio->next = p->next;
if (p->next != NULL)
{
p->next->prio = p->prio;
}
free(p);
return true;
}
⑩删除
删除pos位置的值
//删除pos位置的值
bool DelPos(DList plist, int pos)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
if (pos < 0 || pos >= GetLength(plist))
{
return false;
}
//找位置
DNode* p;
int i;
for (p = plist, i = 0; i < pos; i++, p = p->next)
{
;
}
DNode* q = p->next;//q是要删除的点
//删除
p->next = q->next;
if (q->next != NULL)
{
q->next->prio = q->prio;
}
free(q);
return true;
}
⑩①判空
//判断plist是否为空链表(没有数据节点)
bool IsEmpty(DList plist)
{
return plist->next == NULL;
}
⑩②获得长度
//获取数据结点的个数
int GetLength(DList plist)
{
int count = 0;
for (DNode* p = plist->next; p != NULL; p = p->next)
{
count++;
}
return count;
}
⑩③获取链表pos位置的值
遍历数组,条件为i<pos;不满足时为i==pos,直接将此时结点的值给rtval。
//获取plist链表的pos位置的值
bool GetElem(DList plist, int pos, int* rtval)//rtval:输出参数
{
if (pos < 0 || pos >= GetLength(plist))
return false;
DNode* p = plist->next;
for (int i = 0; i < pos; i++, p = p->next)
;
*rtval = p->data;
return true;
}
⑩④清空\销毁链表
//销毁整个内存
void Destroy(DList plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return;
}
//总是删除第一个点;
DNode* p;
while (plist->next != NULL)
{
p = plist->next;
plist->next = p->next;
free(p);
}
}
三、完整代码
1. dlist.h头文件
//双向链表
typedef struct DNode
{
int data;//数据域
struct DNode* next;//后继指针
struct DNode* prio;//前驱指针
}DNode,*DList;
//初始化
void InitList(DList plist);
//头插
bool Insert_head(DList plist, int val);
//尾插
bool Insert_tail(DList plist, int val);
//插入数据,在plist链表的pos位置插入val;
bool Insert(DList plist, int pos, int val);
//判空
bool IsEmpty(DList plist);
//获取数据结点的个数
int GetLength(DList plist);
//在plist查找第一个key值,找到返回结点地址,没有找到返回NULL;
DNode* Search(DList plist, int key);
//删除pos位置的值
bool DelPos(DList plist, int pos);
//删除第一个val的值
bool DelVal(DList plist, int val);
//返回key的前驱地址,如果不存在返回NULL;
DNode* GetPrio(DList plist, int key);
//返回key的后继地址,如果不存在返回NULL;
DNode* GetNext(DList plist, int key);
//输出
void Show(DList plist);
//清空数据
void Clear(DList plist);
//销毁整个内存
void Destroy(DList plist);
2. dlist.cpp
#include "dlist.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
//初始化
void InitList(DList plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return;
}
plist->next = NULL;
plist->prio = NULL;
}
//头插(考试重点)
bool Insert_head(DList plist, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
//创建新结点
DNode* p = (DNode*)malloc(sizeof(DNode));
assert(p != NULL);
p->data = val;
//插入新结点
p->next = plist->next;//绑线
plist->next = p;
p->prio = plist;
if (p->next != NULL)//判断很重要
{
p->next->prio = p;
}
return true;
}
//尾插(考试重点)
bool Insert_tail(DList plist, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
//创建新结点
DNode* p = (DNode*)malloc(sizeof(DNode));
assert(p != NULL);
p->data = val;
//找尾巴
DNode* q;
for (q = plist; q->next != NULL; q = q->next)
{
;
}
//插入数据,将p插入到q的后面
p->next = q->next;//p->next=NULL;
p->prio = q;
q->next = p;
return true;
}
//插入数据,在plist链表的pos位置插入val;
bool Insert(DList plist, int pos, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
if (pos<0 || pos>GetLength(plist))
{
return false;
}
//申请结点
DNode* p = (DNode*)malloc(sizeof(DNode));
assert(p != NULL);
p->data = val;
//找位置
DNode* q;
int i = 0;
for (q = plist; i < pos; i++, q = q->next)
{
;
}
//插入数据
//将p插入到q的后面;
p->next = q->next;
q->next = p;
p->prio = q;
if (p->next != NULL)
{
p->next->prio = p;
}
return true;
}
//判空
bool IsEmpty(DList plist)
{
return plist->next == NULL;
}
//获取数据结点的个数
int GetLength(DList plist)
{
int count = 0;
for (DNode* p = plist->next; p != NULL; p = p->next)
{
count++;
}
return count;
}
//在plist查找第一个key值,找到返回结点地址,没有找到返回NULL;
DNode* Search(DList plist, int key)
{
assert(plist != NULL);
if (plist == NULL)
{
return NULL;
}
for (DNode* p = plist->next; p != NULL; p = p->next)
{
if(p->data==key)
{
return p;
}
}
return NULL;
}
//删除pos位置的值
bool DelPos(DList plist, int pos)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
if (pos < 0 || pos >= GetLength(plist))
{
return false;
}
//找位置
DNode* p;
int i;
for (p = plist, i = 0; i < pos; i++, p = p->next)
{
;
}
DNode* q = p->next;//q是要删除的点
//删除
p->next = q->next;
if (q->next != NULL)
{
q->next->prio = q->prio;
}
free(q);
return true;
}
//删除第一个val的值(考试重点)
bool DelVal(DList plist, int val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
DNode* p = Search(plist, val);
if (p == NULL)
{
return false;
}
p->prio->next = p->next;
if (p->next != NULL)
{
p->next->prio = p->prio;
}
free(p);
return true;
}
//返回key的前驱地址,如果不存在返回NULL;
DNode* GetPrio(DList plist, int key)
{
DNode* p = Search(plist, key);
return p == NULL ? NULL : p->prio;
}
//返回key的后继地址,如果不存在返回NULL;
DNode* GetNext(DList plist, int key)
{
DNode* p = Search(plist, key);
return p == NULL ? NULL : p->next;
}
//输出
void Show(DList plist)
{
for (DNode* p = plist->next; p != NULL; p = p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
//清空数据
void Clear(DList plist)
{
Destroy(plist);
}
//销毁整个内存
void Destroy(DList plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return;
}
//总是删除第一个点;
DNode* p;
while (plist->next != NULL)
{
p = plist->next;
plist->next = p->next;
free(p);
}
}