一种选择数组中第 i 小元素的线性时间算法

介绍一种解决选择问题的分治算法,该算法从一个有n个元素的数组中选择第i(i为[1…n])小的元素。

本算法以快速排序算法为模型。与快速排序一样,仍然将输入的数组进行递归划分。假设要从数组A[start…end]中找出第 i 小的元素,算法的运行过程如下:

  1. 检查递归的基本情况,如果数组中只包含一个元素,直接返回即可;
  2. 对数组进行分区,将数组划分为两个子数组A[start…mid-1]和A[mid+1…end],使得A[start…mid-1]中的每一个元素都小于等于A[mid],A[mid+1…end]中的每一个元素都大于A[mid];
  3. 计算A[start…mid]中的元素个数k;
  4. 检查k是否等于i,如果等于,则A[mid]是第 i 小的元素,如果不是,则检查 i 是否小于k,如果小于,则在A[start…mid-1]中递归查找;如果大于,则在A[mid+1…end]中递归查找。

以在数组[4,2,1,5,2]中查找第3小的元素为例,数组变化的示意图如下:
一种选择数组中第 i 小元素的线性时间算法_第1张图片
根据算法实现思路,java代码实现如下:

    /**
     * 一种从输入数组中选出第order小元素的分治算法,渐进运行时间为θ(n)
     *
     * @param nums  输入数组
     * @param start 数组起始下标
     * @param end   数组结束下标
     * @param order 选出元素的排序
     * @return 第order小的元素
     */
    public int randomizedSelect(int[] nums, int start, int end, int order) {
        if (start == end) {
            return nums[start];
        }

        // 寻找分治的中点索引,与快速排序的分区思路一样
        int key = nums[end];
        int i = start - 1;
        for (int j = start; j < end; j++) {
            if (nums[j] <= key) {
                i++;
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
            }
        }
        nums[end] = nums[i + 1];
        nums[i + 1] = key;
        int mid = i + 1;

        // 计算小于等于nums[mid]的元素个数
        int countStartToMid = mid - start + 1;
        // 如果小于等于nums[mid]的元素个数与order相等,则nums[mid]是要寻找的元素
        if (countStartToMid == order) {
            return nums[mid];
        } else if (order < countStartToMid) {
            return randomizedSelect(nums, start, mid - 1, order);
        } else {
            return randomizedSelect(nums, mid + 1, end, order - countStartToMid);
        }
    }

你可能感兴趣的:(算法,算法,java)