【数据结构】哈希表+算法复杂度与经典排序查找(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;

}

相关推荐
光电的一只菜鸡19 小时前
isp中关于锐化对图像清晰度的影响
算法
磐链科技19 小时前
交易所开发中的合规与安全:KYC、AML、冷热钱包设计要点
java·区块链
企鹅的蚂蚁19 小时前
LubanCat RK3588 实时 Linux 开发(九):从 U-Boot 到 initramfs 的串口启动故障分析
linux·rk3588·u-boot·串口调试·initramfs·linux启动
码农飞哥19 小时前
企业级RAG系统架构详解
java·人工智能·ai编程·rag·ai应用
Wang's Blog19 小时前
Java框架快速入门: Spring Security+OAuth2之搭建授权服务器(依赖与表结构)
java·服务器·spring
土司大王19 小时前
LeetCode hot100——4.寻找两个正序数组的中位数:Java 二分第K小 递归裁剪
java·算法·leetcode
dllxhcjla19 小时前
SpringAI+DeepSeek大模型应用开发
java
薛定e的猫咪19 小时前
(ICML2024)QSM:基于 Q 函数梯度对齐分数场的扩散模型离策略强化学习
人工智能·深度学习·算法
小鱼干..19 小时前
青少年ctf url
java
troy12819 小时前
Python 基础语法(六):函数与模块、文件操作、异常处理
开发语言·python