数据结构-----哈希表和内核链表

哈希表(Hash Table)


一、哈希表基本概念

1. 核心定义
  • 哈希函数:将关键字映射到存储位置的函数
  • 哈希冲突:不同关键字映射到相同位置的现象
  • 装载因子:α = 元素数量 / 表长度(衡量空间利用率)
2. 重要特性
特性 说明
平均时间复杂度 查找/插入/删除:O(1)
最坏时间复杂度 所有操作退化到O(n)
空间复杂度 O(n)

二、哈希函数设计原则

1. 优质哈希函数特征
  1. 计算简单
  2. 散列均匀
  3. 冲突概率低
2. 常见哈希函数类型
类型 公式 适用场景
除留余数法 h(k) = k % p(p为质数) 整数关键字
平方取中法 取k²中间几位数 不知分布的关键字
折叠法 分割相加后取模 较长数字关键字

三、完整实现示例

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef int DATATYPE;


typedef struct
{
    DATATYPE *head;
    int tlen;
} HSTable;


HSTable *HSInit(int len)
{
    HSTable *hs = malloc(sizeof(HSTable));
    if (NULL == hs)
    {
        perror("HSInit malloc error");
        return NULL;
    }


    hs->head = malloc(sizeof(HSTable) * len);
    if (NULL == hs->head)
    {
        perror("HSInit malloc2 error");
        return NULL;
    }
    hs->tlen = len;
    memset(hs->head, -1, sizeof(HSTable) * len);
    return hs;
}


int HSFun(HSTable *hs, DATATYPE *data)
{
    return *data % hs->tlen;
}


int HSTableInsert(HSTable *hs, DATATYPE *data)
{
    int inx = HSFun(hs, data);
    while (hs->head[inx] != -1)
    {
        printf("data %d confilt pos :%d\n", *data, inx);
        inx = (inx + 1) % hs->tlen;
    }
    memcpy(&hs->head[inx], data, sizeof(DATATYPE));
    return 0;
}


int HSTableSearch(HSTable *hs, DATATYPE *data)
{
    int inx = HSFun(hs, data);
    int oldinx = inx;
    while (hs->head[inx] != *data)
    {
        inx = (inx + 1) % hs->tlen;
        if (oldinx == inx)
        {
            return -1;
        }
    }
    return inx;
}


int main(int argc, char const *argv[])
{
    int array[] = {12, 34, 56, 78, 91, 23, 45, 67, 89, 10};
    HSTable *hs = HSInit(10);


    for (int i = 0; i < 10; i++)
    {
        HSTableInsert(hs, &array[i]);
    }


    DATATYPE data = 34;
    int idx = HSTableSearch(hs, &data);
    if (-1 == idx)
    {
        printf("can't find\n");
    }
    else
    {
        printf("find %d\n", hs->head[idx]);
    }


    return 0;
}

内核风格链表(klist)


一、链表核心结构

1. 节点定义
复制代码
typedef struct __klist {
    struct __klist *next;
    struct __klist *prev;
} KLIST;
2. 数据容器
复制代码
typedef struct {
    int id;
    char name[40];
    KLIST node;  // 嵌入链表节点
} PER;

二、核心操作实现

1. 链表初始化
复制代码
void klist_init(KLIST* head) {
    head->prev = head;
    head->next = head;
}

示意图

复制代码
       head
┌─prev ────┐
│          │
└─next ────┘
2. 通用插入操作
复制代码
void klist_add(KLIST* newnode, KLIST* prev, KLIST* next) {
    newnode->next = next;
    newnode->prev = prev;
    prev->next = newnode;
    next->prev = newnode;
}

// 头部插入
void klist_add_head(KLIST* head, KLIST* newnode) {
    klist_add(newnode, head, head->next);
}

// 尾部插入
void klist_add_tail(KLIST* head, KLIST* newnode) {
    klist_add(newnode, head->prev, head);
}

三、关键宏解析

1. 容器获取宏
复制代码
#define offset(type, mem) ((size_t)&((type*)0)->mem)
#define containerof(ptr, type, mem) ({ \
    const typeof(((type*)0)->mem)* _mptr = (ptr); \
    (type*)((char*)_mptr - offset(type, mem)); })

原理图解

复制代码
PER结构体内存布局
+---------------+ +---------------+
| id (4字节)    | | name (40字节) |
+---------------+ +---------------+
| node.next     | | node.prev     |
+---------------+ +---------------+
2. 安全遍历宏
复制代码
#define klist_for_each(p, n, head, mem) \
    for(p = containerof((head)->next, typeof(*p), mem), \
        n = containerof(p->mem.next, typeof(*p), mem); \
        &p->mem != (head); \
        p = n, n = containerof(n->mem.next, typeof(*n), mem))

四、应用层接口

