最近重新温习了一下数据结构与算法,所以稍作整理,做了个总结,用通俗易懂的语言,满足更多的初学者阅读和学习这方面的知识,希望对大家有所帮助。
算法
算法的时间复杂度和空间复杂度
我们所讲的算法主要分为查找算法和排序算法,这里也经常会提到算法的时间复杂度和空间复杂度,它可以体现算法的性能,那么什么是算法的时间复杂度和空间复杂度呢?时间复杂度:执行算法所需要的计算工作量。它是一个函数,描述了该算法的运行时间,通常用大“O”来表示
空间复杂度:执行算法所需要的内存空间
查找算法
顺序表查找
顺序表查找又叫线性表查找,是最基本的查找技术。
过程:从表中第一个(或最后一个)记录开始,逐个进行比较,如果某个记录的关键字和给定值相等,则查找城成功。
废话不多说,先上代码:
//array为数组,length为要查找的数组长度,key为要查找的关键字int SequentialSearch(int* array,int length,int key)
{
|for (int i = 1; i <= length; i++)
|{
||if(array[i] == key)
||{
|||return i;
||}
|}
|return 0;
}
这就是最基本的顺序表查找,是不是非常的简单。当然这里并非足够完美,因为每次循环都要对 i 是否越界(即是否<= n)做判断,那么是否还可以优化呢?当然! 我们可以通过设置一个哨兵,可以不需要每次让 i 与 n 作比较。
顺序表查找优化
int SequentialSearch(int* array,int length,int key)
{
|int i = length; //循环从尾部开始|a[0] = key; //设置a[0]为关键字值,即 “哨兵”|while (array[i] != key)
|{
||i--;
|}
|return i; //如果返回0,则说明查找失败}
看上去好像没什么变化,但当总数据较多时,效率提高很大。当然哨兵可以在数组的最开始,也可以在末端。对于这种顺序表查找算法来说,查找成功最好的情况就是在第一个位置就找到了,时间复杂度为O(1);
最差的情况是最后一个位置才找到,需要n次比较,所以时间复杂度为O(n)。
顺序表查找的缺点:当 n 很大时,查找效率极为低下。
二分法查找
二分法查找又叫折半查找。二分法查找的前提是: 要查找的数据集必须是顺序存储的有序线性表。
二分法查找的思想:在有序表中,取一个中间值,如果要查找的值小于这个中间值,就就在中间值左半边继续查找,如果要查找的值大于这个中间值,那就在中间值右半边继续查找,不断重复上面的过程,直到查找成功 (所以大家知道为什么要查找的数据集必须是顺序存储的有序线性表了吧)。
如果觉得我解释的不到位,没关系,直接上代码:
int BinarySearch(int* array,int length,int key)
{
|int low = 1; //定义最低下标为记录首位|int high = length; //定义最高下标为记录末位|int mid = 0; //中间值|while(low < high)
|{
||mid = (low + high) /2; //折半,取中间值||if (key < array[mid]) //如果查找的值比中间值小||high = mid -1; //最高下标向左移动一位||else if (key > array[mid]) //如果查找的值比中间值大||low = mid + 1; //最低下标向右移动一位||else
||return mid; //如果相等则说明mid即为查找到的位置|}
|return 0;
}
二分法查找的时间复杂度为O(logn),显然远远好过顺序表查找的O(n)时间复杂度。
不过由于二分法查找需要有序表顺序存储,所以对于需要频繁插入和删除操作的数据集来说并不适用。
那么我们新的问题又来啦,既然二分法是折二分之一,那么为什么不是折四分之一、八分之一甚至更多呢?
所以我们的二分法查找,是有改进空间的。因此,又引出我们接下来的算法。
插值查找
其实插值查找和二分法查找只有一句代码的差别。插值查找就是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找算法。
在上面二分法查找中:
mid = (low + high) /2; //折半,取中间值
这行代码,我们略微等式变换后可以得到:
mid = (low + high) /2 = low + (high -low) /2
也就是说mid等于最低下标low加上最高下标high与low的差的一半,算法科学家们考虑的就是将这个1/2进行进一步的改进,得到如下的方案:
mid = low + (high - low) * (key - array[low])/(array[high] - array[low]) //插值
因此,插值查找的核心就在于将这个1/2换成了我们的插值函数:
(key -array[low])/(array[hig