zoukankan      html  css  js  c++  java
  • 在上亿级别的数当中找前1000个数

    方法1:

      对于这道题第一个想到的方法可能是把所有的数都排好序,然后从所有的数中取前1000个数。

      但是对于亿级别的数排序可不是一件容易的事,所以这个方法肯定行不通。

    方法2:

      采用分治的思想,在这些数中随便选择一个数,然后以这个数为界限,把所有的数分成两堆,左边的部分都大于这个数,右边的部分都小于这个数。

      如果左边的数的个数大于1000,那么便可以从左边的数进行查找,

      如果左边的数的个数小于1000,那么就从右边的数中在随机选择一个数,再把数分成两堆,从剩下的数中找出(1000-上一次左边的数的个数)这么多个数字。

      依次进行下去,直到找齐1000个数。

      这样做的时间复杂度我们可以计算一下:

      第一次排序所要用的时间为O(n),以后每次排序的时候数据量都减半,时间记为n/2,所有的时间加起来为n+n/2+n/4+...,最后所得的时间肯定小于2n

      所以用这种方法的时间复杂度为n

      但是空间复杂度呢???

      因为数据量过大,我们无法将所有的数据一次性读到内存中进行排序,所以第一次排序时可能造成内存溢出的情况。

    方法3:

      采用二叉堆的方法,关于二叉堆,可以在我的  https://www.cnblogs.com/rao11/p/11976960.html  博客中查看代码。

      因为二叉堆中的小顶堆的顶元素一定时所有元素中最小的,所以可以让这上亿个数中的前1000个组成一个小顶堆,然后用这个1000个数在内存中进行计算,那么怎么进行计算呢?

      把所有的数存放到硬盘中, 从1001个数开始,每个数传入内存与小顶堆的堆顶元素进行比较,如果这个数比堆顶元素大,那么就让这个数与堆顶元素进行交换,得到新的小顶堆。

      然后把新的小顶堆进行有序处理,使得它组成一个有序的小顶堆,然后依次进行手术操作,直到比较完所有的数。

      这样做可以不用占用大量的内存,时间复杂度也为O(n)。

      下面时代码:

      

    package com.rao.linkList;
    
    import java.util.Arrays;
    import java.util.Random;
    
    /**
     * @author Srao
     * @className TopN
     * @date 2019/12/3 19:30
     * @package com.rao.linkList
     * @Description 查找亿级别数中的前1000个数
     */
    public class TopN {
        /**
         * 构建小顶堆,从最后一个非叶子节点开始构建
         * @param arr:要查找的数组
         * @param length:要构建堆的大小,不用把全部的数组都参与构建
         * @return
         */
        public int[] buildHeap(int[] arr, int length){
            for (int i = (length-1)/2; i>=0; i--){
                upAdjust(arr, i, length);
            }
            return arr;
        }
    
        /**
         * 下沉函数
         * @param arr:要操作的数组
         * @param parent:以哪个节点作为父节点
         * @param length:要操作的数组的长度
         * @return
         */
        public int[] upAdjust(int[] arr, int parent, int length){
            //先把父节点保存起来
            int temp = arr[parent];
    
            //获得子节点的下标
            int child = 2*parent+1;
    
            //如果父节点比子节点大,那么进行子节点上浮
            while (child < length){
                //选择左右孩子中小的那个进行比较,因为每次都要判断是用左孩子还是右孩子进行比较,所以这个判断放循环里面
                if (child+1 < length && arr[child+1] < arr[child]){
                    child++;
                }
                //以根节点为中心,每次都是让根节点与子节点进行比较
                if (arr[child] > temp){
                    break;
                }else {
                    arr[parent] = arr[child];
                    parent = child;
                    child = parent*2+1;
                }
    
            }
    
            //这时arr[child] > arr[parent]
            arr[parent] = temp;
    
            //返回数组
            return arr;
    
        }
    
        /**
         * 把小顶堆中堆顶的数与数组中第i个数进行交换
         * @param arr:要筛选的数组
         * @param i:要交换的数的下标
         * @param length:小顶堆的长度
         */
        public void swap(int[] arr, int i, int length){
            if (arr[i] < arr[0]){
                return;
            }
            //两数交换
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            //然后重新调整小顶堆
            upAdjust(arr, 0, length);
        }
    
        /**
         *
         * @param arr:要进行筛选的数
         * @param length:要查找前n个数,即小顶堆的大小
         * @return
         */
        public int[] findTopN(int[] arr, int length){
            //先构建一个小顶堆
            int[] heap = buildHeap(arr, length);
    
            //再依次循环所有数组中的数,如果有比小顶堆堆顶大的数,就把这个数换成堆顶的数
            for (int i = length; i < heap.length; i++){
                swap(heap, i, length);
            }
    
            //返回小顶堆
            return heap;
        }
    
        public static void main(String[] args) {
            TopN topN = new TopN();
    
            // 第一组测试
            int[] arr1 = new int[]{56, 30, 71, 18, 29, 93, 44, 75, 20, 65, 68, 34};
    
            System.out.println("原数组:");
            System.out.println(Arrays.toString(arr1));
            topN.findTopN(arr1, 5);
            System.out.println("调整后数组:");
            System.out.println(Arrays.toString(arr1));
    
            // 第二组测试
            int[] arr2 = new int[1000];
            for(int i=0; i<arr2.length; i++) {
                arr2[i] = i + 1;
            }
    
            System.out.println("原数组:");
            System.out.println(Arrays.toString(arr2));
            topN.findTopN(arr2, 50);
            System.out.println("调整后数组:");
            System.out.println(Arrays.toString(arr2));
    
            // 第三组测试
            Random random =new Random();
            int[] arr3 = new int[1000];
            for(int i=0; i<arr3.length; i++) {
                arr3[i] = random.nextInt();
            }
    
            System.out.println("原数组:");
            System.out.println(Arrays.toString(arr3));
            topN.findTopN(arr3, 50);
            System.out.println("调整后数组:");
            System.out.println(Arrays.toString(arr3));
        }
    
    }
  • 相关阅读:
    topcoder srm 681 div1
    topcoder srm 683 div1
    topcoder srm 684 div1
    topcoder srm 715 div1
    topcoder srm 685 div1
    topcoder srm 687 div1
    topcoder srm 688 div1
    topcoder srm 689 div1
    topcoder srm 686 div1
    topcoder srm 690 div1 -3
  • 原文地址:https://www.cnblogs.com/rao11/p/11979433.html
Copyright © 2011-2022 走看看