之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢

延伸到真正的内核链表~(敢问路在何方?路在脚下~)

1. 简介

链表是Linux 内核中最简单,最普通的数据结构。链表是一种存放和操作可变数量元素(常称为节点)

的数据结构,链表和静态数组的不同之处在于,它所包含的元素都是动态创建并插入链表的,在编译

时不必知道具体需要创建多少个元素,另外也因为链表中每个元素的创建时间各不相同,所以它们在

内存中无须占用连续内存区。正是因为元素不连续的存放,所以各个元素需要通过某种方式被链接在

一起,于是每个元素都包含一个指向下一个元素的指针,当有元素加入链表或从链表中删除元素时,

简单调整一下节点的指针就可以了。

根据它的特性,链表可分为:单链表双链表单向循环链表和双向循环链表,今天总结记录的就是

最简单的单链表,

1.1 节点类型描述

 typedef struct node_t {
data_t data; /* 节点数据域 */
struct node_t *next; /* 节点的后继指针域 */
}linknode_t, *linklist_t;

另一种写法

 struct node_t {
data_t data;
struct node_t *next;
}
typedef struct node_t linknode_t;
typedef struct node_t* linklist_t;

细看说明:

* linknode_t A;
* linklist_t p = &A;
*
* 结构变量A为所描述的节点,而指针变量p为指向此类型节点的指针(p值为节点的地址)
* 这样看来 linknode_t 和 linklist_t 的作用是一样的,那么为什么我们要定义两个数据类
* 型(同一种)呢?  答曰:主要为了代码的可读性,我们要求标识符要望文识义,便于理解
*
* linknode_t *pnode  指向一个节点
* linklist_t list  指向一个整体

1.2 头节点 head (~黄河之水天上来~)

在顺序存储线性表,如何表达一个空表{ },是通过list->last = -1来表现的,所谓的空表就是

数据域为NULL,而链表有数据域和指针域,我们如何表现空链表呢?这时,就引入了头结点

的概念,头结点和其他节点数据类型一样,只是数据域为NULL,head->next = NULL,下面

我们看一个创建空链表的函数,如何利用头结点来创建一个空链表,只要头节点在,链表就还在~

 // 创建一个空链表
linklist_t
CreateEmptyLinklist()
{
linklist_t list;
list = (linklist_t)malloc(sizeof(linknode_t)); if(NULL != list) {
list->next = NULL;
} return list;
}

2. 链表基本运算的相关"算法"操作 or 操刀(~烹羊宰牛且为乐,会须一饮三百杯~)

链表的运算除了上面的创建空链表,还有数据的插入,删除,查找等函数,链表的运算有各种实现方

法,如何写出一个高效的,封装性较好的函数是我们要考虑的,比如数据插入函数,我们就要尽可能

考虑所有能出现的结果,比如:1)如果需插入数据的链表是个空表;2)所插入的位置超过了链表的

长度;如果我们的函数能包含所有能出现的情况,不仅能大大提高我们的开发效率,也会减少代码的

错误率。下面看一个可用性较强的链表插入操作的函数实现~

int
InsertLinklist(linklist_t list, int at, data_t x)
{
linknode_t * node_prev, * node_at, * node_new;
int pos_at;
int found = ; if (NULL == list) return -; /* at must >= 0 */
if (at < ) return -; /*第一步、新节点分配空间*/
node_new = (linklist_t)malloc(sizeof(linknode_t));
if (NULL == node_new) {
return -;
}
node_new->data = x; /* assigned value */
/*
*节点如果插入超过链表长度的位置,会接到尾节点后面,
*这样,node_new成了尾节点,node_new->next = NULL
*/
node_new->next = NULL; /*第二步、定位*/
node_prev = list; //跟随指针,帮助我们更好的定位
node_at = list->next; //遍历指针
pos_at = ;
while(NULL != node_at) {
if(pos_at == at){
found = ; //找到正确的位置,跳出循环
break;
}
/* move to the next pos_at */
node_prev = node_at; //跟随指针先跳到遍历指针的位置
node_at = node_at->next; //遍历指针跳到下一个节点的位置
pos_at++;
} /*第三步、插入*/
if(found) {
/* found = 1,找到正确的位置,插入 */
node_new->next = node_at; //插入的节点next指向node_at
node_prev->next = node_new; //插入节点的前一个节点
}
else {
/*若是没找到正确的位置,即所插入位置超越了链表的长度,
*则接到尾节点的后面,同样,这样适用于{ }即空链表,这样
*我们可以建立一个空链表,利用这个函数,实现链表的初始化
*/
node_prev->next = node_new;
} return ;
}

