常用的经典查找算法【Java实现】

一、线性查找

直接遍历比较,找到则返回其所有位置索引

代码如下:

public class LinearSearch {
    public static void main(String[] args) {
        int[] arr = new int[]{9, 3, 45, -2, 45, 45, 7, -23};
        List<Integer> list = linearSearch(arr, 45);
        if (list.size() == 0) {
            System.out.println("未找到对应元素!");
        } else {
            System.out.println("找到对应元素的索引:" + list);
        }
    }

    /**
     * 线性查找
     *
     * @param arr   数据集合
     * @param value 被查找的数据
     * @return 返回在数据集合中找到的被查找数据所有索引的集合
     */
    public static List<Integer> linearSearch(int[] arr, int value) {
        List<Integer> list = new ArrayList<>();
        //将要查找的值与数组中的元素一个个比较
        for (int i = 0; i < arr.length; i++) {
            //如果找到则返回索引
            if (arr[i] == value) {
                list.add(i);
            }
        }
        return list;
    }
}

测试结果:

找到对应元素的索引:[2, 4, 5]

二、二分查找

前提条件:
	数据集合必须有序
先取中值,如果中值等于被查找的数,则:
	判断当前值左边及右边是否还有相等的数
	将所有相等数的索引放入集合
如果中值小于被查找的数,则向右找,继续取中值
如果中值大于被查找的数,则向左找,继续取中值

代码1 如下(非递归方式):

public class BinarySearch2 {
    public static void main(String[] args) {
        //初始化一个有序的数据集合
        int[] arr = {-23, -2, 3, 7, 7, 7, 7, 7, 7, 9, 45};
        List<Integer> list = binarySearch(arr, 7);
        if (list.size() == 0) {
            System.out.println("未找到对应元素!");
        } else {
            System.out.println("找到对应元素的索引:" + list);
        }
    }

    /**
     * 二分查找(非递归方式)
     *
     * @param arr   有序的数据集合
     * @param value 被查找的数据
     * @return 存放被查找数据索引的集合
     */
    public static List<Integer> binarySearch(int[] arr, int value) {
        int left = 0;
        int right = arr.length - 1;
        int mid = (left + right) / 2;
        List<Integer> list = new ArrayList<>();
        while (arr[mid] != value) {
            //如果中间索引值大于查找值
            //则向左查找,将右侧索引置为中间索引减 1
            if (arr[mid] > value) {
                right = mid - 1;
            }
            //如果中间索引值小于查找值
            //则向右查找,将左侧索引置为中间索引加 1
            if (arr[mid] < value) {
                left = mid + 1;
            }
            //当左侧索引大于右侧索引时
            //则说明遍历完整个数组,仍未找到被查找值
            //则返回空的集合
            if (left > right) {
                return list;
            }
            //将中间索引重置为新的中间索引
            mid = (left + right) / 2;
        }
        //如果退出循环,则表示已经找到第一个索引,且保存在 mid 中
        //此时继续找(数据集合中可能还有其他位置有该值)
        //初始化向左遍历索引
        int temp = mid - 1;
        //从中间索引向左遍历
        //如果在数据集合中还有该值,则将索引放入集合
        while (temp > 0 && arr[temp] == value) {
            list.add(temp--);
        }
        //将最初查找到的索引放入集合
        list.add(mid);
        //初始化向右遍历索引
        temp = mid + 1;
        //从中间索引向右遍历
        //如果在数据集合中还有该值,则将索引放入集合
        while (temp < arr.length && arr[temp] == value) {
            list.add(temp++);
        }
        //返回存放索引的集合
        return list;
    }
}

代码2 如下(递归方式):

public class BinarySearch {
    public static void main(String[] args) {
        //初始化一个有序的数据集合
        int[] arr = {-23, -2, 3, 7, 7, 7, 7, 7, 7, 9, 45};
        List<Integer> list = binarySearch(arr, 7);
        if (list.size() == 0) {
            System.out.println("未找到对应元素!");
        } else {
            System.out.println("找到对应元素的索引:" + list);
        }
    }

    /**
     * 二分查找(方便传参)
     *
     * @param arr   有序的数据集合
     * @param value 被查找的数据
     * @return 存放被查找数据索引的集合
     */
    public static List<Integer> binarySearch(int[] arr, int value) {
        return binarySearch(arr, 0, arr.length - 1, value);
    }

