数据结构基础:哈希表、排序和查找算法

目录

一、哈希表

1.哈希算法

2.哈希碰撞

3.哈希表

4.哈希表相关操作

哈希表插入

哈希表遍历

元素查找

哈希表销毁

二、排序算法

1. 排序算法对比

2. 排序算法实现

冒泡排序

选择排序

插入排序

希尔排序

快速排序

三、查找算法

1. 查找算法对比

2. 查找算法实现

顺序查找

折半查找(二分查找)

一、哈希表

1.哈希算法

  1. 将数据通过哈希算法映射成一个键值,存取都在同一个位置实现数据的高效存储和查找,将时间复杂度尽可能降低至O(1)
  2. 哈希算法是不固定的,需要选择一个合适的哈希算法,才能映射成一个键值

2.哈希碰撞

多个数据通过哈希算法得到的键值相同,称为产生哈希碰撞

3.哈希表

  • 构建一个哈希表,存放0~100之间的数据
  • 哈希算法的选择:

将0~100之间的数据的个位作为键值 

哈希表通过哈希函数实现高效存取,需解决哈希碰撞

4.哈希表相关操作

哈希表插入
#define INDEX 10  // 假设哈希表大小为10(0-9)
linknode *phashtable[INDEX] = {NULL};  // 哈希表数组/* 哈希表插入函数 */
int insert_hashtable(int tmpdata) {int key = 0;linknode **pptmpnode = NULL;  // 二级指针用于修改链表节点linknode *pnewnode = NULL;// 计算哈希键值(取数据的个位)key = tmpdata % INDEX;// 找到插入位置(保持链表有序)for (pptmpnode = &phashtable[key]; *pptmpnode != NULL && (*pptmpnode)->data < tmpdata; pptmpnode = &(*pptmpnode)->pnext) {// 循环移动指针至合适位置}// 申请新节点pnewnode = malloc(sizeof(linknode));if (NULL == pnewnode) {perror("fail to malloc");return -1;}// 插入新节点pnewnode->data = tmpdata;pnewnode->pnext = *pptmpnode;*pptmpnode = pnewnode;return 0;
}
哈希表遍历
/* 遍历哈希表所有元素 */
int show_hashtable(void) {int i = 0;linknode *ptmpnode = NULL;for (i = 0; i < INDEX; i++) {printf("%d:", i);  // 打印键值ptmpnode = phashtable[i];while (ptmpnode != NULL) {printf("%2d ", ptmpnode->data);  // 打印链表元素ptmpnode = ptmpnode->pnext;}printf("\n");}return 0;
}
元素查找
/* 查找哈希表中的元素 */
linknode *find_hashtable(int tmpdata) {int key = 0;linknode *ptmpnode = NULL;key = tmpdata % INDEX;  // 计算键值ptmpnode = phashtable[key];// 遍历对应链表查找元素while (ptmpnode != NULL && ptmpnode->data <= tmpdata) {if (ptmpnode->data == tmpdata) {return ptmpnode;  // 找到返回节点地址}ptmpnode = ptmpnode->pnext;}return NULL;  // 未找到返回NULL
}
哈希表销毁
/* 销毁哈希表,释放所有节点 */
int destroy_hashtable(void) {int i = 0;linknode *ptmpnode = NULL;linknode *pfreenode = NULL;for (i = 0; i < INDEX; i++) {ptmpnode = phashtable[i];pfreenode = phashtable[i];while (ptmpnode != NULL) {ptmpnode = ptmpnode->pnext;free(pfreenode);  // 释放当前节点pfreenode = ptmpnode;}phashtable[i] = NULL;  // 置空数组元素}return 0;
}

二、排序算法

排序算法中,冒泡、选择、插入排序适用于小规模数据;希尔、快速排序适用于大规模数据,其中快速排序性能最优(平均 O (nlogn))

1. 排序算法对比

算法时间复杂度稳定性核心思想
冒泡排序O(n²)稳定相邻元素比较,大元素后移,重复 n-1 次
选择排序O(n²)不稳定每次从剩余元素中找最小值,与当前位置交换
插入排序O(n²)稳定将元素插入到已排序序列的合适位置,类似整理扑克牌
希尔排序O(n^1.3)不稳定按步长分割数组为子序列,分别插入排序,逐步减小步长至 1
快速排序O(nlogn)不稳定选基准值,将数组分为小于和大于基准的两部分,递归排序两部分

2. 排序算法实现

冒泡排序

核心思想是通过相邻元素的比较和交换,使较大的元素逐步 “浮” 到数组的末尾。具体来说,相邻的两个元素比较,大的向后走,小的向前走,循环找出数组中 len-1 个较大的值,最终实现整个数组的有序排列

/* 冒泡排序:相邻元素比较交换,大元素后移 */
int bubble_sort(int *parray, int len) {int i = 0;int j = 0;int tmp = 0;for (j = 0; j < len - 1; j++) {  // 控制轮次for (i = 0; i < len - 1 - j; i++) {  // 每轮比较次数递减if (parray[i] > parray[i + 1]) {// 交换元素tmp = parray[i];parray[i] = parray[i + 1];parray[i + 1] = tmp;}}}return 0;
}
选择排序

核心思想是从前到后在未排序元素中寻找最小值,然后将找到的最小值与未排序部分的前面元素进行交换。通过找到 len-1 个最小值,最后剩下的一个元素自然就是最大值,从而完成排序

/* 选择排序:找最小值并交换到当前位置 */
int select_sort(int *parray, int len) {int i = 0;int j = 0;int tmp = 0;int min = 0;  // 最小值索引for (j = 0; j < len - 1; j++) {min = j;  // 假设当前位置为最小值for (i = j + 1; i < len; i++) {if (parray[i] < parray[min]) {min = i;  // 更新最小值索引}}// 交换最小值到当前位置if (min != j) {tmp = parray[min];parray[min] = parray[j];parray[j] = tmp;}}return 0;
}
插入排序

核心思想是将数组中的每个元素逐个插入到已排序的数列中。首先取出要插入的元素,然后依次和前面的元素比较,比该元素大的元素向后移动,直到遇到前一个元素比要插入的元素小,或者到达有序数列的开头时停止,最后将该元素插入到合适位置

/* 插入排序:将元素插入已排序序列的合适位置 */
int insert_sort(int *parray, int len) {int tmp = 0;  // 待插入元素int i = 0;int j = 0;for (j = 1; j < len; j++) {tmp = parray[j];  // 取出待插入元素// 找到插入位置for (i = j; i > 0 && tmp < parray[i - 1]; i--) {parray[i] = parray[i - 1];  // 元素后移}parray[i] = tmp;  // 插入元素}return 0;
}
希尔排序

核心思想是通过选择不同的步长,将数组拆分成若干个小的子数组,对每个子数组进行插入排序。当若干个子数组成为有序数列后,数组中的数据会大致有序,最后再对整体进行一次插入排序,以完成整个数组的排序

步长为5

步长为2

步长为1

/* 希尔排序:按步长分割子序列,逐步缩小步长 */
int shell_sort(int *parray, int len) {int step = 0;  // 步长int j = 0;int i = 0;int tmp = 0;// 步长初始为len/2,每次减半至0for (step = len / 2; step > 0; step /= 2) {// 对每个子序列进行插入排序for (j = step; j < len; j++) {tmp = parray[j];for (i = j; i >= step && tmp < parray[i - step]; i -= step) {parray[i] = parray[i - step];  // 元素后移}parray[i] = tmp;  // 插入元素}}return 0;
}
快速排序

核心思想是选择数组左边的元素作为键值,从数组后面找一个比键值小的元素放到前面,再从前面找一个比键值大的元素放到后面,最后将键值放到中间位置。如果左右两边还有元素,则递归调用快速排序对两边元素进行排序

/* 快速排序:分治思想,以基准值分割数组 */
int quick_sort(int *parray, int low, int high) {int key = 0;  // 基准值int i = low;  // 左指针int j = high; // 右指针if (low >= high) {return 0;  // 递归终止条件}key = parray[low];  // 选最左元素为基准值while (i < j) {// 从右向左找小于基准值的元素while (i < j && parray[j] >= key) {j--;}if (i < j) {parray[i] = parray[j];  // 移至左指针位置}// 从左向右找大于基准值的元素while (i < j && parray[i] <= key) {i++;}if (i < j) {parray[j] = parray[i];  // 移至右指针位置}}parray[i] = key;  // 基准值归位// 递归排序左半部分if (i - 1 > low) {quick_sort(parray, low, i - 1);}// 递归排序右半部分if (i + 1 < high) {quick_sort(parray, i + 1, high);}return 0;
}

三、查找算法

查找算法中,折半查找效率远高于顺序查找,但依赖有序数据

1. 查找算法对比

算法时间复杂度适用场景核心思想
顺序查找O(n)无序或小规模数据从前往后依次遍历,逐个比较

折半查找

(二分查找)

O(logn)有序数组每次取中间元素比较,缩小查找范围至左半或右半部分

2. 查找算法实现

顺序查找

从数组的起始位置开始,逐个将元素与要查找的目标值进行比较,若找到与目标值相等的元素,则查找成功;若遍历完整个数组都没有找到,则查找失败

/* 顺序查找:遍历数组逐个比较 */
int seq_search(int *parray, int len, int target) {int i = 0;for (i = 0; i < len; i++) {if (parray[i] == target) {return i;  // 找到返回索引}}return -1;  // 未找到返回-1
}
折半查找(二分查找)

针对有序数组,先计算中间位置,将中间位置的元素与目标值比较。如果目标值等于中间元素,则查找成功;如果目标值大于中间元素,则在数组的右半部分继续查找;如果目标值小于中间元素,则在数组的左半部分继续查找,重复此过程直至找到目标值或确定查找范围为空

/* 折半查找:仅适用于有序数组 */
int binary_search(int *parray, int low, int high, int target) {int mid = 0;if (low > high) {return -1;  // 查找失败}mid = (low + high) / 2;  // 计算中间索引if (parray[mid] == target) {return mid;  // 找到返回索引} else if (target > parray[mid]) {// 目标在右半部分,递归查找return binary_search(parray, mid + 1, high, target);} else {// 目标在左半部分,递归查找return binary_search(parray, low, mid - 1, target);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/92535.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/92535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux内核参数调优:为K8s节点优化网络性能

在高并发微服务环境中&#xff0c;网络性能往往成为K8s集群的瓶颈。本文将深入探讨如何通过精细化的Linux内核参数调优&#xff0c;让你的K8s节点网络性能提升30%以上。引言&#xff1a;为什么网络调优如此重要&#xff1f;作为一名在生产环境中维护过数千节点K8s集群的运维工程…

全家桶” 战略如何重塑智能服务标准?无忧秘书 AI + 智脑 + 数字人协同模式的底层架构解析

在数字化浪潮的推动下&#xff0c;企业对智能化服务的需求日益增长。然而&#xff0c;单一的技术或产品往往难以满足复杂场景下的多样化需求。近年来&#xff0c;“全家桶”战略成为科技行业的一大趋势&#xff0c;通过整合多维度技术与服务&#xff0c;为企业提供全方位的支持…

前端后端之争?JavaScript和Java的特性与应用场景解析

一、名字相似&#xff0c;本质迥异 1.1 历史渊源与命名背景 在编程世界中&#xff0c;很少有两种语言像JavaScript和Java这样&#xff0c;仅仅因为名字的相似性就引发了无数初学者的困惑。然而&#xff0c;这种相似性纯属巧合——或者说是一种营销策略的产物。 JavaScript诞…

【文献分享】Machine learning models提供数据和代码

数据输入及前期信息&#xff1a;ChronoGauge 需要一个基因表达矩阵&#xff0c;其中包括来自多个时间进程 RNA-测序实验的观测数据&#xff0c;用于训练&#xff0c;并且需要有关每个基因在连续光照&#xff08;LL&#xff09;条件下经过光暗&#xff08;LD&#xff09;周期调整…

PHP MySQL Delete 操作详解

PHP MySQL Delete 操作详解 引言 在Web开发中&#xff0c;数据库是存储和管理数据的重要工具。PHP作为一种流行的服务器端脚本语言&#xff0c;与MySQL数据库结合使用可以高效地处理数据。本文将详细介绍PHP中如何使用DELETE语句删除MySQL数据库中的数据。 什么是DELETE语句&am…

计组-大/小端存放区别

在计算机系统中&#xff0c;大端存放&#xff08;Big-Endian&#xff09;和小端存放&#xff08;Little-Endian&#xff09;是两种不同的多字节数据存储方式&#xff0c;主要区别在于字节在内存中的排列顺序。理解它们对底层编程&#xff08;如网络通信、二进制文件处理、硬件交…

线程同步相关知识

文章目录一、线程同步的核心目标二、线程安全的判定条件三、同步方式一&#xff1a;synchronized 关键字1. 同步代码块2. 同步方法四、锁的释放与不释放场景1. 自动释放锁的场景2. 不会释放锁的场景五、同步方式二&#xff1a;ReentrantLock&#xff08;显式锁&#xff09;1. 核…

Armoury Crate无法通过BIOS卸载

设备&#xff1a;天选4 Armoury Crate窗口反复弹出影响使用体验&#xff0c;但无法通过BIOS关闭该怎么办&#xff1f;本文以天选4为例提供解决方案。 Step1&#xff1a;进入服务支持官网 Armoury Crate-服务支持 下滑点击”查看更多” 下载安装卸载工具 得到Armoury_Crate_Un…

如何将视频转为GIF格式,3大视频转为GIF工具

在社交媒体和即时通讯盛行的当下&#xff0c;GIF 动图以其独特的魅力备受青睐。它能够生动地捕捉视频中的精彩瞬间&#xff0c;凭借体积小巧、无需复杂加载且可循环播放的特性&#xff0c;成为了人们在网络交流中表达情感、分享趣事的得力工具。无论是制作诙谐幽默的表情包&…

开发避坑指南(22):Vue3响应式编程中this绑定机制与解决方案

错误信息 TypeError: Cannot read properties of undefined (reading find) TypeError: r.vnode.el.querySelector is not a function报错背景 vue2项目升级到vue3后&#xff0c;原来的代码报错。 报错代码computed: {/** 计算列的显示与隐藏*/columnVisible() {return functio…

AI学习笔记三十五:实时传输视频

若该文为原创文章&#xff0c;转载请注明原文出处。 目的是实现视频的传输&#xff0c;只是个demo. 程序分为两部分&#xff0c;视频接收端和视频发送端。 一、视频接收端流程分析 主要流程&#xff1a; 初始化配置&#xff1a; 设置UDP端口&#xff08;5001&#xff09;和缓…

【ArcGIS】分区统计中出现Null值且Nodata无法忽略的问题以及shp擦除(erase)的使用——以NDVI去水体为例

需求 已有某地NDVI栅格、行政区shp以及水体shp&#xff0c;计算每个行政区的平均NDVI 问题 1.如果不剔除水体 负值NDVI会把平均值拉低 且水体NDVI并不全为负 需要通过shp剔除&#xff0c;Mask掩膜是提取水体本身而不是剩余部分 2.使用分区统计工具&#xff08;Zonal statis…

Linux中的内核同步源码相关总结

什么是内核同步Linux 内核同步是指内核中用于解决并发执行单元&#xff08;如进程、中断、内核线程等&#xff09;对共享资源&#xff08;如全局数据结构、硬件寄存器、链表等&#xff09;的竞争访问的一系列机制和技术。其核心目标是保证多个并发单元在操作共享资源时的数据一…

WORD接受修订,并修改修订后文字的颜色

在 Word 中&#xff0c;接受修订之后默认会采用正文的默认字体格式&#xff0c;不会保留修订时设置的颜色&#xff0c;比如“插入内容是蓝色字体”的设置会被清除。 如果你想要做到&#xff1a;✅ 接受所有修订后仍然让“原插入的文字”变为蓝色字体保留下来你只能通过一些手动…

行业速览:中国新能源汽车市场格局与关键趋势

在全球汽车产业迈向绿色、低碳、智能化的变革浪潮中&#xff0c;新能源汽车已成为各国争夺的战略高地。中国&#xff0c;作为全球最大的汽车市场和新能源汽车制造国&#xff0c;正以强大的市场规模、完整的产业链体系以及快速提升的技术创新能力&#xff0c;在这场变革中不断加…

【51单片机2个按键控制流水灯转向】2022-10-25

缘由51单片机按键流水灯-嵌入式-CSDN问答 #include "REG52.h" sbit k1P3^0; sbit k2P3^1; void main() {unsigned char l0,xd0,ys10,ys20,z0;P1l;while(1){if(k10&&xd0){z0;while(k10);}if(k20&&xd0){z1;while(k20);}if(ys10)if(ys20){if(z0)if(l0)…

flutter开发(一)flutter命令行工具

安装 Linux下面的flutter安装比较简单&#xff0c;在flutter 中文战 上下载一个最新稳定的版本&#xff0c;解压到系统上就行了。 我下载的是Linux下的3.32.7版。 解压之后&#xff0c;flutter目录里会有bin、dev等目录&#xff0c;把bin目录加到系统的PATH环境变量里&#…

OpenCV 入门实战:从环境配置到图像 / 视频处理

OpenCV 是计算机视觉领域最常用的开源库之一&#xff0c;它提供了丰富的图像和视频处理功能。本文将从环境配置开始&#xff0c;带大家一步步解析基础操作代码&#xff0c;快速入门 OpenCV 的使用。 一、环境配置 在开始之前&#xff0c;我们需要先搭建好 OpenCV 的运行环境。…

2.2.1 饰面板材和陶瓷的特性和应用

1、饰面石材1&#xff09;天然花岗岩2&#xff09;天然大理石3&#xff09;人造石&#xff08;1&#xff09;人造石按主要原材料分包括人造石实体面材、人造石英石和人造石岗石等产品。2、建筑卫生陶瓷建筑卫生陶瓷包括建筑陶瓷和卫生陶瓷两大类。建筑陶瓷包括陶瓷砖、建筑琉璃…

C++的结构体数组

结构体数组的基础知识 结构体数组通过​​组合数据批量管理​​的特性&#xff0c;广泛应用于学生管理、游戏角色属性存储等场景。常见问题 ​​数组越界​​&#xff1a;静态数组长度固定&#xff0c;超过数组长度的访问&#xff0c;会导致未定义行为。​​未初始化成员​​&a…