数据结构Java——几种查找方式(顺序查找,二分查找,插值查找,斐波那契查找)

顺序查找

使用for循环来依次遍历,实现查找

二分查找

二分查找,又称为折半查找,查找的效率很高

算法思想

给定一串有序的序列,以这串序列的中间元素为基准,
若要查找元素 > 中间位置元素,说明要查找元素在序列前半部分
若要查找元素 < 中间位置元素,说明要查找元素在序列后半部分
这样使得每次查找的范围都将减少一半

算法优缺点

  • 优点:查找的次数较少,查找速度快,平均性能好

  • 缺点:要求查找的序列必须为有序序列

适用序列

有序序列,且查找频繁,变动较少的序列

时间复杂度

最坏的情况时间复杂度为O(log2 N)
最好的情况时间复杂度为O(1)

空间复杂度

非递归实现的空间复杂度为O(1)
递归实现的空间复杂度为O(log2 N)

代码实现

  • 递归实现
package checkHalf;

/**
 * @author LXY
 * @email [email protected]
 * @date 2018/8/15 10:37
 *
 * 递归版本二分查找
 */

/**
 * arr[]为查找的范围数组
 * key为要查找元素
 * start为查找的起点
 * end为查找的终点
 */
public class recursionBinarySearch {
    public static int recursionBinarySearch(int[] arr,int key,int start,int end)
    {
        if(key < arr[start] || key > arr[end - 1] || start > end)
        {
            return -1;
        }
        if(arr[start] == key)
        {
            return start;
        }
        int mid = start + (end - start) / 2;
        if(arr[mid] > key)
        {
            //说明key在前半部分
            return recursionBinarySearch(arr,key,start,mid - 1);
        }
        else if(arr[mid] < key)
        {
            //说明key在前半部分
            return recursionBinarySearch(arr,key,mid + 1,end);
        }
        else
        {
            return mid;
        }
    }

    public static void main(String[] args) {
        int[] arr = {1,2,4,5,6,7,8,9};
        System.out.println(recursionBinarySearch(arr,9,0,arr.length));
    }
}
  • 非递归实现
package checkHalf;

/**
 * @author LXY
 * @email [email protected]
 * @date 2018/8/15 10:53
 *
 * 非递归版本二分查找
 */
