双向链表设计与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 ...
随机推荐
- An internal error occurred during: "Retrieving archetypes:". GC overhead limit exceeded
An internal error occurred during: "Retrieving archetypes:".GC overhead limit exceeded 异常, ...
- Request和Response的格式
Request和Response的格式 Request格式: HTTP请求行 (请求)头 空行 可选的消息体 注:请求行和标题必须以<CR><LF> 作为结尾(也就是,回 ...
- iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge
WebViewJavascriptBridge是一个有点年代的JS与OC交互的库,使用该库的著名应用还挺多的,目前这个库有7000+star.我去翻看了它的第一版本已经是4年前了,在版本V4.1.4以 ...
- 学习Tensorflow,反卷积
在深度学习网络结构中,各个层的类别可以分为这几种:卷积层,全连接层,relu层,pool层和反卷积层等.目前,在像素级估计和端对端学习问题中,全卷积网络展现了他的优势,里面有个很重要的层,将卷积后的f ...
- Android 增量更新和升级
在年初的时候,尝试了一把热修复技术,当时选择的是阿里的andfix,使用起来也很简单,这里就不在多少,如果你对andfix有兴趣请链接:点击打开链接.虽然网上将热修复的文章很多,不过我还是想说原理,然 ...
- acm入门搜索-石油数目
题意:给出一个N*M的矩形区域和每个区域的状态--有/没有石油,(定义)如果两个有石油的区域是相邻的(水平.垂直.斜)则认为这是属于同一个oil pocket. 求这块矩形区域一共有多少oilpock ...
- SSH深度历险(三) EJB Session Bean有状态和无状态的区别与联系
刚开始对两种sessionbean存在误解,认为有状态是实例一直存在,保存每次调用后的状态,并对下一次调用起作用,而认为无状态是每次调用实例化一次,不保留用户信息.仔细分析并用实践检验后,会发现,事实 ...
- input事件--->按键事件的基本实现
本程序基于TINY4412开发板,程序已经验证过,完全正确: 那么,如何来写这样的一个驱动程序呢? 1.分配一个input_dev结构体 2.设置 3.注册 4.硬件相关的代码,比如中断,定时器,休眠 ...
- (九十)使用多个storyboard+代码实现控制器的分开管理
使用单个storyboard会使得项目难与管理,使用纯代码又会过于麻烦,因此如果能将二者结合起来,并且使用多个storyboard,会使得项目简单简单.方便许多. 下面以一个简单的视图关系为例,介绍多 ...
- 《java入门第一季》之HashSet存储元素保证唯一性的代码及图解
上一篇介绍了HashSet存储自定义自定义对象时应该注意的问题http://blog.csdn.net/qq_32059827/article/details/51580642, 这一篇对其内部结构稍 ...