【数据结构】哈希表+算法复杂度与经典排序查找(C语言)

哈希表(散列表)

2.1 哈希表基本概念

散列存储(哈希存储):将要存储的元素的关键字和存储位置之间建立映射关系,这个映射称为哈希函数。

存储:根据哈希函数直接算出存储位置,存入数据

查找:同样通过哈希函数计算位置,直接访问数据

理想情况下,查找、插入、删除时间复杂度可以达到 **O(1)**。

哈希表核心三要素:哈希函数、存储容器、哈希冲突处理机制。

2.2 哈希冲突

哈希冲突:不同关键字,经过同一个哈希函数计算,得到了相同存储下标。

冲突无法完全避免,我们要做的是设计良好哈希函数 + 高效冲突处理方案,降低冲突带来的性能损耗。

2.3 常见冲突解决思路

  1. 开放定址法(闭散列)

发生冲突就按照某种规则,在数组内继续向后探测寻找下一个空位,包含线性探测、二次探测、双重散列。所有数据全部存放在数组中,不使用链表。缺点:容易产生数据聚集,删除比较麻烦,不能直接置空。

  1. 链地址法(拉链法、开散列)

数组每个下标位置保存链表头指针;哈希到同一个下标的全部元素,挂在这个位置对应的单链表上。

优点:实现简单,删除方便,不会干扰其他下标元素;负载因子可以大于1。

缺点:需要额外内存保存链表指针;极端全部冲突会退化成链表O(n)查找效率。

2.4 哈希表特点总结

优点:理想O(1)查找,查询速度极快

缺点:

  1. 存在哈希冲突,最坏性能退化

  2. 不支持范围遍历,不适合有序输出

  3. 需要设计好哈希函数,负载因子过大需要做扩容

工程中C++ unordered_map、Java HashMap底层都是哈希表(链地址法)。

算法基础

3.1 算法五大评价指标

  1. 正确性:合法输入得到正确输出;非法输入给出合理处理。

  2. 可读性:代码清晰,方便阅读交流,高内聚低耦合。

  3. 健壮性:面对非法输入不会直接崩溃,能够异常处理。

  4. 时间复杂度:衡量算法执行时间随数据规模n增长的趋势。

  5. 空间复杂度:衡量算法额外开辟的内存空间随n增长的趋势。

3.2 大O表示法(时间复杂度)

大O:只关心数据规模很大时的增长趋势,忽略常数,只保留最高阶项,去掉最高阶系数。

计算规则:

  1. 常数全部替换为1;

  2. 只保留表达式最高阶项;

  3. 去掉最高阶项相乘系数。

常见复杂度从小到大排序

O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(2ⁿ) < O(n!)

复杂度说明|O(1)

常数时间,与n无关|变量交换,简单运算

O(logn)对数,每次缩小一半范围二分查找

O(n)|线性,遍历全部数据|数组遍历

O(nlogn)线性对数快速排序、希尔排序

O(n²)平方级,双层循环|冒泡、插入、选择排序

示例分析

// O(1) 常数复杂度

void swap(int *a,int *b)

{

int tmp=*a;

*a=*b;

*b=tmp;

}

// O(n) 线性复杂度

void fun1(int n)

{

for(int i=0;i<n;i++){

printf("%d\n",i);

}

}

// O(logn) 对数复杂度,i *=2,每次翻倍

void fun2(int n)

{

for(int i=1;i<n;i*=2){

printf("%d\n",i);

}

}

// O(n²) 双层循环

void fun3(int n)

{

for(int i=0;i<n;i++){

for(int j=i;j<n;j++){

printf("%d %d\n",i,j);

}

}

}

四、经典排序算法(C代码+特性)

4.1 插入排序

思想:把数组分为有序区、无序区;逐个取无序区元素,插入到前面有序区合适位置。

时间复杂度:最坏、平均 O(n²);最好(已有序)O(n)

空间复杂度:O(1),原地排序

稳定性:稳定排序

void insert_sort(int *a, int len)

{

int i,j,tmp;

for(i = 1; i < len; i++)

{

tmp = ai;

j = i;

while(j>0 && tmp < aj-1)

{

aj = aj-1;

j--;

}

aj = tmp;

}

}

4.2 希尔排序(缩小增量排序)

> 插入排序的优化版本,先分组预排序,逐步降低增量,增量最后变为1退化为普通插入排序。

时间复杂度:O(nlogn) ~ O(n²)

空间复杂度:O(1)

稳定性:不稳定排序(相同元素可能交换相对位置)

void shell_sort(int *a, int len)

{

int inc = 0;

int i = 0, j= 0, tmp = 0;

for (inc = len/2; inc > 0; inc /= 2)

{

for (i = inc; i < len; ++i)

{

tmp = ai;

j = i;

while (j >= inc && tmp < ajinc)

{

aj = ajinc;

j -= inc;

}

aj = tmp;

}

}

}

4.3 快速排序(分治思想)

选一个基准key,双指针左右向中间遍历;小于基准放左边,大于基准放右边;递归处理左右子区间。

平均时间复杂度:O(nlogn);最坏退化为O(n²)

空间复杂度:O(logn)(递归栈)

稳定性:不稳定排序

void quick_sort(int *a, int begin, int end)

{

if (begin >= end)

{

return ;

}

int i = begin;

int j = end;

int key = ai;

while (i < j)

{

while (i < j && key <= aj)

{

--j;

}

ai = aj;

while (i < j && key >= ai)

{

++i;

}

aj = ai;

}

ai = key;

quick_sort(a, i+1, end);

quick_sort(a, begin, i1);

}

五、经典查找算法:二分查找

前提条件:数组必须有序(升序/降序)

时间复杂度:O(logn)

空间复杂度:迭代实现O(1);递归O(logn)

//迭代版二分查找,找到返回下标,找不到返回1

int binary_search(int arr\[\],int len,int key)

{

int left=0;

int right = len1;

while(left <= right)

{

int mid = (left+right)/2;

if(arrmid == key)

{

return mid;

}else if(arrmid < key)

{

left = mid+1;

}else{

right = mid1;

}

}

return -1;

}

相关推荐
paopaokaka_luck1 小时前
基于springboot3+vue3的车间生产管理系统(Echarts图形化分析、BI报表)
java·前端·spring boot·学习·echarts
淡海水1 小时前
04-02-哈希-Dictionary-TKey-TValue-上-核心数据结构
数据结构·算法·c#·哈希算法·编译·字典·dictionary
Kismet_nvi1 小时前
Python 基础核心知识点总结
开发语言·windows·python
戴西软件1 小时前
国内有哪些数据轻量化格式转换软件?
数据库·算法·安全·信息可视化·自动化·rpa
爱学习的小邓同学1 小时前
Golang语言入门
开发语言·后端·golang
老洋葱Mr_Onion1 小时前
【C++】CSP-J初赛模拟卷七错题整理(作者自用)
c++·算法·深度优先
λqaq71 小时前
MySQL 数据库基础(2)约束、用户权限、远程连接、外键与 TCP/UDP 理解
linux·数据库·python·tcp/ip·mysql
Zane19941 小时前
遍历时删元素为什么报错:modCount 这个"计数器"在背后盯着你
java·后端
土星云SaturnCloud2 小时前
Real-ESRGAN超分辨率算法原理与边缘侧部署实践
服务器·算法·ai·边缘计算·real-esrgan