数据结构的有序查找

@数据结构复习笔记(二):

数据结构的有序查找

今天主要学的是排查找,这里我们就简单的讲一下几种常见的有序查找:

1.顺序查找(较通用,适用所有情况)

2.二分查找

3.二分查找非递归

-----------------------------二分查找的优化------------------------

4.插值查找

5.fib查找

6.分块查找(无代码,懒~)

先来看查找的输入输出:

给定一个任意无序定长数组num[10]={1,3,5,7,9,2,4,6,8,10}

因为今天写的是有序查找

所以待会儿会将其先进行排序(有关排序可以看我的第一篇文章)

现在排序完成的num是{1,2,3,4,5,6,7,8,9,10}

现在我们的目标值是7

我们需要从现在的有序数组中去找到这个值7的下标即6(下标从0开始)

需要得到6即找到

若未找到输出-1

看似简单的过程:可以有很多种处理的方法:

先看看第一种查找:

顺序查找:

这种查找方法是最简单的,也是初学c语言的必做题之一。

方法就是对数组进行一次遍历,遍历中通过比较,找到值就把下标返回,并跳出循环,如果遍历一遍未找到值

则输出-1

方法简单粗暴,时间复杂度O(n)

不多做分析,上代码

int findGoalbyOrder(int num[], int len, int goal) {
    for (int i = 0; i < len; i++)
    {
        if (num[i] == goal)
            return i;
    }
    return -1;
};

二分查找(非递归):

上一次讲插入排序的时候有提到优化插入排序的方法,即要找到插入位置是一个查找过程,通过二分查找可以把查找的时间复杂度缩减到O(logn)

这里就讲一下二分查找

二分找到的思路是我们从小到大都接触到的一种常用方法

即从数组中间部分开始查找,先看数组的中间值(这里记作mid)

如果mid=goal //goal为目标值
就将mid的下标返回;

如果mid>goal
将范围缩至mid的前半部分;

如果mid<goal
将范围缩至mid的后半部分;

上代码

int findGoalbyBinarySearch(int num[], int goal, int n)
{
    int low, high, mid;
    low = 0;
    high = n - 1;
    while (low <= high)
    {
        mid = (low + high) / 2;
        if (num[mid] == goal)
            return mid;
        if (num[mid] > goal)
            high = mid - 1;
        if (num[mid] < goal)
            low = mid + 1;
    }
    return -1;
}

二分查找(递归):

代码

int BinarySearch2(int num[], int goal, int low, int high)
{
    int mid = low + (high - low) / 2;
    if (num[mid] == goal)
        return mid;
    if (num[mid] > goal)
        return BinarySearch2(num, goal, low, mid - 1);
    if (num[mid] < goal)
        return BinarySearch2(num, goal, mid + 1, high);
}

-----------------------------二分查找的优化------------------------

插值查找

插值查找优化了二分查找

二分查找选取中间位置,插值查找则通过查找值判定大概位于序列的哪个位置比例。
思路大致相同于二分查找

不同之处在于二分查找是确定中间位置向两边查找,而插值查找则是先根据目标值判定大概的位置比例

即修改二分查找中的mid值
mid = low + (goal - num[low]) / (num[high] - num[low]) * (high - low);
这样看着不太直观

我们看看这个式子到底是什么样子:
在这里插入在这里插入图片描述图片描述

上面这个图展示了如何从二分查找的式子变化成插值查找的狮子的,即修改系数1/2,用目标值减去起始下标比上整个数组的长度,得到的比例则是系数

int findGoalbyInsertvalue(int num[], int len, int goal) {
    int low, high, mid;
    low = 0;
    high = len - 1;
    while (low <= high)
    {
        mid = low + (goal - num[low]) / (num[high] - num[low]) * (high - low);
        if (num[mid] == goal)
            return mid;
        if (num[mid] > goal)
            high = mid - 1;
        if (num[mid] < goal)
            low = mid + 1;
    }
    return -1;
};

非递归类比之前二分查找

fib查找

看起来很高端的一种查找
实际上还是二分查找的一种优化

(这里我os一下:我搜了下fib查找比二分查找好在哪儿,基本上都说的是:获取mid的方式是只通过加减,不需要乘除,这里优化了很多,因为时间复杂度肯定没有优化还是logn级,这里噢,我个人感觉是因为黄金分割率给查找带来的好处,因为查找很多情况,目标数组不一定可读,而黄金分割率在很多地方都有存在,可能统计学上,黄金分割率可能广泛存在吧,欢迎大佬讲解!!!!!)

