• 找出一个序列中第k大的元素 [快速选择问题]


    1. 基本思路

    采用快速排序的方法, 不断在子序列中寻找符合条件的值, 不同于快排的是, 寻找第 k大的元素, 每次只需要对一边的数据进行排序即可.

    2. 代码演示

    public class SortDemo {
    
        public static void main(String[] args) {
            int[] arr = new int[] {1, 11, 2, 25, 36, 3, 14, 23, 6, 5, 9, 7, 10};
            int k = 3;
            System.out.println("第 " + k + "大的元素是: " + findMaxN(arr, k));
        }
    
        // 找出序列中第 k大的元素值
        private static int findMaxN(int[] arr, int k) {
            if (k > arr.length) {
                throw new RuntimeException("k的值不可以大于序列长度");
            }
            return findMaxN(arr, 0, arr.length - 1, k);
        }
    
        // 寻找最k大的元素 主例程
        private static int findMaxN(int[] arr, int left, int right, int k) {
    
            // 三数中值法, 获取枢纽元 (基准值)
            int pivot = median3(arr, left, right);
    
            // 如果子序列长度为 1 or 2 or 3
            // 则不需要走下列步骤, 因为此时子序列已经是有序的了 median3()方法中有排序
            int size = right - left + 1;
            if (size == 1 || size == 2 || size == 3) {
                return arr[arr.length - k];
            }
    
            // 快速排序主例程
            int i = left;
            int j = right - 1;
    
            for (; ; ) {
                while (arr[++i] < pivot) {}
                while (arr[--j] > pivot) {}
                if (i < j) {
                    swap(arr, i, j);
                } else {
                    break;
                }
            }
            // 将枢纽元换回中间的位置
            swap(arr, i, right - 1);
    
            // 根据 k的范围, 决定对哪一部分(枢纽元左边还是右边)的元素进行再次交换
            if (k <= i) {
                findMaxN(arr, i + 1, right, k);
            } else if (k > i + 1) {
                findMaxN(arr, left, i - 1, k);
            }
    
            return arr[arr.length - k];
        }
    
        /**
         * 三数中值分割法:
         *    在序列首部, 末尾和中间三个元素中, 选择值处于中间的元素, 并返回其值
         *    交换首部, 中间和尾部的值, 维持有序性.
         */
        private static int median3(int[] arr, int left, int right) {
    
            int size = right - left + 1;
    
            if (size < 1) {
                throw new RuntimeException("子序列长度至少为 1");
            }
            if (size == 1) {
                return arr[left];
            }
            if (size == 2) {
                if (arr[right] < arr[left]) {
                    swap(arr, left, right);
                }
                return arr[left];
            }
    
            int center = (right + left) / 2;
    
            if (arr[center] < arr[left]) {
                swap(arr, left, center);
            }
            if (arr[right] < arr[left]) {
                swap(arr, left, right);
            }
            if (arr[right] < arr[center]) {
                swap(arr, center, right);
            }
    
            // 将中值放到尾部之前的元素, 便于前面元素索引的移动
            swap(arr, center, right - 1);
    
            // 返回中值
            return arr[right - 1];
        }
    
    
        // 交换数组中指定索引处的元素位置
        private static void swap(int[] arr, int i, int j) {
            if (i > arr.length - 1 || i < 0 || j > arr.length - 1 || j < 0) {
                throw new RuntimeException("索引值有误");
            }
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
  • 相关阅读:
    IDEA快捷的添加包名
    Spring源码(一)
    Java的理解
    剪绳子
    机器人的运动范围
    矩阵中的路径
    N皇后问题
    回溯法
    全排列
    反转链表
  • 原文地址:https://www.cnblogs.com/bobo132/p/13950348.html
Copyright © 2020-2023  润新知