1. 添加人员信息
复制代码
int add_per(int id, char* name, KLIST* head) {
    PER* per = malloc(sizeof(PER));
    if (!per) return 1;
    
    per->id = id;
    strcpy(per->name, name);
    klist_add_tail(head, &per->node);
    return 0;
}
2. 显示所有人员
复制代码
int show_per(KLIST* head) {
    PER *p, *n;
    klist_for_each(p, n, head, node) {
        printf("ID: %-5d Name: %s\n", p->id, p->name);
    }
    return 0;
}
3. 删除指定人员
复制代码
int del_per(KLIST* head, int id) {
    PER *p, *n;
    klist_for_each(p, n, head, node) {
        if (p->id == id) {
            klist_del(p->node.prev, p->node.next);
            free(p);
            break;  // 删除后立即退出
        }
    }
    return 0;
}

五、性能对比

操作 时间复杂度 特点
插入头部 O(1) 直接修改头节点指针
插入尾部 O(1) 通过tail指针直接访问
随机访问 O(n) 需要遍历链表
删除节点 O(1) 已知节点位置时直接操作指针

六、containerOfoffsetof 宏补充


一、核心概念解析

1. offsetof
复制代码
#include <stddef.h>
#define offsetof(type, member) ((size_t)&(((type *)0)->member))
  • 功能:计算结构体成员相对于结构体首地址的字节偏移量
  • 原理
    通过将结构体指针强制类型转换为 0 地址指针,获取成员的"虚拟地址",其数值即为偏移量
2. containerOf
复制代码
#define containerOf(ptr, type, member) \
    ((type *)((char *)(ptr) - offsetof(type, member)))
  • 功能:通过成员指针反推外层结构体指针
  • 原理
    成员指针地址减去该成员的偏移量,得到结构体起始地址

二、内存布局图示

1. 结构体内存模型
复制代码
+-------------------+ <-- 结构体起始地址
| member1 (4字节)   |
+-------------------+
| member2 (1字节)   |
+-------------------+
| member3 (8字节)   |
+-------------------+
2. containerOf 工作流程
复制代码
假设:
- 结构体类型为 MyStruct
- 成员指针 ptr 指向 member3
- member3 偏移量为 5 字节

ptr 地址:0x1005
结构体地址 = 0x1005 - 5 = 0x1000

三、完整代码示例

1. 数据结构定义
复制代码
#include <stdio.h>
#include <stddef.h>

typedef struct list_head {
    struct list_head *prev, *next;
} LIST_HEAD;

typedef struct task_struct {
    int pid;
    char state;
    LIST_HEAD tasks;  // 嵌入链表节点
} PROCESS;
2. 宏实现
复制代码
#define container_of(ptr, type, member) ({ \
    const typeof(((type *)0)->member) *__mptr = (ptr); \
    (type *)((char *)__mptr - offsetof(type, member)); })

#define list_entry(ptr) container_of(ptr, PROCESS, tasks)
3. 使用示例
复制代码
void process_task(LIST_HEAD *task_node) {
    // 通过链表节点获取进程结构体
    PROCESS *proc = list_entry(task_node);
    
    printf("PID: %d, State: %c\n", proc->pid, proc->state);
}

int main() {
    PROCESS p = {.pid = 1001, .state = 'R'};
    process_task(&p.tasks);  // 输出:PID: 1001, State: R
    return 0;
}

四、关键特性对比

特性 offsetof containerOf
输入参数 结构体类型 + 成员名 成员指针 + 结构体类型 + 成员名
输出结果 字节偏移量(size_t) 外层结构体指针
计算时机 编译时 运行时
标准支持 C89 标准 GNU 扩展

五、应用场景分析

1. Linux 内核链表
复制代码
// 内核源码示例(linux/list.h)
struct list_head {
    struct list_head *next, *prev;
};

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)
2. 设备驱动开发
复制代码
typedef struct usb_device {
    int vendor_id;
    struct list_head device_list;  // 设备链表节点
} USB_DEV;

void scan_devices(struct list_head *dev_node) {
    USB_DEV *dev = container_of(dev_node, USB_DEV, device_list);
    // 访问设备详细信息
}
相关推荐
Aurora_wmroy19 分钟前
算法竞赛备赛——【数据结构】栈&单调栈
数据结构·c++·算法·蓝桥杯
空雲.1 小时前
ABC 369
数据结构·c++·算法
Brookty2 小时前
逆波兰表达式
数据结构·学习
苏言の狗2 小时前
小R的排列挑战
数据结构·算法
司六米希2 小时前
【算法】十大排序算法(含时间复杂度、核心思想)
数据结构·算法·排序算法
T.Ree.3 小时前
【数据结构】_单链表_相关面试题(一)
c语言·开发语言·数据结构
逆鱼_044 小时前
Linux-数据结构-哈夫曼树-哈希表-内核链表
数据结构·链表·散列表
DreamBoy@4 小时前
【408--复习笔记】数据结构
数据结构
Dust-Chasing4 小时前
数据结构之队列
数据结构·算法·链表
梅见十柒5 小时前
计算机程序的构造与解释笔记
数据结构·经验分享·笔记