3. 正文开始 Demo(~与君歌一曲,请君为我倾耳听~)

listlink.h

 #ifndef _LIST_LINK_H_
#define _LIST_LINK_H_ typedef int data_t; typedef struct node_t {
data_t data; /* 节点数据域 */
struct node_t * next; /* 节点的后继指针域 */
}linknode_t, * linklist_t; /* 链表操作函数*/ // 创建一个空链表
linklist_t CreateEmptyLinklist(); // 销毁链表
void DestroyLinklist(linklist_t list); // 清空链表
void ClearLinklist(linklist_t list); // 是否为空链表
int IsEmptyLinklist(linklist_t list); // 链表长度
int LengthLinklist(linklist_t list); // 获去链表节点数据
int GetLinklist(linklist_t list, int at, data_t * x); // 设置链表节点数据
int SetLinklist(linklist_t list, int at, data_t x); // 插入节点
int InsertLinklist(linklist_t list, int at, data_t x); // 删除节点
int DeleteLinklist(linklist_t list, int at); // 链表转置
linklist_t ReverseLinklist(linklist_t list); // 打印链表
int Display(linklist_t list); #endif // _LIST_LINK_H_

listlink.c

 #include <stdio.h>
#include <stdlib.h>
#include "listlink.h" // 创建一个空链表
linklist_t
CreateEmptyLinklist()
{
linklist_t list;
list = (linklist_t)malloc(sizeof(linknode_t)); if(NULL != list) {
list->next = NULL;
} return list;
} // 销毁链表
void
DestroyLinklist(linklist_t list)
{
if(NULL != list) {
ClearLinklist(list);
free(list);
}
} // 清空链表
void
ClearLinklist(linklist_t list)
{
linknode_t * node; /* pointer to the node to be remove */
if(NULL == list) return; while(NULL != list->next) {
node = list->next;
list->next = node->next; //此时node->next是第二node节点元素依次往后
free(node);
}
return;
} // 是否为空链表
int
IsEmptyLinklist(linklist_t list)
{
if(NULL != list) {
if(NULL == list->next) // 只有头节点
return ; // 返回1,是个空链表
else
return ; // 返回0,链表非空 } else
return -; // 返回-1, 错误的类型
} // 链表长度
int
LengthLinklist(linklist_t list)
{
int len = ;
linknode_t * node; // 遍历指针 if(NULL == list) return -; node = list->next; // node指针指向第一个节点
while(NULL != node) {
len++;
node = node->next;
} return len;
} // 获去一个链表指定节点数据域的数据值
int
GetLinklist(linklist_t list, int at, data_t * x)
{
linknode_t *node; // 遍历节点的指针
int pos; // 用于遍历比较 if(NULL == list) return -;
/*at 必须要 >= 0*/
if(at < ) return -; /* 从第一个元素开始 */
node = list->next; // node指针指向一个元素
pos = ;
while(NULL != node) {
if(at == pos) {
if(NULL != x)
*x = node->data;
return ;
}
// 下一个元素
node = node->next;
pos++;
}
return -;
} // 设置一个指定链表节点的数据域值
int
SetLinklist(linklist_t list, int at, data_t x)
{
linknode_t * node; // 遍历链表
int pos;
int found = ; if(!list) return -;
/*at 必须 >= 0*/
if(at < ) return -; /* node指针指向第一个元素 */
node = list->next;
pos = ;
while(NULL != node) {
if(at == pos) {
found = ; // 找到了位置
node->data = x;
break;
}
/*往后移动元素*/
node = node->next;
pos++;
}
if( == found)
return ;
else
return -;
} // 插入节点
int
InsertLinklist(linklist_t list, int at, data_t x)
{
/*
* node_at and pos_at are used to locate the position of node_at.
* node_prev follows the node_at and always points to previous node
* of node_at.
* node_new is used to point to the new node to be inserted.
*/
linknode_t * node_prev, * node_at, * node_new;
int pos_at;
int found = ; if(NULL == list) return -; /* at 必须 >= 0 */
if(at < ) return -; node_new = malloc(sizeof(linknode_t));
if(NULL == node_new)
return -;
node_new->data = x; // assigned value
node_new->next = NULL; node_prev = list; // head
node_at = list->next; //node_at指针指向第一元素
pos_at = ;
while(NULL != node_at) {
if(pos_at == at) {
found = ; // found the node ‘at'
break;
}
/* move to the next pos_at */
node_prev = node_at;
node_at = node_at->next;
pos_at++;
} if(found) {
/* insert */
node_new->next = node_at;
node_prev->next = node_new;
} else{
/*
* If not found,means the provided 'at'
* exceeds the upper limit of the list, just
* append the new node to the end of the list
*/
node_prev->next = node_new;
} return ;
} // 删除节点
int
DeleteLinklist(linklist_t list, int at)
{
/*
* node_at and pos_at are used to locate the position of node_at.
* node_prev follows the node_at and always points to previous node
* of node_at.
*/
linknode_t * node_prev, * node_at;
int pos_at;
int found = ; if(!list) return -;
if(at < ) return -; node_prev = list; // node_prev指针指向链表头
node_at = list->next; // node_at指针指向第一元素
pos_at = ; while(NULL != node_at) {
if(pos_at == at) {
// found the node 'at'
found = ;
break;
}
// move to the next pos_at
node_prev = node_at;
node_at = node_at->next;
pos_at++;
}
if(found) {
// remove
node_prev->next = node_at->next;
free(node_at);
return ;
}else
return -;
} // 链表转置
linklist_t
ReverseLinklist(linklist_t list)
{
linknode_t * node; // iterator
linknode_t * node_prev; // previous node of iterator
linknode_t * node_next; /* next node of iterator
* used to backup next of iterator
*/
if(NULL == list) return NULL;
node_prev = NULL;
node = list->next; // node指针指向第一个元素
while(NULL != node) {
/*
* step1: backup node->next
* due to the next of iterator will be
* modified in step2
*/
node_next = node->next;
/*
* when iterator reaches the last node
* of original list, make the list head
* point to the last node, so the original
* last one becomes the first one.
*/
if(NULL == node_next)
list->next = node;
/*
* step2: reverse the linkage between nodes
* make the node pointer to the previous node,
* not the next node
*/
node->next = node_prev;
/*
* step3: move forward
*/
node_prev = node;
node = node_next;
} return list;
} // 打印链表
int
Display(linklist_t list)
{
linknode_t * node; if(NULL == list) return -; node = list->next;
while(node != NULL) {
printf(" %d ", node->data);
node = node->next;
}
printf("\n"); return ;
} int main(int argc, char * argv[])
{
int i;
data_t x;
linklist_t p; /*创建链表*/
p = CreateEmptyLinklist();
Display(p);
data_t a[] = {,,,,,,,,,}; for(i = ; i < ; i++) {
/*插入链表*/
InsertLinklist(p, i, a[i]);
}
Display(p); /*链表转置*/
ReverseLinklist(p);
/*链表长度*/
printf("The length of the list is [%d]\n", LengthLinklist(p));
Display(p); /*获取特定节点值*/
GetLinklist(p, , &x);
printf("The No.4 of this list is [%d]\n", x); /*设置特定节点的值*/
SetLinklist(p, , );
GetLinklist(p, , &x);
printf("After updating! The No.4 of this list is [%d]\n", x);
Display(p); /*删除节点*/
DeleteLinklist(p,);
printf("After delete!The length of list is [%d]\n", LengthLinklist(p));
Display(p); /*清空链表*/
ClearLinklist(p);
if(IsEmptyLinklist(p))
printf("This list is empty!\n");
/*销毁链表*/
DestroyLinklist(p);
printf("This list is destroyed!\n"); return ; }

运行

4. 鸣谢(你动了谁的奶酪? ^_^)

感谢下面博主的共享,本文的基石,谢谢!!

感谢:https://blog.csdn.net/zqixiao_09/article/details/50402523

5. 后记

行歌

在草长莺飞的季节里喃喃低唱 

到处人潮汹涌还会孤独

怎么

在灯火阑珊处竟然会觉得荒芜

从前轻狂绕过时光        

Linux C 数据结构 ->单向链表<-(~千金散尽还复来~)的更多相关文章

  1. Linux C 数据结构 ->单向链表

    之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢 延伸到真正的内核链表~(敢问路在何方?路在脚下~) 1. 简介 链表是Linux 内核中最简单,最普通的数据结 ...

  2. linux内核数据结构之链表

    linux内核数据结构之链表 1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域.后来看代码注释发现该 ...

  3. linux内核数据结构之链表【转】

    转自:http://www.cnblogs.com/Anker/p/3475643.html 1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结 ...

  4. 数据结构-单向链表 C和C++的实现

    数据结构,一堆数据的存放方式. 今天我们学习数据结构中的 链表: 链表的结构: 链表是一种特殊的数组,它的每个元素称为节点,每个节点包括两个部分: 数据域:存放数据,此部分与数组相同 指针域:存放了下 ...

  5. python数据结构——单向链表

    链表 ( Linked List ) 定义:由许多相同数据类型的数据项按照特定顺序排列而成的线性表. 特点:各个数据在计算机中是随机存放且不连续. 优点:数据的增删改查都很方便,当有新的数据加入的时候 ...

  6. Python3玩转单链表——逆转单向链表pythonic版

    [本文出自天外归云的博客园] 链表是由节点构成的,一个指针代表一个方向,如果一个构成链表的节点都只包含一个指针,那么这个链表就是单向链表. 单向链表中的节点不光有代表方向的指针变量,也有值变量.所以我 ...

  7. 玩转C线性表和单向链表之Linux双向链表优化

    前言: 这次介绍基本数据结构的线性表和链表,并用C语言进行编写:建议最开始学数据结构时,用C语言:像栈和队列都可以用这两种数据结构来实现. 一.线性表基本介绍 1 概念: 线性表也就是关系户中最简单的 ...

  8. C# 单向链表数据结构 (一)

    单向链表数据结构是有节点组成,每个节点包含两部分,第一部分为存储数据,第二部分为指向下一个节点的指针.注意,有两个特色的节点,分别为“头节点”和“尾节点”,头节点本身没有数据,只存储下一个节点的指针, ...

  9. 数据结构(1) 第一天 算法时间复杂度、线性表介绍、动态数组搭建(仿Vector)、单向链表搭建、企业链表思路

    01 数据结构基本概念_大O表示法 无论n是多少都执行三个具体步骤 执行了12步 O(12)=>O(1) O(n) log 2 N = log c N / log c N (相当于两个对数进行了 ...

随机推荐

  1. .NET、NET Framewor以及.NET Core的关系(一)

    什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先从类型系统开始讲起,我将通过跨语言操作这个例子来逐渐引入一系列.NET的相关概念,这主要包 ...

  2. elasticsearch-hadoop使用

    elasticsearch-hadoop是一个深度集成Hadoop和ElasticSearch的项目,也是ES官方来维护的一个子项目,通过实现Hadoop和ES之间的输入输出,可以在Hadoop里面对 ...

  3. HDU1796How many integers can you find(容斥原理)

    在计数时,必须注意无一重复,无一遗漏.为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计 ...

  4. (android实战)破解apk

    简单的总结几个关键步骤: 一.工具准备:apktool , dex2jar , jd-gui 二.使用dex2jar + jd-gui 得到apk的java源码 1.用解压工具从 apk包中取出 cl ...

  5. cocos-lua基础学习(10)scheduler类学习笔记

    local scheduler = cc.Director:getInstance():getScheduler() local function shouldNotCrash(dt) end loc ...

  6. JavaScript Ajax上传文件miniupload.js

    用到jquery和layer.js (function ($) { $.fn.miniupload = function (options, callback) { var jqDom = $(thi ...

  7. MySQL从删库到跑路(三)——SQL语言

    作者:天山老妖S 链接:http://blog.51cto.com/9291927 一.SQL语言简介 1.SQL语言简介 SQL是结构化查询语言(Structured Query Language) ...

  8. ng-深度学习-课程笔记-16: 自然语言处理与词嵌入(Week2)

    1 词汇表征(Word representation) 用one-hot表示单词的一个缺点就是它把每个词孤立起来,这使得算法对词语的相关性泛化不强. 可以使用词嵌入(word embedding)来解 ...

  9. 网关服务Spring Cloud Gateway(一)

    Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使 ...

  10. IBM究竟是一家怎样的公司

    每次被问到这样的“简单”问题,我都很纠结: 这家公司,从创始至今已经积累了几十万种技术(2015年蝉联专利排行榜23年之久,仅2015年专利数7355项),开发了上万种产品(从银行的交易系统,到航空的 ...