双向链表设计与API实现
为什么需要双向链表?
- 单链表的结点都只有一个指向下一个结点的指针
- 单链表的数据元素无法直接访问其前驱元素
- 逆序访问单链表中的元素是极其耗时的操作!
双向链表的定义
在单链表的结点中增加一个指向其前驱的pre指针新增功能:游标的定义
在双向链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素。优点:双向链表在单链表的基础上增加了指向前驱的指针
功能上双向链表可以完全取代单链表的使用
双向链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素
缺点:代码复杂
操作:双向链表插入
插入操作异常处理:如下代码片段
插入第一个元素异常处理 判断 next != NULL 此时初始化游标 指向第一个节点
在0号位置处插入元素;node->pre = NULL ;
if (next!=NULL)//当链表插入第一个元素时,需要特殊处理
{
next->pre = node;
}
node->pre = current;
//当链表插入第一个元素时处理游标
if (tList->length == 0)
{
tList->slider = NULL;
}
//若在0位置插入(前插法)需要特殊处理
if (current == (DLinkListNode*)tList)
{
node->pre = NULL;
}
删除操作:
异常处理:如下代码片段
if (next !=NULL)//判断是否删除最后一个元素 同时也包括了当只有一个节点的情况
{
next->pre = current;
//判断是否删除的是第0个位置
if (current == (DLinkListNode*)tList)
{
next->pre = NULL;
}
}
dlinklist.h
#ifndef __MYDLinkList_H__
#define __MYDLinkList_H__
//传统链表 数据和指针在一个节点
//现代链表 把指针独立出来,做成节点 在包含数据的节点中包含这个独立节点
typedef void DLinkList;
typedef struct _tag_DLinkListNode//被大千世界包含
{
struct _tag_DLinkListNode*next;
struct _tag_DLinkListNode*pre;
}DLinkListNode;
DLinkList* DLinkList_Create();
void DLinkList_Destroy(DLinkList* list);
void DLinkList_Clear(DLinkList* list);
int DLinkList_Length(DLinkList* list);
int DLinkList_Insert(DLinkList*list,DLinkListNode*node,int pos);
DLinkListNode* DLinkList_Get(DLinkList*list,int pos);
DLinkListNode* DLinkList_Delete(DLinkList*list,int pos);
//双向链表新操作
/*直接指定删除链表中的某个数据元素*/
DLinkListNode*DLinkList_DeleteNode(DLinkList*list,DLinkListNode*node);
/*将游标重置指向链表中的第一个数据元素*/
DLinkListNode*DLinkList_Reset(DLinkList*list);
/*获取当前游标指向的数据元素*/
DLinkListNode*DLinkList_Current(DLinkList*list);
/*将游标移动指向到链表中的下一个数据元素*/
DLinkListNode*DLinkList_Next(DLinkList*list);
/*将游标移动指向到链表中的前一个数据元素*/
DLinkListNode*DLinkList_Pre(DLinkList*list);
#endif
dlinklist.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dlinklist.h"
//头结点 封装了一个 节点和链表长度
typedef struct _tag_DLinkList
{
DLinkListNode header;
DLinkListNode* slider;//新增功能:游标的定义,可以通过这个游标来遍历链表中的所有元素。
int length;
}TDLinkList;
DLinkList* DLinkList_Create()//创建
{
TDLinkList *ret = NULL;
ret = (TDLinkList *)malloc(sizeof(TDLinkList)) ;
memset(ret,0,sizeof(TDLinkList));//等价于下面两句
//tmp->length = 0;
//tmp->header.next = NULL;
return ret;//任何类型的指针都可以直接赋值给void*
}
void DLinkList_Destroy(DLinkList* list)
{
if (list!=NULL)
{
free(list);
list = NULL;
}
}
//让链表恢复到初始化状态
void DLinkList_Clear(DLinkList* list)
{
TDLinkList *tlist = NULL;
if (list==NULL)
{
return ;
}
tlist = (TDLinkList *)list;//void*类型的指针必须转换后赋值给其他类型的指针
tlist->length = 0;
tlist->header.next = NULL;
}
int DLinkList_Length(DLinkList* list)
{
TDLinkList *tlist = NULL;
if (list==NULL)
{
return 0;
}
tlist = (TDLinkList *)list;//void*类型的指针先转换再赋值给相应类型的指针
return tlist->length;
}
//插入操作
int DLinkList_Insert(DLinkList*list,DLinkListNode*node,int pos)
{
int ret = 0,i;
DLinkListNode*current=NULL;//引入辅助指针变量?
DLinkListNode*next=NULL;//引入辅助指针变量?
TDLinkList *tList = NULL;
if (list ==NULL||node==NULL||pos<0)
{
ret = -1;
printf("func DLinkList_Insert() error :%d\n ",ret);
return ret;
}
tList = (TDLinkList*)list;//void类型的指针需要转换后赋值给相应的指针类型数据
current = &tList->header;//一开始让辅助指针current指向链表头结点的DLinkListNode
//假设要删除3号位置的元素(从0号位置开始),3号元素位置保存在2号位置元素的next域
//移动辅助指针变量current指向2号位置
for (i=0;i<pos&&(current->next!=NULL);i++)
{
current = current->next;
}
next = current->next;
//让新节点node指向后续节点
node->next = current->next;
//当前位置2号 的next域赋值为node
current->next = node;
if (next!=NULL)//当链表插入第一个元素时,需要特殊处理
{
next->pre = node;
}
node->pre = current;
//当链表插入第一个元素时处理游标
if (tList->length == 0)
{
tList->slider = NULL;
}
//若在0位置插入(前插法)需要特殊处理
if (current == (DLinkListNode*)tList)
{
node->pre = NULL;
}
tList->length++;
return ret;
}
DLinkListNode* DLinkList_Get(DLinkList*list,int pos)
{
int ret = 0,i;
DLinkListNode*current=NULL;
TDLinkList *tList = NULL;
if (list ==NULL||pos<0)
{
ret = -1;
printf("func DLinkList_Get() error :%d\n ",ret);
return NULL;
}
tList = (TDLinkList*)list;
current = &tList->header;//辅助指针变量指向链表头部
for (i=0;i<pos&&(current->next!=NULL);i++)
{
current = current->next;
}
return current->next;
}
DLinkListNode* DLinkList_Delete(DLinkList*list,int pos)
{
int i;
DLinkListNode*current=NULL;//引入辅助指针变量
DLinkListNode*next=NULL;//引入辅助指针变量?
DLinkListNode*ret=NULL;//引入辅助指针变量 缓存删除的节点 返回
TDLinkList *tList = NULL;
if (list ==NULL||pos<0)
{
printf("func DLinkList_Delete() error\n");
return NULL;
}
tList = (TDLinkList*)list;
current = &tList->header;//一开始让辅助指针current指向链表头结点的DLinkListNode
//假设删除3号位置节点 移动current指向2号位置
for (i=0;i<pos&&(current->next!=NULL);i++)
{
current = current->next;
}
ret = current->next;//缓存即将删除的节点(3号位置)
next = ret->next;
//连线
current->next = next;
if (next !=NULL)//判断是否删除最后一个元素 同时也包括了当只有一个节点的情况
{
next->pre = current;
//判断是否删除的是第0个位置
if (current == (DLinkListNode*)tList)
{
next->pre = NULL;
}
}
//如果删除游标指向的节点
if (tList->slider == ret)
{
tList->slider = ret->next;
}
tList->length--;
return ret;
}
//双向链表新操作
/*直接指定删除链表中的某个数据元素*/
DLinkListNode*DLinkList_DeleteNode(DLinkList*list,DLinkListNode*node)
{
DLinkListNode*ret=NULL;
TDLinkList *tList = (TDLinkList*)list;
DLinkListNode*current=NULL;
int i =0;
if (list ==NULL)
{
printf("func DLinkList_DeleteNode() error\n");
return NULL;
}
current = (DLinkListNode*)tList;
//在循环链表中查找node的位置
for (i=0;i<tList->length;i++)
{
if (current->next==node)
{
ret = current->next;//缓存
break;
}
current = current->next;
}
//如果找到
if ( ret != NULL )
{
ret = DLinkList_Delete(list,i);
}
return ret;
}
/*将游标重置指向链表中的第一个数据元素*/
DLinkListNode*DLinkList_Reset(DLinkList*list)
{
TDLinkList *tList = (TDLinkList*)list;//void类型的指针需要转换后赋值给相应的指针类型数据
DLinkListNode*ret =NULL;
if (list ==NULL||(tList->length == 0))
{
printf("func DLinkList_Reset() error :list ==NULL||(tList->length == 0)\n ");
return ret ;
}
tList->slider = tList->header.next;
ret = tList->slider;
return ret;
}
/*获取当前游标指向的数据元素*/
DLinkListNode*DLinkList_Current(DLinkList*list)
{
TDLinkList *tList = (TDLinkList*)list;//void类型的指针需要转换后赋值给相应的指针类型数据
DLinkListNode *ret = NULL;
if (list ==NULL||(tList->length == 0))
{
printf("func DLinkList_Current() error :list ==NULL||(tList->length == 0)\n ");
return ret ;
}
ret = tList->slider;
return ret;
}
/*将游标移动指向到链表中的下一个数据元素*/
DLinkListNode*DLinkList_Next(DLinkList*list)
{
DLinkListNode *ret = NULL;
TDLinkList *tList = (TDLinkList*)list;//void类型的指针需要转换后赋值给相应的指针类型数据
if (list ==NULL||(tList->length == 0))
{
printf("func DLinkList_Next() error :list = NULL\n ");
}
ret = tList->slider;
//当游标指向了最后一个节点时
if (ret->next==NULL)
{
return ret;//游标保持不动 直接返回
}
tList->slider = ret->next;
return ret;
}
/*将游标移动指向到链表中的前一个数据元素*/
DLinkListNode*DLinkList_Pre(DLinkList*list)
{
DLinkListNode *ret = NULL;
TDLinkList *tList = (TDLinkList*)list;//void类型的指针需要转换后赋值给相应的指针类型数据
if (list ==NULL||(tList->length == 0))
{
printf("func DLinkList_Next() error :list = NULL\n ");
}
//
//如果只有一个节点(除了头结点)或者当游标指向了第一个节点时
if (tList->length==1||(tList->slider == tList->header.next))
{
tList->slider = tList->header.next;
ret = tList->slider;
return ret;
}
ret = tList->slider;
tList->slider = ret->pre;
return ret;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include<stdio.h>
#include <string.h>
#include "dlinklist.h"
//现代链表:业务节点(大千世界)包含链表节点(我)
typedef struct Teacher
{
DLinkListNode listnode;
char*name;
int age;
}Teacher;
int main()
{
//创建一个线性表
DLinkList* list= NULL;
int length ,ret,i;
Teacher t1,t2,t3,t4,t5,t6;
Teacher *tmp;
t1.age = 31;
t2.age = 32;
t3.age = 33;
t4.age = 34;
t5.age = 35;
t6.age = 36;
//创建并返回TDLinkList类型的头结点
list = DLinkList_Create();//任意类型的指针变量都可以直接辅助给void*
if (list == NULL)
{
return -1;
}
length = DLinkList_Length(list);//TDLinkList
printf("length:%d\n",length);
//插入
/*说明DLinkListNode*)&t1 把结构体Teacher转为结构体DLinkListNode 是可以的
因为结构体Teacher的地址和结构体Teacherz中DLinkListNode地址重叠*/
//实现链表算法和具体业务节点的分离(细细体会)
//头插法
/*ret = DLinkList_Insert(list,(DLinkListNode*)&t1, 0);
ret = DLinkList_Insert(list,(DLinkListNode*)&t3, 0);
ret = DLinkList_Insert(list,(DLinkListNode*)&t4, 0);
ret = DLinkList_Insert(list,(DLinkListNode*)&t5, 0);
ret = DLinkList_Insert(list,(DLinkListNode*)&t6, 0);*/
//尾插法
ret = DLinkList_Insert(list,(DLinkListNode*)&t1, DLinkList_Length(list));
ret = DLinkList_Insert(list,(DLinkListNode*)&t2, DLinkList_Length(list));
ret = DLinkList_Insert(list,(DLinkListNode*)&t3, DLinkList_Length(list));
ret = DLinkList_Insert(list,(DLinkListNode*)&t4, DLinkList_Length(list));
ret = DLinkList_Insert(list,(DLinkListNode*)&t5, DLinkList_Length(list));
ret = DLinkList_Insert(list,(DLinkListNode*)&t6, DLinkList_Length(list));
length = DLinkList_Length(list);
printf("length:%d\n",length);
//遍历
for (i=0;i< DLinkList_Length(list);i++)
{
tmp =(Teacher *)DLinkList_Get(list,i);
if (tmp==NULL)
{
return ;
}
printf("%d\n",tmp->age);
}
tmp = (Teacher*)DLinkList_DeleteNode(list,(DLinkListNode*)&t1);
printf("删除后\n");
//遍历
for (i=0;i< DLinkList_Length(list);i++)
{
tmp =(Teacher *)DLinkList_Get(list,i);
if (tmp==NULL)
{
return ;
}
printf("%d\n",tmp->age);
}
/*将游标重置指向链表中的第一个数据元素*/
tmp = (Teacher *)DLinkList_Reset(list);
if (tmp==NULL)
{
return ;
}
printf("游标重置:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Current(list);
if (tmp==NULL)
{
return ;
}
printf("游标当前元素:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Next(list);
if (tmp==NULL)
{
return ;
}
printf("游标下移:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Current(list);
if (tmp==NULL)
{
return ;
}
printf("游标当前元素:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Reset(list);
if (tmp==NULL)
{
return ;
}
printf("游标重置:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Next(list);
if (tmp==NULL)
{
return ;
}
printf("游标下移:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Current(list);
if (tmp==NULL)
{
return ;
}
printf("游标当前元素:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Next(list);
if (tmp==NULL)
{
return ;
}
printf("游标下移:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Current(list);
if (tmp==NULL)
{
return ;
}
printf("游标当前元素:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Pre(list);
if (tmp==NULL)
{
return ;
}
printf("游标前移:%d\n",tmp->age);
tmp = (Teacher *)DLinkList_Current(list);
if (tmp==NULL)
{
return ;
}
printf("游标当前元素:%d\n",tmp->age);
//让游标不断前移 测试临界点
for (i=0;i<3;i++)
{
tmp = (Teacher *)DLinkList_Pre(list);
if (tmp==NULL)
{
return ;
}
printf("游标前移:%d\n",tmp->age);
}
//让游标不断后移 测试临界点
for (i=0;i<6;i++)
{
tmp = (Teacher *)DLinkList_Next(list);
if (tmp==NULL)
{
return ;
}
printf("游标后移:%d\n",tmp->age);
}
//删除
while (DLinkList_Length(list)>0)
{
tmp =(Teacher*) DLinkList_Delete(list,0);
if (tmp==NULL)
{
return ;
}
printf("delete : %d\n",tmp->age);
}
//销毁
DLinkList_Destroy(list);
system("pause");
return 0;
}