为什么需要双向链表?

单链表的结点都只有一个指向下一个结点的指针

单链表的数据元素无法直接访问其前驱元素

逆序访问单链表中的元素是极其耗时的操作!

双向链表的定义

在单链表的结点中增加一个指向其前驱的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实现的更多相关文章

  1. Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!

    前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...

  2. Laravel5设计json api时候的一些道道

    对于返回数据格式没规整的问题 在开发api的时候,这个问题是和客户端交涉最多的问题,比如一个user结构,返回的字段原本是个user_name的,它应该是string类型.但是呢,由于数据库设计这个字 ...

  3. Flask 学习篇一: 搭建Python虚拟环境,安装flask,并设计RESTful API。

    前些日子,老师给我看了这本书,于是便开始了Flask的学习 GitHub上的大神,于是我也在GitHub上建了一个Flask的项目. 有兴趣可以看看: https://github.com/Silen ...

  4. 设计 REST API 的13个最佳实践

    写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口这件事情就成为了家常便饭,并且,还伴随着无数的争论与无奈.编写友好的 restful api 不论对于你的同事,还是将来作为第三方服 ...

  5. 使用 Python 和 Flask 设计 RESTful API

    近些年来 REST (REpresentational State Transfer) 已经变成了 web services 和 web APIs 的标配. 在本文中我将向你展示如何简单地使用 Pyt ...

  6. 13 个设计 REST API 的最佳实践

    原文 RESTful API Design: 13 Best Practices to Make Your Users Happy 写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口 ...

  7. 学习设计接口api(转)

    介绍 先说说啥是 Api 吧,以下摘自百度百科: API (Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于 ...

  8. 循环链表设计与API实现

    基本概念 循环链表的定义:将单链表中最后一个数据元素的next指针指向第一个元素 循环链表拥有单链表的所有操作 创建链表 销毁链表 获取链表长度 清空链表 获取第pos个元素操作 插入元素到位置pos ...

  9. Spring Boot Security 整合 OAuth2 设计安全API接口服务

    简介 OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版.本文重点讲解Spring Boot项目对OAuth2进行的实现,如果你对OAut ...

随机推荐

  1. oh forever love~

    npm install -g forever forever start c9sdk/server.js --listen 0.0.0.0 --port 80 -a aa:111 -w ~ To el ...

  2. GitLab服务器IP地址设置

    最近使用GitLab 搭建了Git的私有仓库,但是发现私有仓库的地址居然是localhost,不是本机的IP地址,最后百度了一下,找了很久才找到,特此记录一下. 首先说明一下,我linux虚拟机的IP ...

  3. 好用的SQLAlchemy

    准备 安装SQLAlchemy框架 测试代码 知识点剖析 引入库支持 基类和引擎 实体类 声明类 数据库自动完成 CRUD 总结 这里简单的记录一下本人第一次使用SQLAlchemy这个ORM框架的过 ...

  4. ROS机器人程序设计(原书第2版)补充资料 (零) 源代码、资料和印刷错误修订等 2017年01月01日更新

    ROS机器人程序设计(原书第2版)补充资料 (零) 源代码等 ROS官网 版)部分内容修订 页:第1行,删去$ 页:第6行,float64 y 前面加一个空格 页:中间创建主题:下面程序不用换行,(& ...

  5. [lua]写个简单的Lua拓展-sleep函数

    这几天在做一个小项目,其中用到了一些基本的API, 例如sleep,获取当前目录等等,lua标准库中没有提供这些接口,虽然所第三方库中也都有实现,但是要用的就那么几个函数,在一个嵌入式系统中安装那么多 ...

  6. Java多线程的调度策略

    在Java多线程环境中,为保证所有线程的执行能按照一定的规则执行,JVM实现了一个线程调度器,它定义了线程调度的策略,对于CPU运算的分配都进行了规定,按照这些特定的机制为多个线程分配CPU的使用权. ...

  7. SQLite 运算符(http://www.w3cschool.cc/sqlite/sqlite-operators.html)

    SQLite 运算符 SQLite 运算符是什么? 运算符是一个保留字或字符,主要用于 SQLite 语句的 WHERE 子句中执行操作,如比较和算术运算. 运算符用于指定 SQLite 语句中的条件 ...

  8. 20 ViewPager Demo4自动轮播

    MainActivity.java 思想:才用非常大的数 让其看起来可以循环轮播图片并且用户可以从尽头滑到首图的特点 . package com.qf.day20_viewpager_demo4; i ...

  9. JAVA面向对象-----继承

    类和类之间的常见关系. 既然继承是描述类和类之间的关系,就需要先来了解类和类之间的常见关系 现实生活的整体与部分 举例说明 现实生活 学生 是人 狗 是动物 球队 包含 球员 整体与部分的关系,部分可 ...

  10. 所谓 Spinner 组件

    Spinner组件在平常的开发过程中应该算是比较常用的隶属于Input Control的android控件了,所以我们很有必要掌握这一个知识点,下面我就来谈一谈我对这个组件的一些认识. 是什么? 下拉 ...