Data structure and algorithm analysis----Fibonacci search algorithm + binary search algorithm + difference search algorithm

Binary search and difference search

The binary search method and the difference search method are basically the same, only one code is different, and they are combined to describe

Let's talk about binary search first

overview

Binary search is also called binary search (Binary Search), which is a more efficient search method. However, the binary search requires that the linear table must adopt a sequential storage structure, and the elements in the table are arranged in order according to the key .
Baidu Encyclopedia: First, assuming that the elements in the table are arranged in ascending order, compare the keywords recorded in the middle position of the table with the search keywords. If the two are equal, the search is successful; otherwise, use the records in the middle position to divide the table into front and back sub-groups. table, if the key recorded in the middle position is greater than the search key, the previous sub-table is further searched, otherwise the latter sub-table is further searched. Repeat the above process until a record meeting the condition is found, making the search successful, or until the child table does not exist, at this time the search is unsuccessful.

accomplish

The premise of the binary search method is that the sequence is ordered. The following
assumes that the sequence is sorted from small to large. The
idea: use recursion to divide the sequence in half according to the size relationship between the value you are looking for and the middle value, and recurse until the value you are looking for is equal to the middle value. Or the elements in the array are recursively lost.
Further ideas: First, a sequence, we find the value in the middle of the sequence, and use this value to compare with the value we are looking for. If the value is greater, then take the sequence to the left of this value and perform the previous operation (that is, the operation of finding the middle value and comparing it with the value you are looking for). If the value is small, take the sequence on the right side of the value and perform such an operation. If the two are equal, it means that it has been found, then record the index of the intermediate value, and look for the same value from both sides from this index (at this time, the array is in order, and it is easy to find whether there is the same value). If so, also record the indexes of these values ​​and return the set of these indexes. If it has not been found, then when there is no data in the sequence, the loop will also end. At this time, it means that the
general idea has not been found: Write a method as the recursive method body. This method receives four parameters, which are: the original sequence (here we Use an array), the index values ​​of the starting point and ending point of the array in the array required for each recursion, and the value to be searched for. The function of the index values ​​of the two points is to divide the sequence in half. We divide the sequence in half by the difference in the input values ​​of the two indexes in each recursion. The start and end points determine which part of the sequence in the array the current recursion is operating on.
In the method body, we first get the index of the middle value of the current array, then get the middle value, and then compare the size of the middle value with the value we are looking for.
If the intermediate value is large, enter the next recursion, and pass in the parameter index of the end position (the index of the intermediate value + 1) among the parameters passed in by the next recursion, the index of the starting position remains unchanged, and other parameters also remain unchanged
If the intermediate value is small, then enter the next recursion. In the parameters passed in the next recursion, the parameter index of the starting position is passed in (the index of the intermediate value + 1), the index of the ending position remains unchanged, and other parameters also remain unchanged.
If the two are equal, it means to find the position of this value in the sequence, record the index of this intermediate value, and look for the same value from this index to both sides (at this time, the sequence is in order, and it is easy to find not have the same value)
if so, also record the indices of these values ​​and return the set of these indices.
As a recursive method body, we have to consider recursion, so in this method body, at the beginning, we have to judge whether another condition for the end of the recursion has been reached, that is, whether the starting position is greater than the end of the recursion at this time If it is greater than the position,
it means that the data you are looking for is not found in this array, and an empty set is returned directly. Here you need to know several situations. When the starting position is equal to the ending position, it means that there is only one data in the sequence at this time. If it is greater than that, it means that there is no data.
insert image description here
The above is the main code of the binary search
There is only a little difference between binary search and difference search, that is, to update the value of the intermediate value and to judge the conditions for exiting recursion on
int mid=left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]);
Baidu Encyclopedia: interpolation search , a search method for ordered tables. Interpolation search is a search method based on the comparison between the search key and the maximum and minimum record keys in the lookup table. Interpolation search is based on binary search, which improves the selection of search points to adaptive selection and improves search efficiency.
The adaptive here refers to his special method of updating the mid value
. In the interpolation search, the adaptive formula of mid is like this: I don’t know where this formula comes from, int mid=(left+right)/2;but int mid=left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]);
it can indeed speed up the search.
In the conditional judgment of ending recursion, it needs to be changed:
insert image description here
One more left=right, this is to prevent arr[right]-arr[left]=the division by 0 exception caused by 0.
Later findVal<arr[0]||findVal>arr[arr.length-1], it means that if the data you are looking for is greater than the largest element of the array or smaller than the smallest element of the array, it will be returned directly. The first can speed up, but it is also the most important The point is to prevent the array index from crossing the bounds, because the data you are looking for is also involved in the operation, preventing the mid from being too large due to the large data you are looking for, and causing the array index to cross the bounds.

