大
家
好
今天讲讲算法和哈希函数,数据结构基础请跳转数据结构(一)
数据结构干货总结(二)
- 算法
- 常见的数据结构及算法
- 你以为我要都讲嘛?????????不可能,因为我不会,哈哈。大家了解就好,想学的去其他博主那里看看吧,我们今天讲点嵌入式能用到的、简单的、一学就会的
- 1、概念
- 2、算法的设计要求及特性
- 3、时间复杂度
- 4、快速排序
- 哈希函数
- 1、概念
- 2、解决哈希冲突
- 3、哈希表长度的确定
- 4、哈希的原理和特点
- 5、Hash构造函数的方法
- 给大家来一个除留余数法代码
算法
常见的数据结构及算法
你以为我要都讲嘛?????????不可能,因为我不会,哈哈。大家了解就好,想学的去其他博主那里看看吧,我们今天讲点嵌入式能用到的、简单的、一学就会的
比如我的英雄前辈点这里跳转到英雄前辈
1、概念
算法(algorithm,计算程序):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。(计算方法)
2、算法的设计要求及特性
设计要求:
正确性,代码结果一定正确
高效率,时间复杂度低
低存储,尽少开辟内存空间
健壮性
可读性
特性:
确定性,
有穷性,
输入、输出
可行性
3、时间复杂度
什么是时间复杂度
一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度,记为T(n)。(time)
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出T(n)的同数量级(它的同数量级有以下:1,Log2n ,n ,nLog2n ,n的平方,n的三次方,2的n次方,n!),找出后,f(n)=该数量级,若T(n)/f(n)求极限可得到一常数c,则时间复杂度T(n)=O(f(n))。
公式:T(n)=O(f(n))的说明
T(n):时间复杂度
n:问题规模----数据的范围或大小。
f(n):问题规模的函数,是基本操作重复执行次数与n的关系。
用大写O()来体现时间复杂度的记法称大O阶记法。
如:在T(n)=O(n3)中
f(n**)=n3------表示某语句的重复执行次数为n3次
常见的时间复杂度
常见的算法时间复杂度由小到大依次为:
Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)
求解算法的时间复杂度的具体步骤:
找出算法中的基本语句,算法中执行最多的那条语句是基本语句,通常是最内层循环的循环体。
计算基本语句的执行次数的量级,保证最高次幂正确即可查看他的增长率。
用大O几号表示算法的时间性能
4、快速排序
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。他的时间复杂度是:O(nlogn) ~Ο(n2)
快速排序实在序列元素与选定基准元素比较分割为大小两部分的交换排序
从待排序列中任取一个基准元素
与基准元素比较将待排序列分割为大小两部分
再对各部分重新选择基准元素并依此规则排序
直到每个部分只剩一个元素为止
#include "sort.h"
//一次快排的函数
//O(n*log2n)
int part(int arr[],int low,int high)
{
//定义一个基准
int x=arr[low];
while(high>low)
{
//arr[high]>=x也必须满足外层循环条件
while(arr[high]>=x&&low<high)
{
high--;
}
arr[low]=arr[high];
while(arr[low]<=x&&low<high)
{
low++;
}
arr[high]=arr[low];
}
//基准赋值
arr[low]=x;
return low;
}
void sort(int arr[],int low,int high)
{
//if判断用来实现递归出口
if(low<high)
{
//第一次快排结束low的位置
int mid=part(arr,low,high);
sort(arr,low,mid-1);
sort(arr,mid+1,high);
}
}
哈希函数
1、概念
哈希函数
定义:哈希函数是根据关键字确定存储位置的函数
常用构造哈希函数方法:直接定址法、数字分析法、平方取中法、折叠法、
除留余数法、随机数法
哈希表
定义:哈希表是借助哈希函数将序列存储于连续存储空间的查找表
哈希冲突
定义:哈希冲突是不同关键字由哈希函数得到相同存储位置的现象
2、解决哈希冲突
开放定址法
线性探测法
二次探测法
伪随机探测法
再哈希法
链地址法
建立公共溢出区
3、哈希表长度的确定
数列的个数*4/3=表长
除留余数法,余数的确定:小于哈希表表长的最大素数。
4、哈希的原理和特点
(1)单向性:从哈希值不能反向推导原始数据(计算不可行),即从哈希输出无法倒推输入的原始数值。这是哈希函数安全性的基础。
(2)灵敏性:对输入数据敏感,哪怕只改了一个Bit,得到的哈希值也大不相同。换言之,消息M的任何改变都会导致哈希值H(M)发生改变。
(3)易压易算:Hash本质上是把一个大范围集合映射到另一个小范围集合。故输入值的个数必须与小范围相当或者更小,不然冲突就会很多。所以,哈希算法执行效率要高,散列结果要尽量均衡。
(4)抗碰撞性:理想Hash函数是无碰撞的,但实际上很难做到这一点。有两种抗碰撞性:一种是弱抗碰撞性,即对于给定的消息,要发现另一个消息,满足在计算上是不可行的;另一种是强抗碰撞性,即对于任意一对不同的消息,使得在计算上也是不可行的。
5、Hash构造函数的方法
1.直接定址法(极少用)
以数据元素关键字k本身或它的线性函数作为它的哈希地址,即:H(k)=k或H(k)=a×k+b;(其中a,b为常数)。
此法仅适合于:地址集合的大小==关键字集合的大小,其中a和b为常数。
2.数字分析法
假设关键字集合中的每个关键字都是由s位数字组成(u1,u2,…,us),分析关键字集中的全体,并从中提取分布均匀的若干位或它们的组合作为地址。数字分析法是取数据元素关键字中某些取值较均匀的数字位作为哈希地址的方法。即当关键字的位数很多时,可以通过对关键字的各位进行分析,丢掉分布不均匀的位,作为哈希值。它只适合于所有关键字值已知的情况。通过分析分布情况把关键字取值区间转化为一个较小的关键字取值区间。
此法适于:能预先估计出全体关键字的每一位上各种数字出现的频度。
3.折叠法
将关键字分割成若干部分,然后取它们的叠加和为哈希地址。两种叠加处理的方法:移位叠加:将分割后的几部分低位对齐相加;边界叠加:从一端沿分割界来回折叠,然后对齐相加。
所谓折叠法是将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位),这方法称为折叠法。这种方法适用于关键字位数较多,而且关键字中每一位上数字分布大致均匀的情况。
折叠法中数位折叠又分为移位叠加和边界叠加两种方法,移位叠加是将分割后是每一部分的最低位对齐,然后相加;边界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加。
此法适于:关键字的数字位数特别多。
4.平方取中法
这是一种常用的哈希函数构造方法。这个方法是先取关键字的平方,然后根据可使用空间的大小,选取平方数是中间几位为哈希地址。哈希函数H(key)=“key2的中间几位”因为这种方法的原理是通过取平方扩大差别,平方值的中间几位和这个数的每一位都相关,则对不同的关键字得到的哈希函数值不易产生冲突,由此产生的哈希地址也较为均匀。
此法适于:关键字中的每一位都有某些数字重复出现频度很高的现象。
5.减去法
减去法是数据的键值减去一个特定的数值以求得数据存储的位置。
6.基数转换法
将十进制数X看作其他进制,比如十三进制,再按照十三进制数转换成十进制数,提取其中若干为作为X的哈希值。一般取大于原来基数的数作为转换的基数,并且两个基数应该是互素的。
7.除留余数法
假设哈希表长为m,p为小于等于m的最大素数,则哈希函数为h(k)=k%p,其中%为模p取余运算。除留余数法的模p取不大于表长且最接近表长m素数时效果最好,且p最好取1.1n~1.7n之间的一个素数(n为存在的数据元素个数)。
8.随机数法
设定哈希函数为:H(key)=Random(key)其中,Random为伪随机函数
此法适于:对长度不等的关键字构造哈希函数。
9.随机乘数法
亦称为“乘余取整法”。随机乘数法使用一个随机实数f,0≤f<1,乘积fk的分数部分在0~1之间,用这个分数部分的值与n(哈希表的长度)相乘,乘积的整数部分就是对应的哈希值,显然这个哈希值落在0~n-1之间。其表达公式为:Hash(k)=「n(fk%1)」其中“fk%1”表示fk的小数部分,即fk%1=fk-「fk」
此方法的优点是对n的选择不很关键。通常若地址空间为p位就是选n=2p.Knuth对常数f的取法做了仔细的研究,他认为f取任何值都可以,但某些值效果更好。如f=(-1)/2=0.6180329…比较理想。
10.字符串数值哈希法
把字符串的前10个字符的ASCⅡ值之和对N取摸作为Hash地址,只要N较小,Hash地址将较均匀分布[0,N]区间内。对于N很大的情形,可使用ELFHash(ExecutableandLinkingFormat,ELF,可执行链接格式)函数,它把一个字符串的绝对长度作为输入,并通过一种方式把字符的十进制值结合起来,对长字符串和短字符串都有效,这种方式产生的位置可能不均匀分布。
给大家来一个除留余数法代码
typedef struct node
{
int key;
struct node* next;
}node,*pnode;
#include "hash.h"
//初始化哈希表
void init_hash(pnode hash[])
{
if(hash==NULL)
{
printf("入参为空\n");
return;
}
int i;
for(i=0;i<MAX;i++)
{
hash[i]=NULL;
}
}
//插入元素
void insert_hash(pnode hash[],int key)
{
if(hash==NULL)
{
printf("入参为空\n");
return;
}
int i=key%13;
pnode p=(pnode)malloc(sizeof(node));
if(p==NULL)
{
return;
}
p->key=key;
p->next=hash[i];
hash[i]=p;
printf("插入成功\n");
}
//输出哈希表
void print_hash(pnode hash[])
{
if(hash==NULL)
{
printf("入参为空\n");
return;
}
int i;
for(i=0;i<MAX;i++)
{
printf("%d->",i);
pnode temp=hash[i];
while(temp!=NULL)
{
printf("%d---->",temp->key);
temp=temp->next;
}
printf("^\n");
}
}
//哈希表查找
void search_hash(pnode hash[],int key)
{
if(hash==NULL)
{
printf("入参为空\n");
return;
}
int i=key%13;
pnode p=hash[i];
while(p!=NULL&&p->key!=key)
{
p=p->next;
}
if(p==NULL)
{
printf("要查找的数据不在哈希表中\n");
}
else
{
printf("要查找的元素在哈希表中\n");
}
}