public class commonBinarySearch {
    //可以没有start和end
    public static int commonBinarySearch(int[] arr,int key)
    {
        int start = 0;
        int end = arr.length - 1;
        int mid = 0;
        if(arr[start] == key)
        {
            return start;
        }
        if(arr[end] == key)
        {
            return end;
        }
        while (start < end)
        {
            mid = start + (end - start) / 2;
            if(key < arr[mid])
            {
                end = mid - 1;
            }
            else if(key > arr[mid])
            {
                start = mid + 1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = {1,2,4,5,6,7,8,9};
        System.out.println(commonBinarySearch(arr,0));
    }
}

插值查找

差值查找是二分查找的改进版本

算法思想

上面介绍了二分查找的算法思想,关键在于每次都将查找范围缩小一半,但是这种二分查找的算法有些过于死板,查找有些过于被动
比如:如果在1~1000中要查找5的位置,要是按照二分查找的方式,那么要要查找的次数会很多,那么大多数人的第一反映一定是从开头开始查找,这就是差值查找的思想,不是根据中间位置来分区,而是根据离所求值的距离来进行搜索
对于二分查找:mid=(start+end)/2, 即mid=start+1/2*(end-start);
对于插值查找:mid=start+(key-a[start])/(arr[end]-arr[start])*(end-start);

算法优缺点

  • 优点:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找的性能比折半查找要好很多
  • 缺点:要求查找的序列必须为有序序列,且数组中元素要分布均匀

适用序列

有序序列,且查找频繁,变动较少的序列

时间复杂度

时间复杂度为O(log2 (log2 N))

代码实现

代码与二分查找类似,不同在于对于分界点的不同
- 递归实现

package chazhi;

/**
 * @author LXY
 * @email [email protected]
 * @date 2018/8/15 11:37
 *
 * 递归版本插值查找
 */
public class InsertValueSearch {
    public static int insertValueSearch(int[] arr,int key,int start,int end)
    {
        if(key < arr[start] || key > arr[end] || start > end)
        {
            return -1;
        }
        if(arr[start] == key)
        {
            return start;
        }
        if(arr[end] == key)
        {
            return end;
        }
        int mid = start+(end-start)*((key-arr[start])/(arr[end]-arr[start]));
        if(arr[mid] > key)
        {
            //说明key在前半部分
            return insertValueSearch(arr,key,start,mid - 1);
        }
        else if(arr[mid] < key)
        {
            //说明key在前半部分
            return insertValueSearch(arr,key,mid + 1,end);
        }
        else
        {
            return mid;
        }
    }
    public static void main(String[] args) {
        int[] arr = {1,2,4,5,6,7,8,9};
        System.out.println(insertValueSearch(arr,10,0,arr.length - 1));
    }
}
  • 非递归实现
package chazhi;

/**
 * @author LXY
 * @email [email protected]
 * @date 2018/8/15 10:53
 *
 * 非递归版本插值查找
 */
public class commonInsertValueSearch {
    //可以没有start和end
    public static int commonInsertValueSearch(int[] arr,int key)
    {
        int start = 0;
        int end = arr.length - 1;
        int mid = 0;
        if(arr[start] == key)
        {
            return start;
        }
        if(arr[end] == key)
        {
            return end;
        }
        while (start < end)
        {
            mid = start+(end-start)*((key-arr[start])/(arr[end]-arr[start]));
            if(key < arr[mid])
            {
                end = mid - 1;
            }
            else if(key > arr[mid])
            {
                start = mid + 1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = {1,2,4,5,6,7,8,9};
        System.out.println(commonInsertValueSearch(arr,3));
    }
}

斐波那契查找

斐波那契查找是二分查找的改进版本

算法思想

斐波那契查找是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=F(k)-1;开始将k值与第F(k-1)位置的记录进行比较(即mid=low+F(k-1)-1),比较结果也分为三种

  • k = mid,mid位置的元素即为所求
  • k > mid,low=mid+1,k-=2;
    说明:low=mid+1说明待查找的元素在[mid+1,high]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找。
  • k < mid,high=mid-1,k-=1。
    说明:low=mid+1说明待查找的元素在[low,mid-1]范围内,k-=1 说明范围[low,mid-1]内的元素个数为F(k-1)-1个,所以可以递归 的应用斐波那契查找。

时间复杂度

最坏情况的时间复杂度为O(log2 N)
期望的时间复杂度也为O(log2 N)

代码实现

package fibonacci;

/**
 * @author LXY
 * @email [email protected]
 * @date 2018/8/15 19:11
 */
public class FibonacciSearch {
    static int FibonacciSearch(int[] a, int n, int key) {
        int[] F = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34};
        int low = 1;
        int high = n;
        int k = 0;
        int mid = 0;
        while (n > F[k] - 1) /* 计算n位于斐波那契数列的位置 */
            k++;
        while (low <= high) {
            mid = low + F[k - 1] - 1;
            if (key < a[mid]) {
                high = mid - 1;
                k = k - 1;
            } else if (key > a[mid]) {
                low = mid + 1;
                k = k - 2;
            } else {
                if (mid <= n)
                {
                    return mid;
                }
                else
                {
                    return n;
                }
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        System.out.println(FibonacciSearch(arr,arr.length,5));
        System.out.println(FibonacciSearch(arr,arr.length,1));
        System.out.println(FibonacciSearch(arr,arr.length,9));

    }
}

猜你喜欢

转载自blog.csdn.net/L_X_Y_HH/article/details/81701199