I don't know why, but when I was testing, I encountered several cases of dead recursion, and the index of the intermediate value kept looping through a number, or simply looping through a number.

Full code:

import java.util.ArrayList;
import java.util.List;

public class BinarySearch {
    
    
    public static void main(String[] args) {
    
    
        BinarySearchDemo searchDemo=new BinarySearchDemo();
        int[] array={
    
    1,2,3,4,5,6,7,8,9,10,11,12};

        int Array[]=new int[8000];
        for (int i = 0; i < 8000; i++) {
    
    
            Array[i]= (int) (Math.random()*8000);
        }
        QuickSortDemo sortDemo=new QuickSortDemo();
        sortDemo.SortDemo(Array);

        Long time1=System.currentTimeMillis();

        System.out.println(searchDemo.SearchDemo(Array, 452));
        System.out.println("耗时(毫秒)"+(System.currentTimeMillis()-time1));

    }
}
//二分查找方法前提是数列是有序的
//下面假设数列是从小到大排序
//思路:用递归按照所寻找的数值与中间值的大小关系,对数列进行对半分,一直递归到所寻找的数值等于中间值或者数列中的元素给递归没了
//进一步思路:首先一个数列,我们找到数列中间的那个值,用此值和所要寻找的值进行大小比较,若此值更大,则取此值左边的数列再执行前面的操作(即找中间值再和所寻找的值比大小的操作)
//若此值小,则取此值右边的数列进行再进行这样的操作,若两者相等,则表示找到了,则记录此中间值的索引,并从此索引向两边找一下看有没有相同的值(此时数列有序,可很简单的找到是不是有相同值)
//若有,则也记录下这些值的索引然后返回这些索引的集合。若一直没找到,则当数列中没数据了,也结束循环,此时表示未找到
//大致思路:写一个方法,作为递归的方法体,此方法接收四个参数,分别是:原数列(这里我们用数组)、每次递归所需要数列在数组中的起始点和终止点的索引值、要寻找的值
//要两个点的索引值作用就是为了对数列进行中分,我们借由每次递归中,对两点索引传入值的不同来对数列进行对半分。起始终止两点决定了当前的递归是对数组中的那一部分数列进行操作
//在方法体中,我们先获得当前数列的中间值的索引,然后获得中间值,然后用中间值和所要寻找的值进行大小比较,
//若中间值大,则进入下一次递归,下一次的递归传入的参数中,把终止位置的参数索引传入(中间值的索引+1),起始位置索引不变,其他参数也不变
//若中间值小,则进入下一次递归,下一次的递归传入的参数中,把起始位置的参数索引传入(中间值的索引+1),终止位置索引不变,其他参数也不变
//若两者相等,则表示找到此值在数列中的位置,记录下此中间值的索引,并从此索引向两边找一下看有没有相同的值(此时数列有序,可很简单的找到是不是有相同值)
// 若有,则也记录下这些值的索引然后返回这些索引的集合。
//作为一个递归的方法体,我们得为递归进行考虑,所以在此方法体内,一开始,我们得先判断是否达到了递归结束的另一个条件,即是否此时递归中,起始位置是否大于终止位置,
// 若大于则表示在此数组中未找到所要寻找的数据,直接返回一个空集合。这里需要知道几种情况,当起始位置等于终止位置,则表示此时数列中仅有一个数据,若大于,则表示一个数据也没了
class BinarySearchDemo{
    
    
    public List SearchDemo(int[] arr,int findVal){
    
    
        return SearchDemo(arr,0, arr.length-1, findVal);
    }
    private List SearchDemo(int[] arr,int left,int right,int findVal){
    
    
        if (left>=right||findVal<arr[0]||findVal>arr[arr.length-1]){
    
     //如果left>right,说明整个数列已经全部查找过了,而且没有找到,这里直接返回一个空集合
//            这里需要再加一个=号,为了防止除0异常
            return new ArrayList<>();
//            为什么进入这里就表示没找到呢?
//            因为如果找到的话就会在后面那一部分代码中直接return,就不会有left>right的机会了
        }
//        int mid=(left+right)/2;  //更新中间值的索引
        int mid=left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]);
        int midValue = arr[mid];
        if (findVal>midValue){
    
    
            //如果要找寻的值大于中间的那个值,则向右半部分寻找
            return SearchDemo(arr,mid+1,right,findVal);
        }else if (findVal<midValue){
    
    
            //如果要找寻的值小于中间的那个值,则向左半部分寻找
            return SearchDemo(arr,left,mid-1,findVal);
        }else{
    
    //            如果中间值正是寻找的值,则进入这里
            int i=mid; //先记录中间值的索引
            List<Integer> list=new ArrayList<>();  //放置所寻找的值在数列中可能有多个,这里创建一个集合,用来存储所有值的索引
            while ((i>=0)&&(arr[i]==findVal)){
    
      //向找到的索引的左边寻找其他相同的值
                list.add(i);
                i--;
            }
            int n=mid+1;
            while ((n<arr.length)&&(arr[n]==findVal)){
    
      //向找到的索引的右边寻找其他相同的值
                list.add(n);
                n++;
            }
            return list;
        }
    }
}

