Java与数据结构——查找算法

在 java 中,我们常用的算法有四种:顺序(线性)查找、二分(折半)查找、插值查找、斐波那契查找。

线性查找:遍历数组,如果找到了就提示找到,并给出下标值。数组可以是有序也可以是无序的。

代码实现:

package search;

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

public class SeqSearch {
    public static void main(String[] args) {
        int[] arr = { 1, 9, 2, 6, 11, -2, 2 };
        int index1 = seqSearch(arr,3);
        int[] index2 = seqSearch2(arr,3);
        if (index1 == -1 ){
            System.out.println("没有查找到对应值。");
        }else {
            System.out.println("对应值的索引为:" + index1);
        }
        if (index2.length == 0){
            System.out.println("没有查找到对应值。");
        }else {
            System.out.println("对应值的索引为:" + Arrays.toString(index2));
        }
    }
//返回第一个结果
    public static int seqSearch(int[] arr, int value){
        //线性查找就是逐一比对,发现有相同值,就返回下标
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == value)
                return i;
        }
        return -1;//没有找到就返回 -1
    }

//返回多个结果
    public static int[] seqSearch2(int[] arr, int value){
        //线性查找就是逐一比对,发现有相同值,就返回下标
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == value)
                list.add(i);
        }
        if (list == null){
            return null;
        }
        int[] res = new int[list.size()];
        int index = 0;
        for (int i = 0; i < list.size(); i++) {
            res[ index++ ] = list.get(i);
        }
        return res;
    }
}

二分查找

思路分析:

1. 首先确定该数组中间的下标     mid = (left + right) / 2

2. 然后让需要查找的数 findVal 和 arr[ mid ] 进行比较

        2.1 findVal > arr[ mid ] ,说明你要查找的数在 mid 的右边,因此需要递归地向右查找;

        2.2 findVal < arr[ mid ] ,说明你要查找的数在 mid 的左边,因此需要递归地向左查找;

        2.3 findVal == arr[ mid] ,说明找到,就返回

注意:什么时候结束递归:1. 找到目标值就结束递归;2. 递归完整个数组,仍然没有找到 findVal,即 left > right 时就需要退出递归。

代码实现:

package search;

public class BinarySearch {
    public static void main(String[] args) {
        int[] arr = { 2,6,9,13,17,22,29,35,46,59,63,77 };//二分查找需要有序
        int targetVal = 65;
        int index = binarySearch(arr,0,arr.length-1, targetVal);
        int index2 = binarySearch2(arr,targetVal);
        if (index == -1){
            System.out.println("递归版二分查找:未查找到该值!");
        }else {
            System.out.println("递归版二分查找 " + targetVal + " 对应的索引值为:" + index);
        }
        if (index2 == -1){
            System.out.println("循环版二分查找:未查找到该值!");
        }else {
            System.out.println("循环版二分查找 " + targetVal + " 对应的索引值为:" + index2);
        }
    }
    //二分查找递归版本
    public static int binarySearch(int[] arr, int left, int right, int targetVal){
        //判断 left > right ? 结束递归
        if (left > right) {
            return -1;
        }
        int mid = (left + right) / 2;
        int midValue = arr[mid];
        if (targetVal > midValue){//向右递归
            return binarySearch(arr,mid+1,right,targetVal);
        }else if (targetVal < midValue){
            return binarySearch(arr,left,mid-1,targetVal);
        }else {
            return mid;
        }
    }
    //二分查找循环版本
    public static int binarySearch2(int[] arr,int targetVal){
        int left = 0;
        int right = arr.length;
        while (left <= right){
            int mid = (left + right)/2;
            if (targetVal > arr[mid])
                left = mid + 1;
            else if (targetVal < arr[mid])
                right = mid - 1;
            else
                return mid;
        }
        return -1;
    }
}

 

升级版:如果一个有序数组中有多个相同的数值时,如何将所有的数值都查找到并返回呢?

思路分析:

1. 当找到 mid 索引值时,不要马上返回;

2. 向 mid 索引值的左边扫描,将所有等于目标值的元素的下标加入到集合 ArrayList;

