链表的基本操作基于对链表的遍历;计算链表的长度就是对链表进行一次遍历:

 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语言描述)(二)的更多相关文章

  1. 一种神奇的双向循环链表C语言实现

    最近在看ucore操作系统的实验指导.里面提要一个双向循环链表的数据结构,挺有意思的. 其实这个数据结构本身并不复杂.在普通链表的基础上加一个前向指针,我们就得到了双向链表,再把头尾节点连起来就是双向 ...

  2. 带头结点的双向循环链表----------C语言

    /***************************************************** Author:Simon_Kly Version:0.1 Date: 20170520 D ...

  3. 双向循环链表(C语言描述)(四)

    下面以一个电子英汉词典程序(以下简称电子词典)为例,应用双向循环链表.分离数据结构,可以使逻辑代码独立于数据结构操作代码,程序结构更清晰,代码更简洁:电子词典的增.删.查.改操作分别对应于链表的插入. ...

  4. 双向循环链表(C语言描述)(一)

    双向循环链表是链表的一种,它的每个节点也包含数据域和指针域.为了方便程序维护,可以单独为数据域定义一种数据类型,这里以整型为例: typedef int LinkedListData; 双向循环链表( ...

  5. C语言通用双向循环链表操作函数集

    说明 相比Linux内核链表宿主结构可有多个链表结构的优点,本函数集侧重封装性和易用性,而灵活性和效率有所降低.     可基于该函数集方便地构造栈或队列集.     本函数集暂未考虑并发保护. 一  ...

  6. 1.Go语言copy函数、sort排序、双向链表、list操作和双向循环链表

    1.1.copy函数 通过copy函数可以把一个切片内容复制到另一个切片中 (1)把长切片拷贝到短切片中 package main import "fmt" func main() ...

  7. 【C语言教程】“双向循环链表”学习总结和C语言代码实现!

    双向循环链表 定义 双向循环链表和它名字的表意一样,就是把双向链表的两头连接,使其成为了一个环状链表.只需要将表中最后一个节点的next指针指向头节点,头节点的prior指针指向尾节点,链表就能成环儿 ...

  8. c语言编程之双向循环链表

    双向循环链表就是形成两个环,注意每个环的首尾相连基本就可以了. 程序中采用尾插法进行添加节点. #include<stdio.h> #include<stdlib.h> #de ...

  9. 双向循环链表涉及双向指针的基本操作(C语言)

    链表大概分为有无头指针,有无尾指针,是否循环,单向还是双向, 这些都很简单,前提是你要把指针和单链表理解透彻.这些都是基于单链表 的变形,要根据实际问题,选择链表的类型. 头指针的指针域储存着储存头节 ...

  10. c语言双向循环链表

    双向循环链表,先来说说双向链表,双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继 ...

随机推荐

  1. Ubuntu下安装并配置VS Code编译C++

    作者:tongqingliu 转载请注明出处:http://www.cnblogs.com/liutongqing/p/7069091.html Ubuntu下安装并配置VS Code编译C++ 安装 ...

  2. HTML5 中的拖放

    今天,给大家整理一个html5 拖放. 首先,我们先了解一下什么是拖放? 拖放(Drag 和 drop)是 HTML5 标准的组成部分. 拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HT ...

  3. SCI论文写作中的注意事项

    SCI论文一般都是英文的格式,其中有很多原则和细节需要我们注意,在我完成第一篇SCI论文的过程中,做些记录,同时和大家分享一下这些经验.同时也稍微改变一下园子里的人口比例,都是攻城狮,程序猿什么的也过 ...

  4. (1)认识javascript

    认识javascript 在本篇学习资料中,讲解javascript的基本概念.编写工具.在html中的使用: JavaScript 是脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务 ...

  5. AospExtended K3 Note最新官方版 Android7.1.2 极速 省电 流畅 Galaxy XIAOMI Moto Lenovo Coolpad 均支持

    AospExtended 最新官方版 Android7.1.2 极速 省电 流畅 Galaxy  XIAOMI Moto  Lenovo  Coolpad  均支持 之前用过1629开发版等,体验了很 ...

  6. php中的捕获异常操作

    <?php if(!isset($_SESSION)){ session_start(); } include '../common/mysql.class.php'; include '../ ...

  7. linux 小技巧(磁盘空间搜索)

    这里记录一些linux 管理中可能会用到的又容易忘的一些小技巧. linux磁盘写入失败,提示磁盘空间不足.一般都会用df -h 或者df -i看是不是磁盘空间不足或者是inode空间不足.发生这种情 ...

  8. 每篇半小时1天入门MongoDB——4.MongoDB索引介绍及数据库命令操作

    准备工作 继续连接到mongo C:\Users\zouqi>mongo MongoDB shell version: 3.0.7 connecting to: test 查看数据库和集合 &g ...

  9. MySQL基础语法命令

    1. 建表 创建MySQL数据表需要以下信息: 表名 表字段名 定义每个表字段 通用语法: CREATE TABLE table_name (column_name column_type); 实例: ...

  10. winform利用委托delegate进行窗体间通信

    前段时间学习委托,感觉很模糊的样子,也做过许多实例,但是项目中一直没有用到,今天在项目中遇到一个很简单的例子,现在拿出来,做一个简单的记录. 要求:将弹出框里勾选的内容返回到主面板上. 工具:委托. ...