回归正题,这里我们讲解一下fib查找——斐波那契((Fibonacci)查找(黄金分割查找)
首先不得不提的就是斐波那契数列,这个数列被广泛使用,因为其独特的存在,无论是数列还是计算机的递归,这都是一个不可绕开的话题,即给出前两项1,1,从现在起的下一项都等于前两项的和

1 1 2 3 5 8 13…

数据显示,这个数列的第n项,当n趋于无穷大时,lim fib(n-1)/fib(n)=golden section即约等于0.618

黄金分割率的前32位
0.6180339887 4989484820 458683436565

难道618是这么来的????

好了现在我们已经知道了fib数列

来看看fib查找:
就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为F[n],完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。

简单来说
第一步 确定一个新的数组(此数组位原数组+补位0或补位数):根据原数组长度n,在fib数列中招到一个fib[x],使得fib[x]>n,刚好大于就行
第二步,找到比率值,找到了fib[x],那么前面的一个数fib[x-1]/fib[x]就约等于0.618,所以fib[x-1]则位mid的位置,0.618就类似于上面插值查找的那个系数
第三步,同二分查找,若mid大,范围往左,反之往右
上代码

//这里是获取mid 即通过fib数列获取到比例值所在的位置
int getMid(int len) {
    int num1,num2,temp,mid;
    num1 = num2 = 1;
    while (num2 < len) {
        temp = num2;
        num2 = num1 + num2;
        num1 = temp;
    }
    mid = num1;
    return mid-1; //数组下标从0开始,为了省事,我们直接在这里返回的是数组的下标值
};

上面给了插值的非递归,这里给个递归,感兴趣的可以写一写非递归的噢,顺道发我一份,hhhh

int findGoalbyFib(int num[], int goal, int low, int high) {
    int mid = low + getMid(high - low + 1);
    if (num[mid] == goal)
        return mid;
    if (num[mid] > goal)
        return findGoalbyFib(num, goal, low, mid - 1);
    if (num[mid] < goal)
        return findGoalbyFib(num, goal, mid + 1, high);
};

分块查找(无代码,懒~)

分块查找又称索引顺序查找,它是顺序查找的一种改进方法。
  算法思想:将n个数据元素"按块有序"划分为m块(m ≤ n)。每一块中的结点不必有序,但块与块之间必须"按块有序";即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;而第2块中任一元素又都必须小于第3块中的任一元素,……
  算法流程:
  step1 先选取各块中的最大关键字构成一个索引表;
  step2 查找分两个部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块中;然后,在已确定的块中用顺序法进行查找。

放个完整的代码:

// search_al.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
/*
查找算法1
线性查找:顺序查找,折半查找,插值查找,fib查找

本实验为线性查找算法

在任意无序数组中查找给定值的数
如:在数组num中查找最大数或给定数

创建人:hu#
创建时间:2020.7.29
*/
//

#include <iostream>

using namespace std;

int getMid(int len);
int findGoalbyOrder(int num[], int len,int goal);
int findGoalbyBinarySearch(int num[], int goal, int n);
int findGoalbyInsertvalue(int num[], int len, int goal);
int findGoalbyFib(int num[], int goal, int low, int high);
int BinarySearch2(int num[], int goal, int low, int high);


int main()
{
    int num[10] = {1,2,3,4,5,6,7,8,9,10};
    int goal = 7;
    int length = sizeof(num)/sizeof(num[0]);
    int result = findGoalbyOrder(num,length,goal);
    cout << result << endl;
    result = findGoalbyBinarySearch(num, goal, length);
    cout << result << endl;
    result = BinarySearch2(num,goal,0,9);
    cout << result << endl;
    result = findGoalbyInsertvalue(num, goal, length);
    cout << result << endl;
    result = findGoalbyFib(num, goal, 0, 9);
    cout << result << endl;
}


int findGoalbyOrder(int num[], int len, int goal) {
    for (int i = 0; i < len; i++)
    {
        if (num[i] == goal)
            return i;
    }
    return -1;
};
int findGoalbyBinarySearch(int num[], int goal, int n)
{
    int low, high, mid;
    low = 0;
    high = n - 1;
    while (low <= high)
    {
        mid = (low + high) / 2;
        if (num[mid] == goal)
            return mid;
        if (num[mid] > goal)
            high = mid - 1;
        if (num[mid] < goal)
            low = mid + 1;
    }
    return -1;
}
int BinarySearch2(int num[], int goal, int low, int high)
{
    int mid = low + (high - low) / 2;
    if (num[mid] == goal)
        return mid;
    if (num[mid] > goal)
        return BinarySearch2(num, goal, low, mid - 1);
    if (num[mid] < goal)
        return BinarySearch2(num, goal, mid + 1, high);
}
int findGoalbyInsertvalue(int num[], int len, int goal) {
    int low, high, mid;
    low = 0;
    high = len - 1;
    while (low <= high)
    {
        mid = low + (goal - num[low]) / (num[high] - num[low]) * (high - low);
        if (num[mid] == goal)
            return mid;
        if (num[mid] > goal)
            high = mid - 1;
        if (num[mid] < goal)
            low = mid + 1;
    }
    return -1;
};

int getMid(int len) {
    int num1,num2,temp,mid;
    num1 = num2 = 1;
    while (num2 < len) {
        temp = num2;
        num2 = num1 + num2;
        num1 = temp;
    }
    mid = num1;
    return mid-1; //数组下标从0开始,为了省事,我们直接在这里返回的是数组的下标值
};
int findGoalbyFib(int num[], int goal, int low, int high) {
    int mid = low + getMid(high - low + 1);
    if (num[mid] == goal)
        return mid;
    if (num[mid] > goal)
        return findGoalbyFib(num, goal, low, mid - 1);
    if (num[mid] < goal)
        return findGoalbyFib(num, goal, mid + 1, high);
};


今天的分享到此结束啦,欢迎大家讨论!!!!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值