双向链表API

双向链表设计与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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值