算法
数据结构中的算法,指的是数据结构所具备的功能
一个算法应该具有以下五个重要的特征:
有穷性:算法的有穷性是指算法必须能在执行有限个步骤之后终止;
确切性:算法的每一步骤必须有确切的定义;
输入项:一个算法有0个或多个输入;
输出项:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
可行性:算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)
如何评价一个算法:
时间复杂度:由于计算机的性能不同,无法准确地衡量出算法执行所需要的时间
因此我们用算法的执行次数来代表算法的时间复杂度
一般使用 O(公式) 一般忽略常数
常见的时间复杂度:
// O(1)
printf("%d",i);
// O(logn)
for(int i=n; i>=0; i/=2)
{
printf("%d",i);
}
// O(n) O(N)
for(int i=0; i<n; i++)
{
printf("%d",i);
}
// O(nlogn)
for(int i=0; i<n; i++)
{
for(int j=n; j>=0; j/=2)
{
printf("%d",i);
}
}
// O(n^2)
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
printf("%d",i);
}
}
空间复杂度:
执行一个程序所需要的内存空间大小,是对一个算法在运行过程中临时占用存储空间大小的衡量
一般只要算法不涉及动态分配的内存以及递归,通常空间复杂度为O(1)
例如:求第n个斐波那契数列的递归实现算法 空间复杂度O(n)
注意:对于一个算法而言,其时间复杂度与空间复杂度往往是相互影响的,没有唯一的标准,需要结合实际综合考虑
分治:
分而治之,把一个大而复杂的问题,分解成很多小而简单的问题,利用计算机强大的计算能力来解决问题
实现分治的方法:循环、递归
查找算法:
顺序查找
对待查找的数据没有要求,从头到尾逐一比较,在小规模的查找中较为常见,查找效率较低
时间复杂度:O(N)
二分查找(折半查找)
待查找的数据必须有序,从数据中间位置开始比较查找,如果中间值比key小,则从左边继续进行二分查找,反之从右边进行。
时间复杂度:O(logN)
块查找(权重查找)
是一种数据处理的思想,不是一种特定的算法,当数据量非常多时,可以先把数据进行分块处理,然后再根据分块的条件进行查找,例如英文字典
哈希查找(Hash)
数据 经过 哈希函数 计算出数据在哈希表中的位置,然后标记位置,方便之后的查找,它的时间复杂度最高可以达到 O(1)
但是该算法有很大的局限性,不适合负数、浮点型数据、字符型数据的查找,还需要额外申请存储空间,空间复杂度高,是一种典型的以空间换时间的算法
直接定址法:直接把数据当做哈希表的下标,把哈希表中该下标的位置+1
数据分析法:分析数据的特点来设计哈希函数,常用的方法是找到最大值和最小值,用 最大值-最小值+1 确定哈希表的长度,使用 数据-最小值 作为哈希表的下标访问哈希表
平方取中法、折叠法、随机数法,但都无法保证哈希数据的唯一性,出现所谓的哈希冲突,一般使用链表解决
Hash函数的应用:MD5、SHA-1都属于Hash算法中的应用
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define swap(a,b) {typeof(a) t=(a);(a)=(b);(b)=t;}
#define show_arr(arr,len) \
{for(int i=0;i<len;printf("%d ",arr[i++]));printf("\n");}
// 顺序查找
int order_search(const int* arr,int len,int key)
{
for(int i=0; i<len; i++)
{
if(key == arr[i]) return i;
}
return -1;
}
void sort(int* arr,int len)
{
for(int i=0; i<len-1; i++)
{
for(int j=i+1; j<len; j++)
{
if(arr[j] < arr[i])
swap(arr[j],arr[i]);
}
}
}
// 循环二分
int binary_search_for(int* arr,int len,int key)
{
int l = 0, r = len-1;
while(l <= r)
{
int p = (l+r)/2;
if(arr[p] == key) return p;
if(arr[p] > key)
r = p-1;
else
l = p+1;
}
return -1;
}
int _binary_search(int* arr,int l,int r,int key)
{
if(l > r) return -1;
int p = (l+r)/2;
if(arr[p] == key) return p;
if(key < arr[p])
return _binary_search(arr,l,p-1,key);
else
return _binary_search(arr,p+1,r,key);
}
// 递归二分
int binary_search_recursion(int* arr,int len,int key)
{
return _binary_search(arr,0,len-1,key);
}
// 哈希查找
bool hash_search(int* arr,int len,int key)
{
/*
// 直接定址法
// 创建哈希表
int hash[1000000] = {};
// 标记
for(int i=0; i<len; i++)
{
hash[arr[i]]++;
}
// 查找
return hash[key];
*/
// 数据分析
int max = arr[0],min = arr[0];
for(int i=1; i<len; i++)
{
if(arr[i] > max) max = arr[i];
if(arr[i] < min) min = arr[i];
}
// 创建哈希表
int hash[max-min+1];
memset(hash,0,sizeof(hash));
// 标记
for(int i=0; i<len; i++)
{
hash[arr[i]-min]++;
}
return hash[key-min];
}
int main(int argc,const char* argv[])
{
int arr[10] = {};
for(int i=0; i<10; i++)
{
arr[i] = rand()%100;
printf("%d ",arr[i]);
}
printf("\norder_search:%d\n",order_search(arr,10,29));
sort(arr,10);
show_arr(arr,10);
printf("binary_search_for:%d\n",binary_search_for(arr,10,83));
printf("binary_search_recursion:%d\n",binary_search_recursion(arr,10,77));
printf("hash_search:%d\n",hash_search(arr,10,77));
}