双向循环链表(C语言描述)(二)
链表的基本操作基于对链表的遍历;计算链表的长度就是对链表进行一次遍历:
int linkedlist_length(const LinkedList list) {
assert(list);
int length = ;
LinkedListNode * pCurNode = list->next;
while (pCurNode != list) {
length++;
pCurNode = pCurNode->next;
}
return length;
}
双向链表有两个遍历方向,通过定义一个枚举类型,在遍历时指定遍历方向,在链表过长时可以节省遍历时间:
typedef enum {
TRAVELDIR_FORWARD, TRAVELDIR_BACKWARD
} LinkedListTravelDir;
向链表中插入元素时,首先申请新节点的内存空间,使工作指针(pCurNode)指向新节点插入位置的前一个节点,使新节点的前驱指针指向这个节点,后继指针指向这个节点的下一个节点(如下左图);然后使插入位置前一个节点的后继指针指向新节点,使插入位置后一个节点的前驱指针指向新节点(如下右图),完成新节点的插入。


链表的头和尾不需要做特殊处理,函数linkedlist_insert()函数实现如下:
void linkedlist_insert(LinkedList list, LinkedListTravelDir dir, int location,
const LinkedListData data) {
LinkedListNode * pCurNode = list;
assert(location > && location <= linkedlist_length(list) + ); // alloc new node
LinkedListNode * pNode = malloc(sizeof(LinkedListNode));
assert(pNode);
memcpy(&(pNode->data), &data, sizeof(LinkedListData)); // move current pointer to prior node
if (dir == TRAVELDIR_FORWARD) {
for (int i = ; i < location - ; i++, pCurNode = pCurNode->next)
; } else {
if (dir == TRAVELDIR_BACKWARD) {
for (int i = ; i < location; i++, pCurNode = pCurNode->prior)
;
}
} // insert new node
pNode->next = pCurNode->next;
pNode->prior = pCurNode;
pCurNode->next = pNode;
pNode->next->prior = pNode;
}
因为节点数据是自定义数据类型,因此复制节点数据时使用memcpy()函数。
从链表中删除元素时,首先使工作指针(pCurNode)指向要删除的节点(如下左图),使它前一个节点的后继指针指向它的后一个节点,使它后一个节点的前驱指针指向它的前一个节点(如下右图),释放它的内存空间,完成节点的删除。


链表的头和尾同样不需要做特殊处理,函数linkedlist_delete()实现如下:
void linkedlist_delete(LinkedList list, LinkedListTravelDir dir, int location) {
LinkedListNode * pCurNode = list;
assert(location > && location < linkedlist_length(list) + );
// move current pointer to the node will deleted
if (dir == TRAVELDIR_FORWARD) {
for (int i = ; i < location; i++, pCurNode = pCurNode->next)
;
} else {
if (dir == TRAVELDIR_BACKWARD) {
for (int i = ; i < location; i++, pCurNode = pCurNode->prior)
;
}
}
// delete current node
pCurNode->prior->next = pCurNode->next;
pCurNode->next->prior = pCurNode->prior;
free(pCurNode);
}
查找链表元素同样是对链表进行遍历,需要一个变量(location)用于记录遍历经过的节点数量;因为链表节点的数据域是自定义数据类型,函数linkedlist_locate()的第4个参数接收一个函数指针,它应该指向用于比较两个链表节点数据的函数,当两个链表节点数据相等时,这个函数应该返回0。由于链表中可能存在多个相同的元素,因此用于记录遍历经过节点数量的变量(location)和工作指针(pCurNode)是静态变量,当list参数传入值为空时,查找从上一个找到的位置开始;元素未找到,则返回-1:
int linkedlist_locate(const LinkedList list, LinkedListTravelDir dir,
const LinkedListData data, int (*fpCompare)(const void *, const void *)) {
static int location = ;
static LinkedListNode * pCurNode = NULL; // if list argument is NULL, continue to start locate
if (list) {
location = ;
pCurNode = list->next;
}
assert(location && pCurNode); // locate data
while (pCurNode != list) {
if (!fpCompare(&(pCurNode->data), &data)) {
return location;
}
location++;
if (dir == TRAVELDIR_FORWARD) {
pCurNode = pCurNode->next;
} else {
if (dir == TRAVELDIR_BACKWARD) {
pCurNode = pCurNode->prior;
}
}
} return -;
}
获取元素的函数linkedlist_get()实现比较简单,它返回指向相应位置节点数据的指针,调用者可以通过这个指针修改数据而不会直接修改到节点的指针域:
LinkedListData * linkedlist_get(LinkedList list, LinkedListTravelDir dir,
int location) {
LinkedListNode * pCurNode = list;
assert(location > && location < linkedlist_length(list) + ); // move pointer to the node wanna get
if (dir == TRAVELDIR_FORWARD) {
for (int i = ; i < location; i++, pCurNode = pCurNode->next)
;
} else {
if (dir == TRAVELDIR_BACKWARD) {
for (int i = ; i < location; i++, pCurNode = pCurNode->prior)
;
}
} return &(pCurNode->data);
}
*本例中的图截取自Data Display Debugger,在Ubuntu Linux下可以使用apt-get命令安装它:
sudo apt-get install ddd
双向循环链表(C语言描述)(二)的更多相关文章
- 一种神奇的双向循环链表C语言实现
最近在看ucore操作系统的实验指导.里面提要一个双向循环链表的数据结构,挺有意思的. 其实这个数据结构本身并不复杂.在普通链表的基础上加一个前向指针,我们就得到了双向链表,再把头尾节点连起来就是双向 ...
- 带头结点的双向循环链表----------C语言
/***************************************************** Author:Simon_Kly Version:0.1 Date: 20170520 D ...
- 双向循环链表(C语言描述)(四)
下面以一个电子英汉词典程序(以下简称电子词典)为例,应用双向循环链表.分离数据结构,可以使逻辑代码独立于数据结构操作代码,程序结构更清晰,代码更简洁:电子词典的增.删.查.改操作分别对应于链表的插入. ...
- 双向循环链表(C语言描述)(一)
双向循环链表是链表的一种,它的每个节点也包含数据域和指针域.为了方便程序维护,可以单独为数据域定义一种数据类型,这里以整型为例: typedef int LinkedListData; 双向循环链表( ...
- C语言通用双向循环链表操作函数集
说明 相比Linux内核链表宿主结构可有多个链表结构的优点,本函数集侧重封装性和易用性,而灵活性和效率有所降低. 可基于该函数集方便地构造栈或队列集. 本函数集暂未考虑并发保护. 一 ...
- 1.Go语言copy函数、sort排序、双向链表、list操作和双向循环链表
1.1.copy函数 通过copy函数可以把一个切片内容复制到另一个切片中 (1)把长切片拷贝到短切片中 package main import "fmt" func main() ...
- 【C语言教程】“双向循环链表”学习总结和C语言代码实现!
双向循环链表 定义 双向循环链表和它名字的表意一样,就是把双向链表的两头连接,使其成为了一个环状链表.只需要将表中最后一个节点的next指针指向头节点,头节点的prior指针指向尾节点,链表就能成环儿 ...
- c语言编程之双向循环链表
双向循环链表就是形成两个环,注意每个环的首尾相连基本就可以了. 程序中采用尾插法进行添加节点. #include<stdio.h> #include<stdlib.h> #de ...
- 双向循环链表涉及双向指针的基本操作(C语言)
链表大概分为有无头指针,有无尾指针,是否循环,单向还是双向, 这些都很简单,前提是你要把指针和单链表理解透彻.这些都是基于单链表 的变形,要根据实际问题,选择链表的类型. 头指针的指针域储存着储存头节 ...
- c语言双向循环链表
双向循环链表,先来说说双向链表,双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继 ...
随机推荐
- URLWRITE视图重写技术
UrlRewrite就是地址重写,用户得到的全部都是经过处理后的URL地址,类似于Apache的mod_rewrite.将我们的动态网页地址转化为静态的地址,如html.shtml,还可以隐藏网页的真 ...
- mysql CMD命令
1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root ...
- python 导入informixdb模块
最近碰到Linux平台使用python连接informixdb数据库的问题.整理如下: 1.安装 informixdb 下载InformixDB-2.5.tar.gz 解压之后,在README文档下看 ...
- 【Android Developers Training】 77. 使用Wi-Fi P2P进行服务搜索
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 怎么利用composer创建laravel项目
前提:已经安装了composer的电脑 创建laravel项目: 第一步: 找到你要创建文件的地方 然后打开doc,输入:composer create_project laravel/laravel ...
- C++ 派生类到基类转换的可访问性
今天看c++ primer关于派生类到基类转换的可访问性,看的很晕,看了下面的文章恍然大悟: http://www.2cto.com/kf/201403/283389.html C++ primer第 ...
- docker 17 安装
docker17 安装 新增一键安装命令: curl -sSL https://get.docker.com/ | sh 以下为手动安装过程 翻译自 Get Docker for Ubuntu Doc ...
- [图形学] Chp8.4 OpenGL 二维观察函数——视口
这节有几个显示窗口的控制函数,可以调整视口,创建子窗口,最小化为图标,设置图标名称,隐藏显示等. gluOrtho2D (xwmin, xwmax, ywmin, ywmax); // 定义二维裁剪窗 ...
- 读Zepto源码之Event模块
Event 模块是 Zepto 必备的模块之一,由于对 Event Api 不太熟,Event 对象也比较复杂,所以乍一看 Event 模块的源码,有点懵,细看下去,其实也不太复杂. 读Zepto源码 ...
- MySQL oracle 分页
(1)MySql的Limit m,n语句 Limit后的两个参数中,参数m是起始下标,它从0开始:参数n是返回的记录数.我们需要分页的话指定这两个值即可. 比如:查询10行记录,起始行从3开始 SEL ...