3. 向 mid 索引值的右边扫描,将所有等于目标值的元素的下标加入到集合 ArrayList;

4. 将 ArrayList 返回。

    //二分查找升级版
    public static ArrayList<Integer> binarySearch3(int[] arr, int left, int right, int targetVal){
        ArrayList<Integer> indexList = new ArrayList<>();
        //判断 left > right ? 结束递归
        if (left > right) {
            return new ArrayList<>();
        }
        int mid = (left + right) / 2;
        int midValue = arr[mid];
        if (targetVal > midValue){//向右递归
            return binarySearch3(arr,mid+1,right,targetVal);
        }else if (targetVal < midValue){
            return binarySearch3(arr,left,mid-1,targetVal);
        }else {
            int index = mid;
            while (arr[index] == targetVal && index >= 0){
                indexList.add(0,index);
                index--;
            }
            index = mid + 1;
            while (arr[index] == targetVal && index < arr.length){
                indexList.add(index);
                index++;
            }
            return indexList;
        }
    }

 

插值查找

原理介绍:

(1)插值查找算法类似于二分查找,不同的是插值查找每次从自适应 mid 处开始查找;

(2)将二分查找中的求 mid 的公式进行变形,targetValue 即为查找的值,

(3)int midIndex = low + (high - low)*(targetValue - arr[low]) / (arr[high] - arr[low]);

(4)插值查找算法的举例说明

数组 arr  = [1, 2, 3, ..., 100],加入查找 1 或 100 

二分查找需要多次递归才能找到目标值,而使用插值查找算法则有

int midIndex = low + (high - low)*(targetValue - arr[low]) / (arr[high] - arr[low]);

int mid = 0 + (99 - 0) * (1-1)/(100 - 1) = 0 + 99 * 0 / 99 = 0;

int mid = 0 + (99 - 0) * (100 - 1) / (100 - 1) = 0 + 99 * 99 / 99 = 99

这样的情况下,插值查找的效率就会高得多。

注意:对于数据量较大,关键字分布较均匀的查找表而言,采用插值查找速度较快,对于关键字分布不均匀的情况,该方法不一定比折半查找效果好。

代码实现:

package search;

import java.util.Arrays;

public class InsertSearch {
    public static void main(String[] args) {
        int[] arr = new int[100];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i + 1;
        }
        System.out.println(Arrays.toString(arr));
        int targetValue = 98;
        int index = insertSearch(arr,targetValue);
        if (index == -1){
            System.out.println("插值查找未能查找到该值!");
        }else {
            System.out.println("插值查找:目标值 " + targetValue + " 的索引值为:" + index);
        }
        int index2 = binarySearch2(arr,targetValue);
        if (index2 == -1){
            System.out.println("二分查找未能查找到该值!");
        }else {
            System.out.println("二分查找:目标值 " + targetValue + " 的索引值为:" + index2);
        }
    }
    //插值查找算法也要求数组有序
    public static int insertSearch(int[] arr, int targetValue){
        int left = 0, right = arr.length - 1, count = 0;
        if (targetValue < arr[left] || targetValue > arr[right])//防止 targetValue 越界
            return -1;
        while (left <= right){
            int mid = left + (right - left) * (targetValue - arr[left]) / (arr[right] - arr[left]);
            count++;
            if (targetValue > arr[mid]){
                left = mid + 1;
            }else if (targetValue < arr[mid]){
                right = mid - 1;
            }else {
                System.out.println("插值查找次数为" + count);
                return mid;
            }
        }
        System.out.println("插值查找次数为" + count);
        return -1;
    }
    //二分查找循环版本
    public static int binarySearch2(int[] arr,int targetVal){
        int left = 0;
        int right = arr.length;
        int count= 0;
        while (left <= right){
            int mid = (left + right)/2;
            count++;
            if (targetVal > arr[mid])
                left = mid + 1;
            else if (targetVal < arr[mid])
                right = mid - 1;
            else {
                System.out.println("二分查找次数为" + count);
                return mid;
            }
        }
        System.out.println("二分查找次数为" + count);
        return -1;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值