交换机转发实验(回顾哈希表实现)

哈希表的C语言实现

哈希函数为对哈希表长取余

哈希表算法实现

# include <stdio.h>
# include <stdlib.h>// 宏定义
//数据类型重定义
# define uint8_t unsigned char
# define uint16_t unsigned short
# define uint32_t unsigned long// 哈希表长度
# define HASH_TABLE_LEN 100

数据结构

哈希表由结构体数据和索引链表来表示,首先是索引和对应的指针,然后指针连成链表

// 链表结点
typedef struct _Link_Node
{uint16_t id;uint16_t data;struct _Link_Node *next;
}Link_Node,*Link_Node_Ptr;
// 链表结点由id,data和指向下一个链表元素的指针构成
// [id | data | ---]--> [id| data| --]--> [id | data | NULL]// 哈希表头
/*哈希表头略有不同,这里其实只需要一个指针,而哈希表头元素的具体位置可以由
数组的下标元素来指示*/
typedef struct _Hash_Header
{struct  _Link_Node *next;
}Hash_Header,*Hash_Header_ptr;

全局变量

// 一个完整的哈希表
Hash_Header_Ptr Hash_Table[HASH_TABLE_LEN];

这里定义了一个以哈希表头结构体为元素的数组,数组的长度为100,因为需要表示数组,所以类似于char* string 等价于 char string[]。

函数

  • 哈希表函数,用哈希函数生成id对应的哈希表中的位置
  • 函数功能: 输入 id, 返回 位置 pos
uint8_t hash_func(uint16_t id)
{uint8_t pos = 0;pos = id % HASH_TABLE_LEN;return pos;
}

至此,是可以用hash.h封装的一个哈希表的数据类型,具体对这个表的增、删、改、查需要注意。

初始化函数

初始化链表节点

Link_Node_Ptr init_link_node(void)
{Link_Node_ptr node;/* 这个函数是对哈希数组(我们把表头元素构成的数组称为哈希数组,把后面所缀的散列称为链表结点,现在这个函数的作用是初始化一个链表结点,包括id|data|指向下一个元素的指针Link_Node_ptr表示指向链表结点的指针*/// 申请结点,然后设置尾指针为NULLnode = (Link_Node_ptr)malloc(sizeof(Link_Node));// 初始化长度为 0node -> next = NULL;return node;
}

初始化哈希表头结点

Hash_Header_Ptr init_hash_header_node(void)
{Hash_Header_ptr node;// 申请结点node = (Hash_Header_Ptr)malloc(sizeof(Hash_Header));// 初始化长度为 0node ->next = NULL;return node;
}

哈希表初始化

void init_hash_table(void)
{uint8_t i = 0;for(i = 0; i < HASH_TABLE_LEN;i++){Hash_Table[i] = init_hash_header_node();/*这一句目前感觉不需要,属于重复说明,首先我们之前定义了一个全局变量,             HASH_TABLE,采用初始化哈希表头,为每一个数据元素(结构体)申请空间,就是为每一个数组元素申请空间,指针赋值为NULL,最后返回node所以上面两句可以理解为Hash_Table[i] = node;node ->next = NULL;第二句应该是不需要的*/Hash_Table[i]->next = NULL;}
}

初始化操作完成

  • 在哈希表增加结点
/*增加结点属于在哈希表的链表末尾进行数据增加,链表末尾需要判断是第一个直接与哈希表头连接的结点,还是已经存在其它的结点*/
void append_link_node(Link_Node_Ptr new_node)
{Link_Node_Ptr node;uint8_t pos = 0;// 新结点下一个指向为空new_node ->next = NULL;// 用哈希函数获取位置pos = hash_func(new_node->id);// 判断是否为直接与哈希表头相连if(Hash_Table[pos]->next == NULL){Hash_Table[pos]->next = new_node;}else{//需要找到该位置所对应的最后一个结点node = Hash_Table[pos]->next;// 进行链表的遍历while(node ->next != NULL){node = node->next;}// 插入node ->next = new_node;}
}

  • 在哈希表查询结点