By the way, I tested the search speed here, QuickSortDemo sortDemo=new QuickSortDemo(); sortDemo.SortDemo(Array);which is the quick sort written in the previous article

Fibonacci lookup

I think Fibonacci search is a very strange thing. It is also called golden section search. As the name suggests, Fibonacci search is to divide the sequence from the golden section point each time, and the rest is the same as binary search. Why? Do you have to divide at the golden section? ? ? I really don't understand it, and during the test, I found that the speed seems to be even slower. In
this search, the most important thing is how to find the point of the golden section.
The Fibonacci sequence is actually a very wonderful sequence. The farther back this series goes, the ratio of the previous value of the series to the next value is infinitely close to the value of the golden section, that is, 0.618... Here, infinitely many decimal places are omitted.
With this feature, we can use the Fibonacci sequence as an aid to easily find the golden section point of the sequence. So, this is also called Fibonacci search

overview

Important: This lookup must also be based on an ordered array

Baidu Encyclopedia: Fibonacci search is divided according to the Fibonacci sequence on the basis of binary search. Find a number F[n] in the Fibonacci sequence that is slightly larger than the number of elements in the lookup table, and expand the original lookup table to a length of F[n] (if you want to add elements, add and repeat the last element until Satisfy F[n] elements), after completion, perform Fibonacci segmentation, that is, F[n] elements are divided into the first half of F[n-1] elements, and the second half of F[n-2] elements, Find out which part the element you're looking for is in and recurse until you find it.
To put it simply:
First of all, this search method is also a binary search, but it is not a middle split. The number of elements between the starting position of the sequence and the split point is closer to the golden section than the number of elements in the entire sequence The value is divided in this way to find the split point. Due to the characteristics of the Fibonacci sequence, you can use the value in the Fibonacci sequence to find the split point, that is, first find a value in the Fibonacci sequence , it is required that this value (that is, F[n]) be greater than or equal to and closest to the number of elements in the array, if F[n] is greater than the number of elements in the array, then expand the original array to a length of F[n] , the expanded position is filled with 0.
Then, because the Fibonacci sequence F[n]=F[n-1]+F[n-2], and, F[n-1]/F[n] is infinitely close to 0.618, which is the golden section value, so F
[ n-1]-1 is the index of the split point of the F[n] sequence. Why -1? Because F[n-1] represents a length, it starts from 1, and the index starts from 0.
Because F[n-2] is the value before F[n-1], so F[n-1] The division point of the sequence is F[n-2]-1
and so on
, but pay attention to one point: we are operating in an array, and the index of the starting position of the sequence that needs to be searched every time is not necessarily 0, the index Each split point is actually起始位置索引+F[n-1]-1

accomplish

Implementation idea: first write a function to create a Fibonacci sequence for subsequent use,
and then write a method body, which is the method body for implementing Fibonacci search. First, use a loop to find the number that is not less than and the closest The value of the Fibonacci sequence of the length of the sequence, and then use the loop to expand the original sequence to the corresponding size
and then officially start the search, use the while loop, the condition for the end of the loop is that there is no data in the sequence, that is, when the starting position is greater than the ending position , or when the result is found,
first find the split point, and then judge the relationship between the size of the split point element and the size of the data you are looking for.
If the split point is larger, look for the number sequence on the left after the split, and update the required Fibonacci The index of the value (used later to find the split point). How to update is detailed in the comments of the following code.
If the split point is smaller, search for the sequence on the right side of the split point, and update the index of the required Fibonacci value (for the subsequent search for the split point). How to update is specifically in the comments of the following code.
The code is as follows:

import java.util.Arrays;