    /**
     * 二分查找(递归方式)
     *
     * @param arr   有序的数据集合
     * @param left  数据集合左侧索引
     * @param right 数据集合右侧索引
     * @param value 被查找的数据
     * @return 存放被查找数据索引的集合
     */
    public static List<Integer> binarySearch(int[] arr, int left, int right, int value) {
        //如果左侧索引大于右侧索引
        //则说明遍历整个数据集合,没有找到对应的值
        if (left > right) {
            //返回一个空的集合
            return new ArrayList<>();
        }
        //数据集合中间索引
        int mid = (left + right) / 2;
        //如果中间索引对应的值大于被查找的值
        if (arr[mid] > value) {
            //则递归向左查找
            return binarySearch(arr, left, mid - 1, value);
            //如果中间索引的值小于被查找的值
        } else if (arr[mid] < value) {
            //则递归向右查找
            return binarySearch(arr, mid + 1, right, value);
            //如果执行到 else,则表示已经在数据集合中找到被查找的值
        } else {
            //初始化存放索引的集合
            List<Integer> list = new ArrayList<>();
            //初始化向左遍历索引
            int temp = mid - 1;
            //从中间索引向左遍历
            //如果在数据集合中还有该值,则将索引放入集合
            while (temp > 0 && arr[temp] == value) {
                list.add(temp--);
            }
            //将最初查找到的索引放入集合
            list.add(mid);
            //初始化向右遍历索引
            temp = mid + 1;
            //从中间索引向右遍历
            //如果在数据集合中还有该值,则将索引放入集合
            while (temp < arr.length && arr[temp] == value) {
                list.add(temp++);
            }
            //返回存放索引的集合
            return list;
        }
    }
}

测试结果:

找到对应元素的索引:[4, 3, 5, 6, 7, 8]

三、差值查找

二分查找存在的问题:
	对于数据集合中数据比较连续,且被查找的元素在数据集合的开头或者结尾,效率会较低
示例:
	数据集合:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
	被查找数据:1
	如果使用二分查找
		mid = 1/2(left+right0.2)=9
		第一次查找数据 arr[9] = 10
		向左递归四次才能找到数据 1

可以将二分查找的中间索引公式由 
	mid = left + (left+right) * 1/2
修改为
	mid = left + (right-left) * (value-arr[left]) / (arr[right]-arr[left])
即:将 1/2 替换为 (value-arr[left]) / (arr[right]-arr[left])
将每次从中间开始查找替换为从自适应 mid 开始查找,提升效率

示例:
	数据集合:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
	被查找数据:1
	如果使用差值查找
		mid = left +(right-left) * (value-arr[left]) / (arr[right]-arr[left])
			   = 0 + (99-0) * (1-arr[0]) / (arr[99]-arr[0])
			   = 0 + (99-0) * (1-1) / (100-1) 
			   = 0
		arr[0] = 1
	一轮即可找到

对于数据量较大、关键字分布比较均匀的查找表来说,采用差值查找,速度较快
对于关键字分布不均匀(跳跃性很大)的情况,该方法不一定比二分查找效率高

代码如下:

public class InsertValueSearch1 {
    public static void main(String[] args) {
        //初始化一个连续的有序数据集合
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
        int index = insertValueSearch(arr, 1);
        if (index == -1) {
            System.out.println("未找到对应元素!");
        } else {
            System.out.println("找到对应元素的索引:" + index);
        }
    }

    /**
     * 差值查找(方便传参)
     *
     * @param arr   有序的数据集合
     * @param value 被查找的数据
     * @return 存放被查找数据索引的集合
     */
    public static int insertValueSearch(int[] arr, int value) {
        return insertValueSearch(arr, 0, arr.length - 1, value);
    }

    /**
     * 差值查找(递归方式)
     *
     * @param arr   有序的数据集合
     * @param left  数据集合左侧索引
     * @param right 数据集合右侧索引
     * @param value 被查找的数据
     * @return 存放被查找数据索引的集合
     */
    public static int insertValueSearch(int[] arr, int left, int right, int value) {
        //如果左侧索引大于右侧索引
        //则说明遍历整个数据集合,没有找到对应的值
        //后续两个判断是为了防止数组越界异常
        if (left > right || value < arr[0] || value > arr[arr.length - 1]) {
            //返回一个空的集合
            return -1;
        }
        //数据集合中间索引
        int mid = left + (right - left) * (value - arr[left]) / (arr[right] - arr[left]);
        //如果中间索引对应的值大于被查找的值
        if (arr[mid] > value) {
            //则递归向左查找
            return insertValueSearch(arr, left, mid - 1, value);
            //如果中间索引的值小于被查找的值
        } else if (arr[mid] < value) {
            //则递归向右查找
            return insertValueSearch(arr, mid + 1, right, value);
            //如果执行到 else,则表示已经在数据集合中找到被查找的值
        } else {
            //返回存放索引的集合
            return mid;
        }
    }
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值