散列表(又名哈希表)仅仅需要一个包含单一指针的链表头。它是双向链表的变体。它不同于双链表——表头和结点使用相同的结构体——散列表对表头和结点有不同的定义。如下:

struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};

散列表的实现一般采用hlist_head数组,每个hlist_head挂一个双向hlist_node链表,大致如下图。其中pprev它指向前一个结点的next指针。

1、初始化

1.1、初始化头

#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)

1.2、初始化结点

static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}

2、逻辑判断

static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}

3、删除结点

3.1、内部API

static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;//(1)
struct hlist_node **pprev = n->pprev;//(2)
*pprev = next;//(3)
if (next)
next->pprev = pprev;//(4)
}

3.2、外部API

static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);//(1)
n->next = LIST_POISON1;//(2)
n->pprev = LIST_POISON2;//(3)
}

static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);//(1)
INIT_HLIST_NODE(n);//(2)
}
}

4、添加结点

4.1、表头添加结点

static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)(0)
{
struct hlist_node *first = h->first;//(1)
n->next = first;//(2)
if (first)
first->pprev = &n->next;//(3)
h->first = n;//(4)
n->pprev = &h->first;//(5)
}

在此基础上再次插入一个结点

4.2、指定结点之前添加结点

/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)//(0)
{
n->pprev = next->pprev;//(1)
n->next = next;//(2)
next->pprev = &n->next;//(3)
*(n->pprev) = n;//(4)
}

4.3、指定结点之后添加结点

static inline void hlist_add_after(struct hlist_node *n,
struct hlist_node *next)//(0)
{
next->next = n->next;//(1)
n->next = next;//(2)
next->pprev = &n->next;//(3) if(next->next)
next->next->pprev = &next->next;//(4)
}

/* after that we'll appear to be on some hlist and hlist_del will work */
static inline void hlist_add_fake(struct hlist_node *n)
{
n->pprev = &n->next;
}

5、移动散列表

/*
* Move a list from one list head to another. Fixup the pprev
* reference of the first entry if it exists.
*/
static inline void hlist_move_list(struct hlist_head *old,
struct hlist_head *new)
{
new->first = old->first;//(1)
if (new->first)
new->first->pprev = &new->first;//(2)
old->first = NULL;//(3)
}

Linux散列表(一)——操作函数的更多相关文章

  1. Linux散列表(二)——宏

    散列表宏承接了双向链表宏的风范,好使好用!务必区分“结点”和“元素”!双链表宏博文中已经提及,这里不赘述! 1.获取元素(结构体)基址 #define hlist_entry(ptr, type, m ...

  2. python字符串 列表 元组 字典相关操作函数总结

    1.字符串操作函数 find 在字符串中查找子串,找到首次出现的位置,返回下标,找不到返回-1 rfind 从右边查找 join 连接字符串数组 replace 用指定内容替换指定内容,可以指定次数 ...

  3. VC散列表

    vc下有2个版本的散列表类,hash_map和unordered_map,hash_map位于stdext命名空间,unordered_map在std命名空间(vs2008及其之后的版本可用),官方推 ...

  4. 散列表Java实现

    package 散列表; import java.util.Scanner; public class HashSearch { public static int data[] = {69,65,9 ...

  5. 10-10Linux的文件操作函数以及所需头文件

    Linux的基本文件操作函数     Linux通过相应的对文件的IO函数来实现对文件的操作,这些函数通常被称作"不带缓冲的IO",这是因为他们都是通过调用Linux的内核调用来实 ...

  6. linux内核的双链表list_head、散列表hlist_head

    一.双链表list_head 1.基本概念 linux内核提供的标准链表可用于将任何类型的数据结构彼此链接起来. 不是数据内嵌到链表中,而是把链表内嵌到数据对象中. 即:加入链表的数据结构必须包含一个 ...

  7. python对redis的常用操作 上 (对列表、字符串、散列结构操作)

    这里的一切讨论均基于python的redis-py库. 安装使用: pip install redis 然后去获取一个redis客户端: redis_conn = redis.Redis(host=R ...

  8. Python 散列表查询_进入<哈希函数>为结界的世界

    1. 前言 哈希表或称为散列表,是一种常见的.使用频率非常高的数据存储方案. 哈希表属于抽象数据结构,需要开发者按哈希表数据结构的存储要求进行 API 定制,对于大部分高级语言而言,都会提供已经实现好 ...

  9. [Linux] Linux进程PID散列表

    linux系统中每个进程由一个进程id标识,在内核中对应一个task_struct结构的进程描述符,系统中所有进程的task_struct通过链表链接在一起,在内核中,经常需要通过进程id来获取进程描 ...

随机推荐

  1. javascript基础学习(四)

    javascript之流程控制语句 学习要点: 表达式语句含义 选择语句:if.if...else.switch 循环语句:while.do...while.for.for...in 跳转语句:bre ...

  2. 一种实现C++反射功能的想法(三)

    如何实现类型名跟类型的对应, 我们很容易想到map, 没错, 就是使用map实现的. std::map<std::string, .....>, 等下, 第二部分该填什么类型, 一个函数指 ...

  3. nuc970连接jlink进行单步调试的设置

    在 USB mode 下, 先跟 NuWriter 接上, 然后用以下的设定. 按 Keil 的 debug (不是 download to flash)就可以接上了.

  4. Android 中文 API (29) —— CompoundButton

    前言 本章内容是android.widget.CompoundButton,翻译来自德罗德,再次感谢德罗德 !期待你一起参与Android API 的中文翻译,联系我over140@gmail.com ...

  5. linux 消息队列的限制

    消息队列的系统限制 作者:冯老师,华清远见嵌入式学院讲师. 消息队列是System V的IPC对象的一种,用于进程间通信,会受到系统的限制,本文主要描述了三个限制.第一:议个消息的最大长度:第二:消息 ...

  6. 关于Unity3D中的版本管理 .

    关于Unity3D中的版本管理 使用Unity3D也有一段时间了,由于团队一直使用SVN进行版本管理,现总结一下: (1) Unity3D的二进制资源必须加锁进行版本控制,因为它没办法merge: ( ...

  7. cf C. Fox and Box Accumulation

    题意:输入一个n,然后输入n个数,问你可以划分多少个序列,序列为:其中一个数为c,在它的前面最多可以有c个数. 思路:先排序,然后对于每一个数逐步的找没有被用过的数,且这个数可以符合条件,然后如果没有 ...

  8. JavaScript+CSS实现经典的树形导航栏

    在一些管理系统里面,一般右侧都会有树形的导航栏,点击一下就会出现下拉菜单,显示出来该父菜单下面的子菜单 项目,然后配以图片,和CSS的效果,可以说是非常常用的功能,现在做一个项目,正好用到这个功能,于 ...

  9. C参数计算

    C‘计算参数时,从右向左压栈的 int a[]={1,3,4}; int * ptr=a; pirntf("%d,%d\n",*ptr,*(++ptr)); 输出都是3:

  10. Linux企业级开发技术(2)——epoll企业级开发之epoll接口

    epoll的接口非常简单,总共只有三个函数: 1.int epoll_create(intsize); 生成一个 Epoll 专用的文件描述符,size用来告诉内核这个监听的数目一共有多大.这个参数不 ...