双向链表设计与API实现
为什么需要双向链表?
单链表的结点都只有一个指向下一个结点的指针
单链表的数据元素无法直接访问其前驱元素
逆序访问单链表中的元素是极其耗时的操作!
双向链表的定义
在单链表的结点中增加一个指向其前驱的pre指针
双向链表拥有单链表的所有操作
创建链表
销毁链表
获取链表长度
清空链表
获取第pos个元素操作
插入元素到位置pos
删除位置pos处的元素
插入操作
插入操作异常处理
插入第一个元素异常处理
在0号位置处插入元素;
删除操作
双向链表的新操作
获取当前游标指向的数据元素
将游标重置指向链表中的第一个数据元素
将游标移动指向到链表中的下一个数据元素
将游标移动指向到链表中的上一个数据元素
直接指定删除链表中的某个数据元素
双向链表重要技术场景
循环链表插入结点技术场景
循环链表删除结点技术场景
优点:双向链表在单链表的基础上增加了指向前驱的指针
功能上双向链表可以完全取代单链表的使用
双向链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素
缺点:代码复杂
// dlinklist.h // 双向链表API声明 #ifndef _DLINKLIST_H_ #define _DLINKLIST_H_ typedef void DLinkList; typedef struct _tag_DLinkListNode { _tag_DLinkListNode *next; _tag_DLinkListNode *pre; }DLinkListNode; // 创建链表 DLinkList* DLinkList_Create(); // 销毁链表 void DLinkList_Destroy(DLinkList *list); // 清空链表 void DLinkList_Clear(DLinkList *list); // 获取链表长度 int DLinkList_Length(DLinkList *list); // 在pos位置,插入结点node int DLinkList_Insert(DLinkList *list, DLinkListNode *node, int pos); // 获取pos位置的结点,返回给上层 DLinkListNode* DLinkList_Get(DLinkList *list, int pos); // 删除pos位置的结点 DLinkListNode* DLinkList_Delete(DLinkList *list, int pos); // 删除值为node的结点 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.cpp // 循环链表API实现 #include <cstdio> #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->header.next = NULL; ret->header.pre = NULL; ret->slider = NULL; ret->length = 0; } return ret; } // 销毁链表 void DLinkList_Destroy(DLinkList *list) { if (list != NULL) { free(list); } return; } // 清空链表 void DLinkList_Clear(DLinkList *list) { TDLinkList *tList = (TDLinkList *)list; if (tList != NULL) { tList->header.next = NULL; tList->header.pre = NULL; tList->slider = NULL; tList->length = 0; } return; } // 获取链表长度 int DLinkList_Length(DLinkList *list) { TDLinkList *tList = (TDLinkList *)list; int ret = -1; if (tList != NULL) { ret = tList->length; } return ret; } // 在pos位置,插入结点node int DLinkList_Insert(DLinkList *list, DLinkListNode *node, int pos) { TDLinkList *tList = (TDLinkList *)list; int ret = -1, i = 0; if (list != NULL && node != NULL && pos >= 0) { ret = 0; DLinkListNode *cur = (DLinkListNode *)tList; DLinkListNode *next = NULL; for (i = 0; i < pos && cur->next != NULL; ++i) { cur = cur->next; } next = cur->next; cur->next = node; node->next = next; // 当链表插入第一个结点时需要进行特殊处理 if (next != NULL) { next->pre = node; } node->pre = cur; if (tList->length == 0) { tList->slider = node; // 当链表插入第一个元素处理游标 } // 若在0位置插入,需要特殊处理,新来的结点next前pre指向NULL if (cur == (DLinkListNode *)tList) { node->pre = NULL; } ++tList->length; } return ret; } // 获取pos位置的结点,返回给上层 DLinkListNode* DLinkList_Get(DLinkList *list, int pos) { TDLinkList *tList = (TDLinkList *)list; DLinkListNode* ret = NULL; int i = 0; if (list != NULL && pos >= 0 && pos < tList->length) { DLinkListNode *cur = (DLinkListNode *)tList; for (i = 0; i < pos; ++i) { cur = cur->next; } ret = cur->next; } return ret; } // 删除pos位置的结点 DLinkListNode* DLinkList_Delete(DLinkList *list, int pos) { TDLinkList *tList = (TDLinkList *)list; DLinkListNode* ret = NULL; int i = 0; if (tList != NULL && pos >= 0) { DLinkListNode *cur = (DLinkListNode *)tList; DLinkListNode *next = NULL; for (i = 0; i < pos && cur->next != NULL; ++i) { cur = cur->next; } ret = cur->next; next = ret->next; cur->next = next; if (next != NULL) { next->pre = cur; if (cur == (DLinkListNode *)tList) { // 第0个位置,需要特殊处理 next->pre = NULL; } } if (tList->slider == ret) { tList->slider = next; } --tList->length; } return ret; } // 删除值为node的结点 DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node) { TDLinkList *tList = (TDLinkList *)list; DLinkListNode* ret = NULL; int i = 0; if (tList != NULL) { DLinkListNode *cur = (DLinkListNode *)tList; for (i = 0; i < DLinkList_Length(tList); ++i) { if (cur->next == node) { ret = cur->next; break; } cur = cur->next; } if (!ret) { DLinkList_Delete(tList, i); } } return ret; } // 重置游标 DLinkListNode* DLinkList_Reset(DLinkList* list) { TDLinkList *tList = (TDLinkList *)list; DLinkListNode* ret = NULL; if (tList != NULL) { tList->slider = tList->header.next; ret = tList->slider; } return ret; } // 获取当前游标所指的结点 DLinkListNode* DLinkList_Current(DLinkList* list) { TDLinkList *tList = (TDLinkList *)list; DLinkListNode* ret = NULL; if (tList != NULL) { ret = tList->slider; } return ret; } // 获取游标当前所指结点,然后让游标指向下一个结点 DLinkListNode* DLinkList_Next(DLinkList* list) { TDLinkList *tList = (TDLinkList *)list; DLinkListNode* ret = NULL; if (tList != NULL && tList->slider != NULL) { ret = tList->slider; tList->slider = ret->next; } return ret; } // 获取游标当前所指结点,然后让游标指向前一个结点 DLinkListNode* DLinkList_Pre(DLinkList* list) { TDLinkList *tList = (TDLinkList *)list; DLinkListNode* ret = NULL; if (tList != NULL && tList->slider != NULL) { ret = tList->slider; tList->slider = ret->pre; } return ret; }
// main.cpp // 循环线表测试程序 #include <cstdio> #include "dlinklist.h" const int maxn = 5; struct Student { DLinkListNode node; int age; }; void play() { Student s[maxn]; for (int i = 0; i < maxn; ++i) { s[i].age = i + 21; } DLinkList *list = NULL; list = DLinkList_Create(); // 创建链表 // 插入结点 for (int i = 0; i < maxn; ++i) { int ret = DLinkList_Insert(list, (DLinkListNode *)&s[i], DLinkList_Length(list)); if (ret < 0) { return; printf("function DLinkList_Insert err.\n"); } } // 遍历链表 for (int i = 0; i < DLinkList_Length(list); ++i) { Student *tmp = (Student *)DLinkList_Get(list, i); if (tmp == NULL) { printf("function DLinkList_Get err.\n"); return; } printf("age: %d\n", tmp->age); } DLinkList_Delete(list, DLinkList_Length(list) - 1); // 删除尾结点 DLinkList_Delete(list, 0); // 删除头结点 // 用游标遍历链表 for (int i = 0; i < DLinkList_Length(list); ++i) { Student *tmp = (Student *)DLinkList_Next(list); if (tmp == NULL) { printf("function DLinkList_Next err.\n"); return; } printf("age: %d\n", tmp->age); } printf("\n"); DLinkList_Reset(list); DLinkList_Next(list); Student *tmp = (Student *)DLinkList_Current(list); if (tmp == NULL) { printf("function DLinkList_Current err.\n"); return; } printf("age: %d\n", tmp->age); DLinkList_DeleteNode(list, (DLinkListNode*)tmp); tmp = (Student *)DLinkList_Current(list); if (tmp == NULL) { printf("function DLinkList_Current err.\n"); return; } printf("age: %d\n", tmp->age); printf("length: %d\n", DLinkList_Length(list)); DLinkList_Pre(list); tmp = (Student *)DLinkList_Current(list); if (tmp == NULL) { printf("function DLinkList_Current err.\n"); return; } printf("age: %d\n", tmp->age); printf("length: %d\n", DLinkList_Length(list)); DLinkList_Destroy(list); return; } int main() { play(); return 0; }
双向链表设计与API实现的更多相关文章
- Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!
前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...
- Laravel5设计json api时候的一些道道
对于返回数据格式没规整的问题 在开发api的时候,这个问题是和客户端交涉最多的问题,比如一个user结构,返回的字段原本是个user_name的,它应该是string类型.但是呢,由于数据库设计这个字 ...
- Flask 学习篇一: 搭建Python虚拟环境,安装flask,并设计RESTful API。
前些日子,老师给我看了这本书,于是便开始了Flask的学习 GitHub上的大神,于是我也在GitHub上建了一个Flask的项目. 有兴趣可以看看: https://github.com/Silen ...
- 设计 REST API 的13个最佳实践
写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口这件事情就成为了家常便饭,并且,还伴随着无数的争论与无奈.编写友好的 restful api 不论对于你的同事,还是将来作为第三方服 ...
- 使用 Python 和 Flask 设计 RESTful API
近些年来 REST (REpresentational State Transfer) 已经变成了 web services 和 web APIs 的标配. 在本文中我将向你展示如何简单地使用 Pyt ...
- 13 个设计 REST API 的最佳实践
原文 RESTful API Design: 13 Best Practices to Make Your Users Happy 写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口 ...
- 学习设计接口api(转)
介绍 先说说啥是 Api 吧,以下摘自百度百科: API (Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于 ...
- 循环链表设计与API实现
基本概念 循环链表的定义:将单链表中最后一个数据元素的next指针指向第一个元素 循环链表拥有单链表的所有操作 创建链表 销毁链表 获取链表长度 清空链表 获取第pos个元素操作 插入元素到位置pos ...
- Spring Boot Security 整合 OAuth2 设计安全API接口服务
简介 OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版.本文重点讲解Spring Boot项目对OAuth2进行的实现,如果你对OAut ...
随机推荐
- markdown绘图插件----mermaid简介
作者:黄永刚 mermaid简介 当撰写文档的时候,对于流程图的生成大多使用Visio等繁重的工具,没有一种轻便的工具能够画图从而简化文档的编写,就像markdown那样. mermaid解决这个痛点 ...
- 干货!Android Studio快捷键VS Eclipse快捷键~亲测!
eclipse as 英文描述 中文描述 ctrl+shift+r ctrl+shift+r Navigate->File 找工作空间的文件 ctrl+shift+t ctrl+shift+t ...
- 解决 oracle IO占用率很高的问题
突然user io占用率很很高,看了一个AWR报告,发现direct path read temp,direct path write temp的的数率很高,后来怀疑是临时表空间不够了,就试着设了一下 ...
- How to Collect Bne Log Files for GL Integrators
In this Document Goal Solution APPLIES TO: Oracle General Ledger - Version 11.0 and laterInforma ...
- android Handler机制之ThreadLocal详解
概述 我们在谈Handler机制的时候,其实也就是谈Handler.Message.Looper.MessageQueue之间的关系,对于其工作原理我们不做详解(Handler机制详解). Messa ...
- 关于在页面中针对不同版本的IE浏览器实现不同的JS或者CSS样式
一般会用到<!--[if IE]>这里是正常的html代码<![endif]--> 条件注释只能在windows Internet Explorer(以下简称IE)下使用,因此 ...
- 程序员的自我修养-----Java开发的必须知道的几个注意点
1. 将一些需要变动的配置写在属性文件中 比如,没有把一些需要并发执行时使用的线程数设置成可在属性文件中配置.那么你的程序无论在DEV环境中,还是TEST环境中,都可以顺畅无阻地运行,但是一旦部署在P ...
- 【移动开发】 Android隐藏输入法软键盘的一些说明
刚刚在写一个仿微信的Android聊天软件,在编写的过程中,发现一个严重的BUG---当用户点击输入框用软键盘输入文本的时候点击了"返回好友列表"的按钮,返回到好友列表时软键盘无法 ...
- 使用github搭建网站
http://blog.csdn.net/pipisorry/article/details/51707366 使用github建站 github设计了Pages功能,允许用户自定义项目首页,用来替代 ...
- UE4读取scv文件 -- 数据驱动游戏性元素
官方文档链接:http://docs.unrealengine.com/latest/CHN/Gameplay/DataDriven/index.html 略懒,稍微麻烦重复的工作,总希望能找人帮忙一 ...