• 从 n个长度的序列中找出前 m大个元素


    1. 基本思路

    方法一
      利用优先队列的特性(堆序), 在构建出 max堆(大顶堆)之后, 不断将堆顶的元素移除, 就能很轻松的获取前 m个最大的元素.

    方法二
      对第一种方法的优化, 维护一个含有 m个元素的序列, 在对原始数列进行扫描, 动态调整目标序列, 一次扫描结束之后, 前 m个元素也就找到了.

    2. 代码演示

    public class SortDemo {
    
        public static void main(String[] args) {
            int[] arr = new int[] {1, 2, 3, 4, 5, 7, 8, 10};
            printArr(findMaxM(arr, 3));
            printArr(findMaxM_2(arr, 3));
        }
    
        // 方法一: 
        //     从给定序列中找出前 m个最大的元素  主例程
        private static int[] findMaxM(int[] arr, int m) {
    
            int[] result = new int[m];
    
            // build heap
            buildMaxHeap(arr);
    
            // delete max
            int count = 0;
            for (int i = arr.length - 1; i > 0 && count < m; i--) {
                swap(arr, 0, i);
                percolateDown(arr, 0, i);
                count++;
            }
    
            System.arraycopy(arr, arr.length - m, result, 0, m);
            return result;
        }
    
        // 方法二: 
        //     维护一个 m个大小的堆, 在对原始数组的扫描中, 动态调整堆, 扫描完成即找到前 m个元素 
        private static int[] findMaxM_2(int[] arr, int m) {
    
            int[] result = new int[m];
    
            // 从原始数组中取前 m个元素, 用于构建初始堆
            System.arraycopy(arr, 0, result, 0, result.length);
            buildMaxHeap(result);
    
            // 遍历过程中, 如果当前元素比堆中最小值大的话, 就将其替换掉, 然后调整位置以维持堆序
            for (int i = m; i < arr.length && findMin(result) > arr[i]; i++) {
                result[findMinIndex(result)] = arr[i];
                buildMaxHeap(result);
            }
    
            // 对堆中元素进行排序
            // 起始这一步也可以不要, 并没有要求前 m大个元素的顺序
            deleteMax(result);
    
            return result;
        }
    
        private static int findMin(int[] arr) {
            int min = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (min > arr[i]) {
                    min = arr[i];
                }
            }
            return min;
        }
    
        private static int findMinIndex(int[] arr) {
            int index = 0;
            int min = arr[index];
            for (int i = 1; i < arr.length; i++) {
                if (min > arr[i]) {
                    index = i;
                    min = arr[index];
                }
            }
            return index;
        }
    
        // build max heap
        private static void buildMaxHeap(int[] arr) {
            for (int i = arr.length / 2; i >= 0; i--) {
                percolateDown(arr, i, arr.length);
            }
        }
    
        // delete max
        private static void deleteMax(int[] arr) {
            for (int i = arr.length - 1; i > 0; i--) {
                swap(arr, 0, i);
                percolateDown(arr, 0, i);
            }
        }
    
        // 下滤操作
        private static void percolateDown(int[] arr, int i, int n) {
    
            int child;    // 保存与父节点产生交换操作的 子节点的索引
            int tmp;      // 保存被调整位置元素的值
    
            // tmp记录被调整元素的值, i的值随 child不断改变, 直到比较完所有的子节点 (leftChild(i) < n) 为止
            for (tmp = arr[i]; leftChild(i) < n ; i = child) {
                child = leftChild(i);
    
                // child == n - 1时, 其左子节点为堆的最后一个元素, 没有右节点, 无须比较
                // 将该节点与其左右节点比较, 记录其中最小的节点的索引
                if (child != n - 1 && arr[child] < arr[child + 1]) {
                    child++;
                }
    
                // 将需要被调整的节点与其子节点进行比较, 如果小于子节点, 当前节点的值替换为子节点的值 (注意不是交换)
                if (tmp < arr[child]) {
                    arr[i] = arr[child];
                } else {
                    break;
                }
            }
    
            // 找到合适的位置后, 直接赋值来避免多余的交换操作
            // 注意 i值的变化, 在调整位置过程中, 如果改变了子序列的堆序, 也要立即调整过来
            arr[i] = tmp;
        }
    
        // 获取指定节点处元素的左节点索引
        private static int leftChild(int i) {
            return 2 * i + 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;
        }
    
        // 打印数组元素
        private static void printArr(int[] arr) {
            System.out.println(Arrays.toString(arr));
        }
    }
  • 相关阅读:
    C#中List<T>用法
    windows phone中,将crash report记录下来,写入文件,方便分析
    解决问题之,wp项目中使用MatchCollection正则表达式匹配出错
    提问的智慧
    toolkit,phonetextbox中实现用户按回车键会换行
    Hibernate主键生成策略
    hibernate.cfg.xml位置及JDBC配置
    Java与数字签名
    MyEclipse不能编译的一种解决方法
    java读文件和写文件编码方式的控制
  • 原文地址:https://www.cnblogs.com/bobo132/p/13950344.html
Copyright © 2020-2023  润新知