双向链表是链表中偏难的
先写头文件dlist.h
#pragma once
//双向链表结构体设计:
typedef int ELEM_TYPE;
typedef struct Dlist
{
ELEM_TYPE data;//数据域 保存有效值
struct Dlist* next;//指针域 保存下一个节点的地址(后继)
struct Dlist* prior;//指针域 保存上一个节点的地址(前驱)
}Dlist, *PDlist;
//双向链表可执行函数声明:
//初始化
void Init_dlist(struct Dlist* plist);
//购买新节点
struct Dlist* BuyNode(ELEM_TYPE val);
//头插
bool Insert_head(PDlist plist, ELEM_TYPE val);
//尾插
bool Insert_tail(PDlist plist, ELEM_TYPE val);
//按位置插
bool Insert_pos(PDlist plist, int pos, ELEM_TYPE val);
//头删
bool Del_head(PDlist plist);
//尾删
bool Del_tail(PDlist plist);
//按位置删
bool Del_pos(PDlist plist, int pos);
//按值删除
bool Del_val(PDlist plist, ELEM_TYPE val);
//查找(如果值重复,返回第一次出现的节点地址)
struct Dlist* Search(PDlist plist, ELEM_TYPE val);
//判空
bool IsEmpty(PDlist plist);
//判满(链表不用实现)
//获取有效长度
int Get_length(PDlist plist);
//清空
void Clear(PDlist plist);
//销毁1
void Destroy(PDlist plist);
//销毁2
void Destroy2(PDlist plist);
//打印
void Show(PDlist plist);
再写dlist.cpp文件
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include "dlist.h"
//初始化(排除两个野指针的风险,将其全部赋值为NULL)
void Init_dlist(struct Dlist* plist)
{
//assert
plist->next = plist->prior = NULL;
}
//购买新节点(一次购买一个)
struct Dlist* BuyNode(ELEM_TYPE val)
{
//assert
struct Dlist* pnewnode = (struct Dlist*)malloc(1 * sizeof(struct Dlist));
assert(pnewnode != NULL);
if(NULL == pnewnode)
{
return NULL; //购买失败
}
pnewnode->data = val;
pnewnode->next = pnewnode->prior = NULL;
return pnewnode;
}
//头插
bool Insert_head(PDlist plist, ELEM_TYPE val)
{
//assert plist
//1.购买新节点
struct Dlist* pnewnode = (struct Dlist*)malloc(1 * sizeof(struct Dlist));
assert(pnewnode != NULL);
pnewnode->data = val;
pnewnode->next = NULL;
pnewnode->prior = NULL;
//2.找个合适的插入位置(找一个指针指向插入位置的上一个节点)
//头插不用找(因为plist就指向插入位置的上一个节点)
//3.插入 (3421)
//第一次:自身的两条线
//第二次:再修改后一个节点的前驱
//第三次:最后修改上一个节点的后继
pnewnode->next = plist->next;//3
pnewnode->prior = plist;//4
if(plist->next != NULL)
{
plist->next->prior = pnewnode;//2
}
plist->next = pnewnode;//1
return true;
}
//尾插(尾插的话,下一个节点肯定不存在,所以下一个节点的前驱这根线,不用写)
bool Insert_tail(PDlist plist, ELEM_TYPE val)
{
//assert
struct Dlist* pnewnode = (struct Dlist*)malloc(1 * sizeof(struct Dlist));
assert(pnewnode != NULL);
pnewnode->data = val;
pnewnode->next = NULL;
pnewnode->prior = NULL;
//2.找个合适的插入位置(找一个指针p指向尾结点)
struct Dlist* p=plist;
for( ; p->next!=NULL; p=p->next);
//此时 for循环结束 p指向尾结点
//3.插入 (3421)
pnewnode->next = p->next; //3
pnewnode->prior = p;//4
//2 这个2不用写,因为尾插不存在 更后边的节点
p->next = pnewnode;
return true;
}
//按位置插(pos=0 相当于头插, pos==length 相当于尾插)
//bool Insert_pos1(PDlist plist, int pos, ELEM_TYPE val)
//{
// assert(plist != NULL);
// assert(pos>=0 && pos<=Get_length(plist));
//
//
// //1.购买新节点
// struct Dlist* pnewnode = (struct Dlist*)malloc(1 * sizeof(struct Dlist));
// assert(pnewnode != NULL);
// pnewnode->data = val;
// pnewnode->next = NULL;
// pnewnode->prior = NULL;
//
// //2.找个合适的插入位置(找一个指针p指向待插入位置的上一个节点)
// struct Dlist* p=plist;
// for(int i=0; i<pos; i++)
// {
// p=p->next;
// }
// //此时 for循环结束 p指向待插入位置的上一个节点
//
// //3.插入 (3421)
// pnewnode->next = p->next;//3
// pnewnode->prior = p;//4
// if(pos != Get_length(plist) && !IsEmpty(plist))
// {
// p->next->prior = pnewnode;//pnewnode->next->prior = pnewnode;//2
// }
// p->next = pnewnode;//1
//
// return true;
//}
//按位置插(pos=0 相当于头插, pos==length 相当于尾插)
bool Insert_pos(PDlist plist, int pos, ELEM_TYPE val)
{
assert(plist != NULL);
assert(pos>=0 && pos<=Get_length(plist));
//头部和尾部有风险, 那最开始就排除掉
if(pos == 0)
{
return Insert_head(plist, val);
}
else if(pos == Get_length(plist))
{
return Insert_tail(plist, val);
}
//此时,能执行到这里,代表头部和尾部风险全部排除掉,剩余情况就普遍情况了(4根线都存在)
//1.购买新节点
struct Dlist* pnewnode = (struct Dlist*)malloc(1 * sizeof(struct Dlist));
assert(pnewnode != NULL);
pnewnode->data = val;
pnewnode->next = NULL;
pnewnode->prior = NULL;
//2.找个合适的插入位置(找一个指针p指向待插入位置的上一个节点)
struct Dlist* p=plist;
for(int i=0; i<pos; i++)
{
p=p->next;
}
//此时 for循环结束 p指向待插入位置的上一个节点
//3.插入 (3421)
pnewnode->next = p->next;//3
pnewnode->prior = p;//4
p->next->prior = pnewnode;//pnewnode->next->prior = pnewnode;//2
p->next = pnewnode;//1
return true;
}
//头删
bool Del_head(PDlist plist)
{
//assert
//插入不需要判满 但是删除一定记得判空
if(IsEmpty(plist))
{
return false;
}
//找个指针p指向待删除节点
struct Dlist *p =plist->next;
//找个指针q指向待删除节点的上一个节点(此时plist替代了指针q的作用)
//跨越指向+释放内存
plist->next = p->next;//plist->next = plist->next->next;
if(p->next != NULL)//代表 不仅仅只有一个有效值
{
p->next->prior = plist;
}
free(p);
return true;
}
//尾删
bool Del_tail(PDlist plist)
{
//assert
if(IsEmpty(plist))//插入不需要判满 但是删除一定记得判空
{
return false;
}
//第二种策略 先找q 再找p
struct Dlist *q = plist;
for(q; q->next->next!=NULL; q=q->next);
//此时 for循环结束 q指向倒数第二个节点
struct Dlist *p = q->next;
//跨越指向+释放内存
q->next = p->next;//让上一个节点指向下一个节点
//让下一个节点指向上一个节点(因为是尾删,所以待删除节点后面没有节点了)
free(p);
return true;
}
//按位置删(一般来说,头和尾比较危险,一开始处理掉即可)
bool Del_pos(PDlist plist, int pos)
{
assert(plist != NULL);
assert(pos >=0 && pos<Get_length(plist));//pos == length //error 失败
if(IsEmpty(plist))//插入不需要判满 但是删除一定记得判空
{
return false;
}
if(pos == 0)
{
return Del_head(plist);
}
else if(pos == Get_length(plist)-1)
{
return Del_tail(plist);
}
//先找q 再找p pos==x 则q向后跑x步
struct Dlist *q = plist;
for(int i=0; i<pos; i++)
{
q = q->next;
}
struct Dlist *p = q->next;
q->next = p->next;
p->next->prior = q;
free(p);
return true;
}
//按值删除(特殊情况:如果你找的val值 在尾结点找到了)
bool Del_val(PDlist plist, ELEM_TYPE val)
{
//assert
if(IsEmpty(plist))//插入不需要判满 但是删除一定记得判空
{
return false;
}
struct Dlist* p = Search(plist, val);
if(p == NULL)
{
return false;
}
//在申请一个指针q,指向p的前一个节点
struct Dlist *q = plist;
for(q; q->next!=p; q=q->next);
//此时 q在p前面
//跨越指向 + 释放内存
q->next = p->next; //ok
if(p->next != NULL)
{
p->next->prior = q;//特殊情况:如果你找的val值 在尾结点找到了 这行代码就会失败
}
free(p);
return true;
}
//查找(如果值重复,返回第一次出现的节点地址)
struct Dlist* Search(PDlist plist, ELEM_TYPE val)
{
//assert
for(struct Dlist* p=plist->next; p!=NULL; p=p->next)
{
if(p->data == val)
{
return p;
}
}
return NULL;
}
//判空
bool IsEmpty(PDlist plist)
{
return plist->next == NULL;
}
//判满(链表不用实现)
//获取有效长度
int Get_length(PDlist plist)
{
int count = 0;
for(struct Dlist* p=plist->next; p!=NULL; p=p->next)
{
count++;
}
return count;
}
//清空
void Clear(PDlist plist)
{
Destroy(plist);
}
//销毁1(借助头结点,无限头删)
void Destroy(PDlist plist)
{
//assert
while(plist->next != NULL)
{
struct Dlist *p = plist->next;
plist->next = p->next;//这里只需要上一个节点 指向下一个节点即可
free(p);
}
plist->next = plist->prior = NULL;
}
//销毁2(不借助头结点, 但是有两个临时指针变量)
void Destroy2(PDlist plist)
{
//assert
struct Dlist *p = plist->next;
struct Dlist *q = NULL;
plist->next = plist->prior = NULL;
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
}
//打印
void Show(PDlist plist)
{
//assert
for(struct Dlist* p=plist->next; p!=NULL; p=p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
最后在主函数中实现代码
#include <stdio.h>
#include "assert.h"
#include <stdlib.h>
#include <vld.h>
#include "dlist.h"
int main()
{
struct Dlist head;
Init_dlist(&head);
for(int i=0; i<20; i++)
{
Insert_pos(&head, i, i+1);
}
Show(&head);
Insert_head(&head, 100);
Insert_tail(&head, 200);
Show(&head);
printf("length = %d\n", Get_length(&head));
Del_head(&head);
Del_tail(&head);
Show(&head);
printf("length = %d\n", Get_length(&head));
Del_pos(&head, 3);
Del_val(&head, 14);
Show(&head);
printf("length = %d\n", Get_length(&head));
//Destroy(&head);
Destroy2(&head);
return 0;
}
测试结果: