双向循环链表(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语言双向循环链表
双向循环链表,先来说说双向链表,双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继 ...
随机推荐
- Ubuntu下安装并配置VS Code编译C++
作者:tongqingliu 转载请注明出处:http://www.cnblogs.com/liutongqing/p/7069091.html Ubuntu下安装并配置VS Code编译C++ 安装 ...
- HTML5 中的拖放
今天,给大家整理一个html5 拖放. 首先,我们先了解一下什么是拖放? 拖放(Drag 和 drop)是 HTML5 标准的组成部分. 拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HT ...
- SCI论文写作中的注意事项
SCI论文一般都是英文的格式,其中有很多原则和细节需要我们注意,在我完成第一篇SCI论文的过程中,做些记录,同时和大家分享一下这些经验.同时也稍微改变一下园子里的人口比例,都是攻城狮,程序猿什么的也过 ...
- (1)认识javascript
认识javascript 在本篇学习资料中,讲解javascript的基本概念.编写工具.在html中的使用: JavaScript 是脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务 ...
- AospExtended K3 Note最新官方版 Android7.1.2 极速 省电 流畅 Galaxy XIAOMI Moto Lenovo Coolpad 均支持
AospExtended 最新官方版 Android7.1.2 极速 省电 流畅 Galaxy XIAOMI Moto Lenovo Coolpad 均支持 之前用过1629开发版等,体验了很 ...
- php中的捕获异常操作
<?php if(!isset($_SESSION)){ session_start(); } include '../common/mysql.class.php'; include '../ ...
- linux 小技巧(磁盘空间搜索)
这里记录一些linux 管理中可能会用到的又容易忘的一些小技巧. linux磁盘写入失败,提示磁盘空间不足.一般都会用df -h 或者df -i看是不是磁盘空间不足或者是inode空间不足.发生这种情 ...
- 每篇半小时1天入门MongoDB——4.MongoDB索引介绍及数据库命令操作
准备工作 继续连接到mongo C:\Users\zouqi>mongo MongoDB shell version: 3.0.7 connecting to: test 查看数据库和集合 &g ...
- MySQL基础语法命令
1. 建表 创建MySQL数据表需要以下信息: 表名 表字段名 定义每个表字段 通用语法: CREATE TABLE table_name (column_name column_type); 实例: ...
- winform利用委托delegate进行窗体间通信
前段时间学习委托,感觉很模糊的样子,也做过许多实例,但是项目中一直没有用到,今天在项目中遇到一个很简单的例子,现在拿出来,做一个简单的记录. 要求:将弹出框里勾选的内容返回到主面板上. 工具:委托. ...