在1000万整数中找到前100个最大的数 算法

这一段时间写毕业设计,遇到一些大数据的情况,想起之前和同学讨论的最优算法,写一下相关思路。

 

在1000万整数中寻找最大的100个数,最优的算法是建一个大小为100的小顶堆(堆排序需要建立完全二叉树),先取出前100个数并构建小顶堆,然后遍历数据与小顶堆的堆顶相比,比堆顶小直接丢弃,比堆顶大则替换堆顶,并重新构建这个堆。

堆排序

先介绍一下完全二叉树:除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐

 

堆排序:堆排序是将数据看成是完全二叉树、根据完全二叉树的特性来进行排序的一种算法

  • 最小堆要求节点元素都不大于其左右孩子
  • 那么处于最小堆的根节点的元素一定是这个堆中的最小值

模拟一次最小堆排序的过程:

假如有

        int[] arr = new int[]{3,6,8,7,0,1,10,4,2};

第一步:建立完全二叉树

       从上图可以看出,第n个节点的左子节点为2*n+1,右子节点为2*n+2

第二步:从 arr.length/2 - 1 就是最后一个叶子节点的父节点开始,比较其左右子节点,找到最小的,并与其交换,一直到根节点:

这样一个最小堆就建立完成。

相关代码(java):

    /**
     * 建堆
     *
     * @param arr          看作是完全二叉树
     * @param currentNode 当前父节点位置
     * @param size            节点总数
     */
public class HeapSortTest {
    public static void main(String[] args) {
        int[] arr = new int[]{3,6,8,7,0,1,10,4,2};
        int start = arr.length/2 -1;
        for (int i = start; i >= 0 ; i--){
            minHeap(arr, arr.length, i);
        }
        System.out.println(Arrays.toString(arr));
    }
    public static void minHeap(int []arr, int size, int currentNode){
        //左子树和右字数的位置
        int leftNode = currentNode * 2 + 1;
        int rightNode = currentNode * 2 + 2;
        //把当前父节点位置看成是最小的
        int min = currentNode;

        //比较左右节点
        if (leftNode < size && arr[leftNode] < arr[min]){
            max = leftNode;
        }
        if (rightNode < size && arr[rightNode] < arr[min]){
            max = rightNode;
        }
        //如果比父节点小,就交换
        if (min != currentNode){
            int temp = arr[min];
            arr[min] = arr[currentNode];
            arr[currentNode] = temp;
            //继续比较,直到完成一次建堆
            minHeap(arr, size, min);
        }

    }
}

对于1000万个数:

    public static int N = 100;        //Top100
    public static int LEN = 10000000; //1千万个整数
    public static int arrs[] =  new int[LEN];
    public static int result[] = new int[N]; //在内存维护一个长度为N的小顶堆
    public static int len = result.length;
    //堆中元素的有效元素 heapSize<=len
    public static int heapSize = len;
    public static void main(String[] args) {
        //生成随机数组
        for(int i = 0;i<LEN;i++){
            arrs[i] = new Random().nextInt(999999999);
        }
 
        //构建初始堆
        for(int i =  0;i<N;i++){
            result[i] = arrs[i];
        }
        //构建小顶堆
        long start =System.currentTimeMillis();
        buildMinHeap();
        for(int i = N;i<LEN;i++){
            if(arrs[i] > result[0]){
                result[0] = arrs[i];
                minHeap(arrs, N, 0);
            }
        }
        System.out.println(LEN+"个数,求Top"+N+",耗时"+(System.currentTimeMillis()-start)+"毫秒");
        print();
    }
     /**
     * 自底向上构建小堆
     */
    public static void buildMinHeap(){
        int size = len / 2 -1 ; //最后一个非叶子节点
        for(int i = size;i>=0;i--){
            minHeap(arrs, N, i);
        }

一次堆排序重整的时间复杂度:logn (第一次建堆是从下到上,重整堆是从上到下,每次数据替换时,最差情况是堆顶数据为最大,依次向下比较交换,直到叶子节点,比较交换次数为logn 即为构建二叉树层数)。

最差情况为每次都替换,即时间复杂度为10000000*log100

 

参考资料:https://www.cnblogs.com/Java3y/p/8639937.html

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值