/*在哈希表查询结点:
1.找到在哈希表某处的单链表中,并开始遍历
2.返回的是查询结点的前一个结点指针,这样便于删除操作
输入 pos : 哈希表数组元素下标,从0开始计数
id : 需要查询的结点id
root: 如果是第一个链表结点,则*root = 1, 否则为0
返回: 所需查询的结点的前一个结点指针,如果是第一个链表结点,则返回第一个链表结点
失败则返回0*/
Link_Node_Ptr search_link_node(uint8_t id,uint8_t *root)
{Link_Node_ptr node;uint8_t pos = 0;// 用哈希函数获得位置pos = hash_func(id);// 获取第一个链表结点node = Hash_Table[pos]->next;// 判断单链表是否存在if(node ==  NULL){return 0;}// 判断是否为第一个链表结点if(node ->id == id){*root = 1;return node;}else{// 不是第一个链表结点,遍历*root = 0;while(node ->next != NULL){if(node->next->id == id){return node;}else{node = node->next;}}return 0;}
}

  • 在哈希表删除结点
  • 删除的不是当前节点,而是输入节点的后一个节点
  • 输入: node:删除此节点后面的一个节点 new_node:新节点
void delete_link_node(Link_Node_Ptr node)
{Link_Node_Ptr delete_node;// 重定向需要删除的前一个结点delete_node = node -> next;node -> next = delete_node ->next;// 删除结点free(delete_node);
}
  • 删除第一个链表结点

    void delete_link_root_node(Link_Node_Ptr node)
    {uint8_t pos = 0;// 用哈希函数获得位置pos = hash_func(node->id);// 哈希表头清空if(node != NULL){Hash_Table[pos]->next = node ->next;//删除结点free(node);node = NULL;}
    }
    

    这里感觉写法有点问题,一般free以后不需要再赋值NULL

    // 试水: 删除第一个链表结点
    void delete_link_root_node(Link_Node_Ptr node)
    {uint8_t pos = 0;pos = hash_func(node->id);if(node != NULL){Hash_Table[pos]->next = node->next;free(node);}else{Hash_Table[pos]->next = NULL;}
    }
    

    计数

    // 获得哈希表中所有的结点数
    uint16_t get_node_num(void)
    {Link_Node_Ptr node;uint16_t i = 0;uint16_t num = 0;// 遍历for(i = 0 ; i < HASH_TABLE_LEN;i++){// 获取第一个链表结点node = Hash_Table[i]->next;while(node !=NULL){node = node->next;num++;}}return num;
    }
    

    • 从哈希表中获得对应节点的序号
    • 参数: index: 序号: 从1 开始,最大值为节点总数值
    • root: 如果是根节点,则*root = 1,否则为 0
    • 返回: 所需查询的节点的前一个结点指针,如果是第一个链表节点,则返回第一个链表节点
    // 查找第index个结点,并返回指向该结点的指针
    Link_Node_Ptr get_node_from_index(uint16_t index,uint8_t *root)
    {Link_Node_Ptr node;uint16_t i = 0;uint16_t num = 0;// 遍历for( i = 0; i < HASH_TABLE_LEN; i++){// 获取第一个链表节点node = Hash_Table[i]->next;// 判断单链表是否存在if(node == NULL){continue;}// 表示对于 Hash_Table中的[下标| NULL]后面的代码不再执行// 执行i++操作,直到找到第一个单链表存在,使得num++num++;/* 这个时候我们逃脱了第一个if判断,这个哈希表的代码实现不喜欢使用else,而是尽量用if,contiune执行操作,上面我们排除了所有表头next成员为NULL的部分,得到第一个非空的表头对应的链表,并且已经执行了num++的操作,下面需要考虑的是存在单链表的分两种情况判断:单链表是第一个直接和表头相连的结点(或者说根结点)还是普通的结点*/if(num == index){*root = 1;return node;}/*遇到根结点,恰好下标符合,设置root并返回根结点否则继续进行判断*/while(node->next!=NULL){num++;/*这里先行加了一个,所以最后实际上num应该是 index-1 这样提前进行 num++ 的好处在于,返回第index - 1 的结点的指针在最后删除操作的时候得到便利*/if(num == index){*root = 0;return node;}node = node ->next;} }return 0;
    }
    

    全删

    • 删除hash表中所有的结点,,但是仍保留单链表结构
    void drop_hash()
    {// 类似list_for_each_entry,删除时设置两个指针,指向当前和当前的后一个Link_Node_Ptr node;uint16_t i = 0;Link_Node_Ptr node_next;// 遍历for(i = 0; i < HASH_TABLE_LEN;i++){//获取单链表node = Hash_Table[i]->next;while(1){// 判断是否存在单链表if(node == NULL){// 不存在,这一步我依然认为没必要Hash_Table[i]->next = NULL;break;}// 下一个结点,或者说第二个...普通结点node_next = node->next;free(node);node = node_next;/*进行一个迭代,首先node = Hash_Table[i]->next指向下一个结点然后根据node_next = node ->next获取后面的结点信息,得到结点信息之后,就可以free(node)并且对后一个结点node_next重新开始下一轮判断*/}}
    }
    

    打印

    // 输出所有结点
    void printf_hash()
    {Link_Node_Ptr node;uint8_t root = 0;uint8_t i = 0;uint8_t num = 0;printf("------------------打印hash表-----------------\n")num = get_node_num();for(i = 1; i <= num;i++){    /*因为get_node_from_index取的实际是第 i-1 个结点,所以从 1 开始先传入 0,如果需要改变,则为1*/node = get_node_from_index(i,&root);if(node != 0){// 拿到了返回的对应结点if(root){printf("根节点:节点号%d,id为%d\n",i,node->id);}else{printf("普通节点:节点号%d,id为%d\n",i,node->id);}}}
    }
    

    主函数

    • 实现对哈希表的新建、建立节点、查询及增加、删除节点的操作

      int main()
      {Link_Node_Ptr node;uint8_t temp = 0;uint8_t root = 0;uint8_t i = 0;init_hash_table();//初始化,全局变量Hash_Table//插入数据 id = 1,data = 2node = init_link_node();node-> id = 1;node -> data = 2;append_link_node(node);// 查询节点数printf("1.节点数为%d\n",get_node_num());//插入数据 id = 1002,data = 1001node = init_link_node();node ->id =1002;node -> data = 1001;append_link_node(node);/*其实init_link_node的操作可以直接写在main函数里,这里反复调用的一个好处是,可以直接复用node变量名*/node = init_link_node();node->id = 10000;node ->data = 10001;append_link_node(node);node = init_link_node();node->id = 1000;node ->data = 10001;append_link_node(node);node = init_link_node();node ->id = 2;node ->data = 10001;append_link_node(node);// 查询节点数printf("2.节点数为%d\n",get_node_num());// 查询 id = 1000 并删除node = search_link_node(1000,&temp);if(node! = 0){if(temp == 0){printf("删除普通节点:所查询id的值为%d,数据为%d\n",node->next->id,node->next->data);// 删除delete_link_node(node);}else{// 是根结点printf("删除根节点:所查询id的值为%d,数据为%d\n",node->id,node->data);delete_link_root_node(node);}}else{printf("查询失败!\n");}// 纯查询操作,id = 1001node = search_link_node(1001,&temp);if(node!=0){if(temp == 0){printf("所需查询id的值为%d\n",node->next->data);}else{printf("所需查询id的值为%d\n",node->data);}}else{printf("查询失败\n");}// 查询节点数printf("节点数为%d\n",get_node_num());printf_hash();getchar();return 0;
      }
      

交换机转发实验(回顾哈希表实现)相关推荐

  1. C++利用双哈希表实现存储机制hash table的算法(附完整源码)

    C++利用双哈希表实现存储机制的算法 C++利用双哈希表实现存储机制的算法完整源码(定义,实现,main函数测试) C++利用双哈希表实现存储机制的算法完整源码(定义,实现,main函数测试) #in ...

  2. c语言哈希表电子辞典_C语言实现的哈希表实现程序

    下面我们一起来看一个C语言实现的哈希表实现程序了,这个程序没有过多的说明只是一个例子,大家有兴趣可以进入看看或测试一下. 为了巩固一下链表知识,自己实现的一个哈希表,在GCC 4.4.7下编译通过: ...

  3. 哈希表实现电话号码查询系统(c++)

    问题描述:设计哈希表实现电话号码查询系统,实现下列功能: (1) 假定每个记录有下列数据项:电话号码.用户名.地址. (2) 一是从数据文件old.txt(自己现行建好)中读入各项记录,二是由系统随机 ...

  4. 利用Mininet环境-交换机转发实验整个过程

    目录 1.写在前面 2.安装工作 2.1.mininet安装 2.2 cmake安装 2.3 xterm安装 2.4 wireshark安装 3.作业要求:(交换机转发实验) ​ 4.C语言完成函数编 ...

  5. 哈希表实现通讯录的设计与实现

    [实验目的] 为某个单位建立一个员工通讯录管理系统,可以方便查询每一个员工的电话与地址.设计哈希表存储,设计并实现通讯录查找系统. [实验要求] (1)每个员工记录有下列数据项:电话号码.用户名.地址 ...

  6. C语言项目 电话查询系统 哈希表实现(项目要求 + 运行界面 + 代码分析 + 完整代码)

    电话查询系统 1. 项目要求 2. 数据样例 3. 运行界面 4. 代码分析 5. 完整代码 6. 项目报告 关注博主不迷路,博主带你码代码! 1. 项目要求 设每个记录有以下数据项:用户名.电话.地 ...

  7. 巧用哈希表实现随机操作

    题目描述 这是 LeetCode 上的 380. O(1) 时间插入.删除和获取随机元素 ,难度为 中等. Tag : 「数据结构」.「哈希表」 实现 RandomizedSet 类: Randomi ...

  8. 设计散列表实现通讯录查找系统_[源码和文档分享]利用哈希表实现电话号码查询系统...

    第一章 需求分析 1.1 问题描述 设计一个电话号码查询系统,为来访的客⼈提供各种信息查询服务. 1.2 基本要求 设计每个记录有下列数据项:电话号码.用户名.地址 从键盘输入个记录,分别以电话号码和 ...

  9. 《交换机转发原理的回顾》—那些你应该知道的知识(十一)

    写在前面: 近期被一道有点奇怪的题目考住了,考点其实就是最基础的交换机的转发原理.在此复习.整理一下. 在此,我们不讲这个题目提出的其他条件,单独讲述PC1能够ping通PC2的这一过程. 交换机SW ...

最新文章

  1. 手把手教你实现GAN半监督学习
  2. App icon和启动图的尺寸需求(没有iPad)
  3. java从入门到精髓 - 反射Constructor
  4. python基础知识选择题-python基础知识练习题(二)
  5. vue-resource全攻略
  6. Redis如何实现刷抖音不重复-布隆过滤器(Bloom Filter)
  7. 【To Read】LeetCode | Jump Game II(转载)
  8. 学会5个数据分析常见定律,数据敏感度提升N个度
  9. Swift实现LRU缓存淘汰算法
  10. pr生成html文件格式,pr支持哪些格式
  11. secsetupwizard以停止,三星手机恢复出厂设置之后,出现SecSetupWizard已停止的错误...
  12. 原生JavaScript + Canvas实现图片局部放大器
  13. php 股票数据 sina,使用Sina API获取新浪财经的证券股票数据接口(时价 K线等)
  14. 【vue】微信sdk中接口和标签本地调试
  15. 文本智能处理与RPA领军企业达观数据宣布完成5.8亿元C轮融资
  16. jvm jni 及 pvm pybind11 大批量数据传输及优化
  17. Linux下编写C语言
  18. 关于Win10。。。我是来吃螃蟹的。。。
  19. 一文搞懂“正态分布”所有重要知识点
  20. oracle新建一个自增列,sequence的使用

热门文章

  1. linux鼠标灵敏度参数,linux怎么设置鼠标灵敏度? linux设置鼠标灵敏度的方法
  2. java如何开发生产派工报工_派工工序报工
  3. chrome automation extension崩溃(selenium +chromedriver)
  4. JeeSite 4.x 消息管理、消息推送、消息提醒
  5. HTML5 画布 火柴人实例及代码
  6. C语言中Scanf语句的注意事项
  7. 使用AlarmManager实现定时提醒,解决锁屏后无法正常唤醒CPU和屏幕的问题
  8. 【技术实战】华为手机学生模式下的微信数据提取
  9. Python实现录屏
  10. OPPO R11网络口碑扑街,OPPO 新品恐遭滑铁卢!