双向循环链表(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语言双向循环链表
双向循环链表,先来说说双向链表,双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继 ...
随机推荐
- 【踩坑记录】记一次MySQL主从复制延迟的坑
最近开发中遇到的一个MySQL主从延迟的坑,记录并总结,避免再次犯同样的错误. 情景 一个活动信息需要审批,审批之后才能生效.因为之后活动要编辑,编辑后也可能触发审批,审批中展示的是编辑前的活动内容, ...
- Masonry 在swift下的使用
Masonry在oc下使用很方便,但是在swift下,由于oc方法和property都可以使用.fuc的语法,swift下只有属性可以使用.property的语法,方法只能写成.func().因此在s ...
- PHP中对汉字进行UNICODE编码和解码的实现
<?php /** PHP中对汉字进行UNICODE编码和解码的实现 **/ class Helper_Tool{ //php中的unicode编码转中文 static function uni ...
- [leetcode-565-Array Nesting]
A zero-indexed array A consisting of N different integers is given. The array contains all integers ...
- python实现简单的循环购物车小功能
python实现简单的循环购物车小功能 # -*- coding: utf-8 -*- __author__ = 'hujianli' shopping = [ ("iphone6s&quo ...
- ExtJs异步无法向外传值和赋值的解决办法,亲测有效
1.Ext.data.Store.load();方法是异步的,下面的方式获得的reCount始终是0,因为还没等后台的方法执行完就赋值了,此时store的record还没获得值. var testSt ...
- ionic 最简单的路由形式,头部固定,下面tab切换-------一个简单的单页切换起飞了
<ion-header-bar class="bar-dark" align-title="left"> <h1 class="ti ...
- VB6之HTTP服务器的实现(二)
接上篇,这次做了小小的改动和提升.增加了对POST的支持和对其他方法(GET和POST之外的)选择405回复.另外,增加了对CGI的支持,目前可以使用C语言来写(是不是好蠢的赶脚).相对于上篇,整体做 ...
- vijos1090题解
题目: 有n个正整数排成一行.你的目的是要从中取出一个或连续的若干个数,使它们的和能够被k整除. 例如,有6个正整数,它们依次为1.2.6.3.7.4.若k=3,则你可以取出1.2.6,或者2.6.3 ...
- 學習 DT device tree 以 ST 的開發板 STM32F429i-disc1 為例
目標 因為對 device tree 不是很熟悉, 所以就將 device tree, 設為學習目標. 啟動 注意, 這篇隨筆的解說都放在最下面,會標 Explanation_XX,只要搜尋 Expl ...