慧子和她的3D 慧子和她的3D
单链表的局限:
-
单链表的结点都只有一个指向下一个结点的指针
每个元素只知道后继结点是谁,不知道前驱结点
-
单链表的数据元素无法直接访问其前驱元素
-
逆向访问单链表中的元素是极其耗时的操作
len = LinkList_Length(list); for (i= len-1;len >= 0;i--) { LinkListNode* p = LinkList_Get(list,i); // 访问数据元素p中的元素 }
LinkList_Get 每次做循环找到需要返回的元素,复杂度是o(n^2)
在单链表的结点中增加一个指向前驱的pre指针-->双链表
前面留有习题,单链表进行逆序操作,算法复杂度为O(n)
对于单链表逆序访问非常耗时,可以写一个时间算法复杂度为O(n)的操作,每次运算的时候转置一下:最后一个元素变成第一个元素,进行逆序操作,因为算法复杂度为O(n),可以从原来的最后一个也就是现在的第一个进行访问,复杂度为O(n)访问结束。这里面逆序耗时很多,此时引入双向链表。
将单链表代码改为双向链表工程名为:DLinkList.pro
DLinkList.h
#ifndef _DLINKLIST_H_ #define _DLINKLIST_H_ typedef void DLinkList; typedef struct _tag_DLinkListNode DLinkListNode; struct _tag_DLinkListNode { DLinkListNode* Next; DLinkListNode* Pre;// 增加一个指向前驱数据元素的结点 } DLinkList* DLinkList_Creat(int capacity); void DLinkList_Destory(DLinkList* list); void DLinkList_Clear(DLinkList* list); int DLinkList_Length(DLinkList* list); // int DLinkList_Capacity(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); #endif
源文件
#include <stdio.h> #include <malloc.h> #include "DlinkList.h" // 头结点结构体 typedef struct _tag_DlinkList { int length//静态链表最多容纳多少元素 TDlinkListNode header;//链表头 }TDlinkList; DlinkList* DlinkList_Create()//创建时指定静态链表的容量 { TDlinkList* ret = (TDlinkList*)malloc(sizeof(TDlinkList)); if(ret != NULL)//指针不为0时可以继续赋值操作 { ret->length = 0;//data作为静态链表表头表示长度 ret->header.next = NULL; ret->header.pre = NULL;// 初始化的时候头结点的前向指针为空,即便不用也要初始化 } return ret; } void DlinkList_Destory(DlinkList* list) { free(list); } void DlinkList_Clear(DlinkList* list) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 if(sList !=NULL)//链表不为空是合法的,可以继续清空操作 { sList->length = 0; sList->header.next = NULL; sList->header.pre = NULL; } int DlinkList_Length(DlinkList* list) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 int ret = -1;//定义一个返回值 if(sList !=NULL)//链表不为空是合法的,可以继续清空操作 { ret = sList->length; } return ret; } int DlinkList_Insert(DlinkList* list,DlinkListNode* node,int pos) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 int ret =(sList !=NULL) && (pos >=0) && (node != NULL);//单链表方法完成判断 int i=0; if(ret)//在数组中找空闲位置index { DlinkListNode* current = (DlinkListNode*)sList; DlinkListNode* next = NULL ;// 定义指针 // 移动到插入位置 for(i=0;(i<pos) && (current->next != NULL);i++)//后面是个保护动作,对于位置的保护,最多放在最后 { current = current->next;//当前移动到下一个位置 } next = current->next; current->next = node; node->next = next; // 新插入位置是双向链表第一个 if(next != NULL)// 双向链表第一个元素的next不能为空,不然会与76行报错 { next->pre = node; } node->pre = current; if (sLsit->length == 0)// 如果是个空链表插入第一个位置,那头结点要指向空 { node->pre = NULL ; } sList->length++ ; } // 插入位置在头结点的位置比较特殊。pre需指向空 return ret; } //由表头开始通过next域移动pos次后,当前元素的next域即要获取元素在数组中的下标 DlinkListNode* DlinkList_Get(DlinkList* list,int pos) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 int i = 0; if( (sList != NULL ) && (0 <= pos) && (pos < sList->length) )//链表不为空是合法的,长度正常 { DlinkListNode* current =(DlinkListNode*)sList; for(i=0;i<pos;i++) { current = current->next;//第一个元素所在下标 } ret = current->next; } return ret; } //获取第pos个元素,将第pos个元素从链表里删除 DlinkListNode* DlinkList_Delete(DlinkList* list,int pos) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 int i = 0; if( (sList !=NULL ) && (0 <= pos)&&(pos < sList->length )//链表不为空是合法的,长度正常 { DlinkListNode* current = (DlinkListNode*)sList; DlinkListNode* next = NULL; // 找被删除元素 for(i=0;i<pos;i++) { current = current ->next;//第一个元素所在下标 } ret = current ->next; next = ret->next;// 被删除元素后继 current ->next = next; // 被删除是最后一个元素 if(next != NULL) { next->pre = current; // 被删元素是第一个 if(current == (DlinkListNode*)sList)// 强制性修正,判断当前是否是表头 { next->pre = NULL; } } sList->length--; }
main.c
#include <stdio.h> #include <stdlib.h> #include "DlinkList.h" struct Value { DLinkListNode header;// 表头域 int v; }; int main(int argc,char *argv[]) { int i = 0; DlinkList* list = DlinkList_Creat(); struct Value* pv = NULL;// 定义一个指针 // 定义 struct Value v1; struct Value v2; struct Value v3; struct Value v4; struct Value v5; // 赋值 v1.v = 1; v2.v = 2; v3.v = 3; v4.v = 4; v5.v = 5; // 最后一个元素后面插入 DlinkList_Insert(list,(DLinkListNode*)&v1,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v2,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v3,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v4,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v5,DLinkList_Length(list)); for(i = 0;i <DlinkList_Length(list);i++) {// 调用get来取元素 pv = (struct Value*)DlinkList_Get(list,i); printf("%d\n",pv->v); } printf("\n"); // 删除第一个和最后一个元素 DlinkList_Delete(list,DlinkList_Length(list)-1;); DlinkList_Delete(list,0); DlinkList_Destroy(list); return 0; }
双向链表新操作
DLinkList.h
#ifndef _DLINKLIST_H_ #define _DLINKLIST_H_ typedef void DLinkList; typedef struct _tag_DLinkListNode DLinkListNode; struct _tag_DLinkListNode { DLinkListNode* Next; DLinkListNode* Pre;// 增加一个指向前驱数据元素的结点 } DLinkList* DLinkList_Creat(int capacity); void DLinkList_Destory(DLinkList* list); void DLinkList_Clear(DLinkList* list); int DLinkList_Length(DLinkList* list); // int DLinkList_Capacity(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_Current(DLinkList* list); DLinkListNode* DLinkList_Next(DLinkList* list); DLinkListNode* DLinkList_Pre(DLinkList* list); #endif
源文件
#include <stdio.h> #include <malloc.h> #include "DlinkList.h" #define AVAILABLE -1//空闲位置的宏 //链表结构体定义 typedef struct _tag_DlinkList { int length;//静态链表最多容纳多少元素 TDlinkListNode header;//链表头 TDlinkListNode slider;//游标 }TDlinkList; DlinkList* DlinkList_Create()//创建时指定静态链表的容量 { TDlinkList* ret = (TDlinkList*)malloc(sizeof(TDlinkList)); if(ret != NULL)//指针不为0时可以继续赋值操作 { ret->length = 0;//data作为静态链表表头表示长度 ret->header.next = NULL; ret->header.pre = NULL;// 初始化的时候头结点的前向指针为空,即便不用也要初始化 ret->slider = NULL; } return ret; } void DlinkList_Destory(DlinkList* list) { free(list); } void DlinkList_Clear(DlinkList* list) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 if(sList !=NULL)//链表不为空是合法的,可以继续清空操作 { sList->length = 0; sList->header.next = NULL; sList->header.pre = NULL; ret->slider = NULL; } int DlinkList_Length(DlinkList* list) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 int ret = -1;//定义一个返回值 if(sList !=NULL)//链表不为空是合法的,可以继续清空操作 { ret = sList->length; } return ret; } int DlinkList_Insert(DlinkList* list,DlinkListNode* node,int pos) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 int ret =(sList !=NULL) && (pos >=0) && (node != NULL);//单链表方法完成判断 int i=0; if(ret)//在数组中找空闲位置index { DlinkListNode* current = (DlinkListNode*)sList; DlinkListNode* next = NULL ;// 定义指针 // 移动到插入位置 for(i=0;(i<pos) && (current->next != NULL);i++)//后面是个保护动作,对于位置的保护,最多放在最后 { current = current->next;//当前移动到下一个位置 } next = current->next; current->next = node; node->next = next; // 新插入位置是双向链表第一个 if(next != NULL)// 双向链表第一个元素的next不能为空,不然会与76行报错 { next->pre = node; } node->pre = current; if (sLsit->length == 0)// 如果是个空链表插入第一个位置,那头结点要指向空 { node->pre = NULL ; sLsit->slider = node;// 将链表中游标指向第一个元素 } sList->length++ ; } // 插入位置在头结点的位置比较特殊。pre需指向空 return ret; } //由表头开始通过next域移动pos次后,当前元素的next域即要获取元素在数组中的下标 DlinkListNode* DlinkList_Get(DlinkList* list,int pos) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 int i = 0; if( (sList != NULL ) && (0 <= pos) && (pos < sList->length) )//链表不为空是合法的,长度正常 { DlinkListNode* current =(DlinkListNode*)sList; for(i=0;i<pos;i++) { current = current->next;//第一个元素所在下标 } ret = current->next; } return ret; } //获取第pos个元素,将第pos个元素从链表里删除 DlinkListNode* DlinkList_Delete(DlinkList* list,int pos) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 int i = 0; if( (sList !=NULL ) && (0 <= pos)&&(pos < sList->length )//链表不为空是合法的,长度正常 { DlinkListNode* current = (DlinkListNode*)sList; DlinkListNode* next = NULL; // 找被删除元素 for(i=0;i<pos;i++) { current = current ->next;//第一个元素所在下标 } ret = current ->next; next = ret->next;// 被删除元素后继 current ->next = next; // 被删除是最后一个元素 if(next != NULL) { next->pre = current; // 被删元素是第一个 if(current == (DlinkListNode*)sList)// 强制性修正,判断当前是否是表头 { next->pre = NULL; } } // 里面只有一个元素被删除 if(sList->slider == ret)// 删除元素是不是游标指向元素 { sList->slider == next;// 移动游标指向下一个元素 } sList->length--; } return ret; } // 引进游标新操作 DLinkListNode* DLinkList_DeleteNode(DLinkList* list,DLinkListNode* node) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 int i = 0; // 查找删除元素 if(sList !=NULL ) { DlinkListNode* current = (DlinkListNode*)sList; // 找被删除元素 for(i=0;i<sList->length;i++) { if(current ->next == node) { ret = current ->next; break; } current = current ->next; } if(ret != NULL)// 没找到 { DlinkList_Delete(sList,i); } // 里面只有一个元素被删除 if(sList->slider == ret)// 删除元素是不是游标指向元素 { sList->slider == next;// 移动游标指向下一个元素 } } sList->length--; } return ret; } // 游标相关操作 DLinkListNode* DLinkList_Current(DLinkList* list) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 if(sList !=NULL)//链表不为空是合法的,可以继续清空操作 { ret->sList->slider ; } } DLinkListNode* DLinkList_Next(DLinkList* list) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 if ((sList !=NULL) && (sList->slider != NULL))//链表不为空是合法的,可以继续清空操作 { ret->sList->slider ; sList->slider = ret->next; } } DLinkListNode* DLinkList_Pre(DLinkList* list) { TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 DlinkListNode* ret = NULL;//定义一个返回值 if(sList !=NULL)//链表不为空是合法的,可以继续清空操作 { ret->sList->slider ; sList->slider = ret->pre;// 指向当前前一个 } }
main.c
#include <stdio.h> #include <stdlib.h> #include "DlinkList.h" struct Value { DLinkListNode header;// 表头域 int v; }; int main(int argc,char *argv[]) { int i = 0; DlinkList* list = DlinkList_Creat(); struct Value* pv = NULL;// 定义一个指针 // 定义 struct Value v1; struct Value v2; struct Value v3; struct Value v4; struct Value v5; // 赋值 v1.v = 1; v2.v = 2; v3.v = 3; v4.v = 4; v5.v = 5; // 最后一个元素后面插入 DlinkList_Insert(list,(DLinkListNode*)&v1,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v2,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v3,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v4,DLinkList_Length(list)); DlinkList_Insert(list,(DLinkListNode*)&v5,DLinkList_Length(list)); for(i = 0;i <DlinkList_Length(list);i++) {// 调用get来取元素 pv = (struct Value*)DlinkList_Get(list,i); printf("%d\n",pv->v); } printf("\n"); // 删除第一个和最后一个元素 DlinkList_Delete(list,DlinkList_Length(list)-1;); DlinkList_Delete(list,0); for(i = 0;i <DlinkList_Length(list);i++) {// 调用get来取元素 pv = (struct Value*)DlinkList_Next(list); printf("%d\n",pv->v); } printf("\n"); DlinkList_Resert(list);// 游标指向第一个元素 DlinkList_Next(list);// 游标向后移一个指向位置2 pv = (struct Value*)DlinkList_Current(list); printf("%d\n",pv->v); // 删除当前元素 DlinkList_DeleteNode(list,( DlinkLisNode*)pv); pv = (struct Value*)DlinkList_Current(list); printf("%d\n",pv->v); DlinkList_Pre(list); pv = (struct Value*)DlinkList_Current(list); printf("%d\n",pv->v); printf("Length : %d\n",DlinkList_Length(list)); DlinkList_Destroy(list); return 0; }
小结:
双向链表在单链表的基础上增加了指向前驱的指针
功能上双向链表可以取代单链表的使用
循环链表的NEXT PRE CURRENT操作可以高效的遍历链表中所有元素