C 双向链表
单链表的结点都只有一个指向下一个结点的指针 单链表的数据元素无法直接访问其前驱元素 逆序访问单链表中的元素是极其耗时的操作! |
len = LinkList_Length(list); for (i=len-1; len>=0; i++) //O(n) { LinkListNode *p = LinkList_Get(list, i); //O(n) //访问数据元素p中的元素 // } |
双向链表的定义 在单链表的结点中增加一个指向其前驱的pre指针 |
插入操作异常处理 插入第一个元素异常处理 在0号位置处插入元素; |
删除操作 |
删除操作异常处理 |
双向链表的新操作 |
获取当前游标指向的数据元素 将游标重置指向链表中的第一个数据元素 将游标移动指向到链表中的下一个数据元素 将游标移动指向到链表中的上一个数据元素 直接指定删除链表中的某个数据元素 |
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); |
循环链表插入结点技术场景
循环链表删除结点技术场景
优点和缺点
优点:双向链表在单链表的基础上增加了指向前驱的指针 功能上双向链表可以完全取代单链表的使用 双向链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素 |
缺点:代码复杂 |
02 08 10
#ifndef _MY_DLINKLIST_H_
#define _MY_DLINKLIST_H_ typedef void DLinkList;
/*
typedef struct _tag_DLinkListNode DLinkListNode;
struct _tag_DLinkListNode
{
DLinkListNode* next;
DLinkListNode* pre;
};
*/ 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); //-- add
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
#include <stdio.h>
#include <malloc.h>
#include "DLinkList.h" typedef struct _tag_DLinkList
{
DLinkListNode header;
DLinkListNode* slider;
int length;
} TDLinkList; DLinkList* DLinkList_Create()
{
TDLinkList* ret = (TDLinkList*)malloc(sizeof(TDLinkList)); if( ret != NULL )
{
ret->length = ;
ret->header.next = NULL;
ret->header.pre = NULL;
ret->slider = NULL;
} return ret;
} void DLinkList_Destroy(DLinkList* list)
{
if (list != NULL)
{
free(list);
}
} void DLinkList_Clear(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list; if( sList != NULL )
{
sList->length = ;
sList->header.next = NULL;
sList->header.pre = NULL;
sList->slider = NULL;
}
} int DLinkList_Length(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
int ret = -; if( sList != NULL )
{
ret = sList->length;
} return ret;
} //大家一定要注意:教科书不会告诉你 项目上如何用;哪些点是项目的重点
int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos)
{
int ret = , i = ;
TDLinkList* sList = (TDLinkList*)list; if (list==NULL || node==NULL || pos<)
{
return -;
} DLinkListNode* current = (DLinkListNode*)sList;
DLinkListNode* next = NULL; //需要增加next指针 for(i=; (i<pos) && (current->next != NULL); i++)
{
current = current->next;
} next = current->next; //步骤1-2
current->next = node;
node->next = next; //步骤3-4
if( next != NULL ) //当链表插入第一个元素,需要特殊处理
{
next->pre = node;
}
node->pre = current; if( sList->length == )
{
sList->slider = node; //当链表插入第一个元素处理游标
} //若在0位置插入,需要特殊处理 新来结点next前pre指向null
if( current == (DLinkListNode*)sList )
{
node->pre = NULL;
} sList->length++; return ret;
} DLinkListNode* DLinkList_Get(DLinkList* list, int pos)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
int i = ; if( (sList != NULL) && ( <= pos) && (pos < sList->length) )
{
DLinkListNode* current = (DLinkListNode*)sList; for(i=; i<pos; i++)
{
current = current->next;
} ret = current->next;
} return ret;
} //插入第一个节点
//删除的是最后一个结点,该是如何处理
DLinkListNode* DLinkList_Delete(DLinkList* list, int pos)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL;
int i = ;
if (sList == NULL || pos < )
{
return NULL;
}
//if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
{
DLinkListNode* current = (DLinkListNode*)sList;
DLinkListNode* next = NULL; //需要增加next指针 for(i=; i<pos; i++)
{
current = current->next;
} ret = current->next;
next = ret->next; //步骤1
current->next = next; //步骤2
if( next != NULL )//需要特殊处理
{
next->pre = current; if( current == (DLinkListNode*)sList ) //若第0个位置,需要特殊处理
{
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 = ; if( sList != NULL )
{
DLinkListNode* current = (DLinkListNode*)sList; for(i=; i<sList->length; i++)
{
if( current->next == node )
{
ret = current->next;
break;
} current = current->next;
} if( ret != NULL )
{
DLinkList_Delete(sList, i);
}
} return ret;
} DLinkListNode* DLinkList_Reset(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL; if( sList != NULL )
{
sList->slider = sList->header.next;
ret = sList->slider;
} return ret;
} DLinkListNode* DLinkList_Current(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL; if( sList != NULL )
{
ret = sList->slider;
} return ret;
} 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;
} return ret;
} DLinkListNode* DLinkList_Pre(DLinkList* list)
{
TDLinkList* sList = (TDLinkList*)list;
DLinkListNode* ret = NULL; if( (sList != NULL) && (sList->slider != NULL) )
{
ret = sList->slider;
sList->slider = ret->pre;
} return ret;
}
#include <stdio.h>
#include <stdlib.h>
#include "DLinkList.h" struct Value
{
DLinkListNode node;
int v;
}; int main()
{
int i = ;
DLinkList* list = DLinkList_Create();
struct Value* pv = NULL;
struct Value v1, v2, v3, v4, v5; v1.v = ; v2.v = ; v3.v = ; v4.v = ;
v5.v = ; 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=; i<DLinkList_Length(list); i++)
{
pv = (struct Value*)DLinkList_Get(list, i); printf("%d\n", pv->v);
} printf("\n"); DLinkList_Delete(list, DLinkList_Length(list)-);
DLinkList_Delete(list, );
//DLinkList_Delete(list, 3); for(i=; i<DLinkList_Length(list); i++)
{
pv = (struct Value*)DLinkList_Next(list); printf("%d\n", pv->v);
} printf("\n"); DLinkList_Reset(list);
DLinkList_Next(list); pv = (struct Value*)DLinkList_Current(list); printf("%d\n", pv->v); DLinkList_DeleteNode(list, (DLinkListNode*)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);
system("pause");
return ;
}
C 双向链表的更多相关文章
- 学习Redis你必须了解的数据结构——双向链表(JavaScript实现)
本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文链接 http://www.cnblogs.com/tdws/ 下午分享了JavaScript实现单向链表,晚上就来补充下双向链表吧.对链表 ...
- 双向链表、双向循环链表的JS实现
关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法: 单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...
- 剑指Offer面试题:25.二叉搜索树与双向链表
一.题目:二叉搜索树与双向链表 题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向.比如输入下图中左边的二叉搜索树,则输出转换之后的 ...
- Linux 内核数据结构:Linux 双向链表
Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...
- Linux 内核数据结构:双向链表
Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...
- 线性表-双向链表(LinkedList)
双向链表:如图1-3 所示,会把当前header拆分开,重新插入一个Entry<E>. LinkedList源码 0.首先这个类中的两个变量 private transient Entry ...
- Shuffling Machine和双向链表
1. 双向链表 https://github.com/BodhiXing/Data_Structure 2. Shuffling Machine https://pta.patest.cn/pta/t ...
- MS - 1 - 把二元查找树转变成排序的双向链表
## 1. 把二元查找树转变成排序的双向链表 ## ### 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表. ### 要求不能创建任何新的结点,只调整指针的指向. 10 ...
- javascript中的链表结构—双向链表
1.概念 上一个文章里我们已经了解到链表结构,链表的特点是长度不固定,不用担心插入新元素的时候新增位置的问题.插入一个元素的时候,只要找到插入点就可以了,不需要整体移动整个结构. 这里我们了解一下双向 ...
- Java自己实现双向链表LinkList
/** * <p> * Node 双向链表实体类 * <p> * * @author <a href="mailto:yangkj@corp.21cn.com& ...
随机推荐
- I.MX6 Android Linux shell MMPF0100 i2c 设置数据
#!/system/bin/busybox ash # # I.MX6 Android Linux shell MMPF0100 i2c 设置数据 # 说明: # 本文主要记录通过shell脚本来设置 ...
- ubuntu安装和卸载软件命令
ubuntu安装和卸载软件命令 Ubuntu软件安装与删除相关命令 安装软件 命令: apt-get install softname1 softname2 softname3…… 卸载软件 命令: ...
- Python 同时for遍历多个列表
a = range(3) b = range(3) [ (x, y) for x, y in zip(a, b) ] 结果: [ (0,0), (1,1), (2,2) ] 当然,如上可以推广到多个列 ...
- git ssh key for github
第一:检查.ssh是否存在(ls -al ~/.ssh) $ ls -al ~/.ssh Lists the files in your .ssh directory, if they exist 第 ...
- EasyUI + Spring MVC + hibernate实现增删改查导入导出
(这是一个故事--) 前言 作为一个JAVA开发工程师,我觉得最基本是需要懂前端.后台以及数据库. 练习的内容很基础,包括:基本增删改查.模糊查询.分页查询.树菜单.上传下载.tab页 主管发我一个已 ...
- acm-DP整理
一.背包 .各种01背包 void leastOne_Pack(int id, int num ) {//至少取一个: int i, j, c, v ; ; i <= num ; i ++ ) ...
- phonegap Overview
PhoneGap 和 Cordova的关系阐述 是PhoneGap贡献给Apache后的开源项目,是从PhoneGap中抽出的核心代码,是驱动PhoneGap的核心引擎.你可以把他想象成类似于Webk ...
- Objective-C Runtime 运行时之五:协议与分类
Objective-C中的分类允许我们通过给一个类添加方法来扩充它(但是通过category不能添加新的实例变量),并且我们不需要访问类中的代码就可以做到. Objective-C中的协议是普遍存在的 ...
- 传统XmlDocument操作
需要引用的命名空间: using System.Xml; 常用的类:XmlDocument.XmlElement.XmlNode.XmlNodeList 一.使用XmlDocument创建xml // ...
- 文件上传(FileUpload控件)
asp.net 文件上传,大文件上传. 新建一个asp.net页面,在工具栏里拖入 FileUpload 上传控件.一个按钮 Button ! ! ! 进入Button事件 //- ...