public class FibonacciSearch {
    
    
    public static void main(String[] args) {
    
    
        FibonacciSearchDemo searchDemo=new FibonacciSearchDemo();
        int[] array={
    
    0,1,3,5,7,8,9,10,11,12,45,78,49};
        int Array[]=new int[8000000];
        for (int i = 0; i < 8000000; i++) {
    
    
            Array[i]= (int) (Math.random()*8000000);
        }
        QuickSortDemo sortDemo=new QuickSortDemo();
        sortDemo.SortDemo(Array);

        Long time1=System.currentTimeMillis();
        System.out.println(searchDemo.SearchDemo(Array, 12));
        System.out.println("耗时(毫秒)"+(System.currentTimeMillis()-time1));

    }
}
class FibonacciSearchDemo{
    
    
    public int SearchDemo(int[] arr,int key){
    
    
        int low=0;  //记录数列的起始索引
        int high= arr.length-1;   //记录数列的终止索引
        int[] fib=getFibArr(100);  //接收斐波那契数列
        int k=0;//用来记录所需的斐波那契数值在斐波那契数列中的索引
        int mid;//mid用来记录数列中的中分点

//        在斐波那契数列中寻找一个数值,要求此值不小于数列的长度,且最接近数列的长度
        while (high+1>fib[k]-1){
    
      //为什么要加一?因为hight表示的是最大索引值,他和数列的长度差1
//            为什么fib[k]-1,因为我们要找到那个值不小于数列的长度,而且,是最接近数列的长度的那个值
            k++;
        }

//        数列的原长度可能达不到前面找到的那个数值的大小,这里需要将数列扩增
        int[] temp= Arrays.copyOf(arr,fib[k]);//这个方法可以将arr数组增长到fib[k],增加的元素都默认为0
//        我们需要的是用数列的最后一个元素来填充数组,所以需要修改后面的0
        for (int i = high+1; i < temp.length; i++) {
    
    
            temp[i]=arr[high];
        }

        while (low<=high){
    
    
            mid=low+fib[k-1]-1;//为啥最后面要减一?因为fib[k-1]在此处表示的是一个数量,即从low索引到中分点间的元素个数,而此处我们想要的mid表示的是一个索引
//            这里为啥是fib[k-1]?因为fib[k]表示的是这个数列的长度,所以这个数列的黄金分割点到数列头的长度就是fib[k]的前一个数值,我们这里需要的就是黄金分割点,所以k-1
            if (key<temp[mid]){
    
      //若所寻找的值小于中间值,则进入分割后左边的数列,即F[k-1]的那一部分数列
                high=mid-1;
                k--;
//                为什么这里k自减?因为进入下次循环的时候,从数列头到这个黄金分割点(即f[k-1])的长度,就是我们下次循环时候数列的总长,
//                而这个数列的黄金分割点正是f[k-1]的前一个数值,所以这里我们需要得到的是斐波那契数列中的前一个数值,即fib[k-1]的前一个数值,所以是k--
            }else if (key>temp[mid]){
    
       //若所寻找的值大于中间值,则进入分割后右边的那一部分数列,即F[k-2]的那一部分
                low=mid+1;
                k-=2;
//                这里为什么是k自减2?因为当数列经过黄金分割之后,如果key>temp[mid],即key在经过黄金分割后的数列的后半部分,
//                则我们下次就需要把后半部分的这个数列进行黄金分割找值,如果要对这个数列黄金分割,我们需要先知道这个数列的长度,这个数列的长度就很明显
//                因为斐波那契数列f(n)=f(n-1)+f(n-2),由因为黄金分割后数列后半部分长度不可能大于前半部分长度,所以后半部分长度就是f(n-2)了,
//                f(n-2)的黄金分割点就是这次黄金分割点的上上个数值,即f[k-1]的上上个数值,也就是f[k-1-1-1],所以f[k-1-1]的黄金分割点又是f[k-1-1]的上一个数值
//                所以此处是k自减2
//                明明后面是个减三个1为啥这里只k自减两次?因为,在每次循环开始之前,得到的中间值都是low+fib[k-1]-1,每次用的值不是k而是k-1
            }else {
    
    
                return mid;
            }
        }
        return -1;
    }
    public int[] getFibArr(int n){
    
    
        int[] fib=new int[n];
        fib[0]=0;
        if (n==1){
    
    
            return fib;
        }
        fib[1]=1;
        if (n==2){
    
    
            return fib;
        }
        for (int i = 2; i < n; i++) {
    
    
            fib[i]=fib[i-1]+fib[i-2];
        }
        return fib;
    }
}

By the way, here is a test of the speed of searching in 8 million data, which QuickSortDemo sortDemo=new QuickSortDemo(); sortDemo.SortDemo(Array);is the quick sort written in the previous article

Guess you like

Origin blog.csdn.net/qq_45821251/article/details/121251821