【剑指offer中的排序算法】推排序(大顶堆/小顶堆)/归并排序

一、堆排序

题:如何得到一个数据流中的中位数?(动态、实时,在任意时刻都能够直接拿到我们想要的均值而不去计算下标取值)
堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:

 小顶堆:Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]
 大顶堆:Key[i]>=Key[2i+1]&&key>=key[2i+2]

最小堆
题:如何得到一个数据流中的中位数?(动态、实时,在任意时刻都能够直接拿到我们想要的均值而不去计算下标取值)

思路:

需要求的是中位数,如果我将 1 2 3 4 5 6 7 8定为最终的数据流

  • 此时的中位数是4+5求均值。为什么是4,为什么是5
  • 利用队列我们就可以看得很清楚,4是前半部分最大的值,肯定是维系在大顶堆
  • 而5是后半部分的最小值,肯定是维系在小顶堆。
  • 问题就好理解了:使用小顶堆存大数据,使用大顶堆存小数据。这样堆顶一取出就是中位数了。

代码中奇数时刻大顶堆存值,所以遇到奇数时刻,大顶堆直接弹出就是中位数

public class stockMedium {

    private int cnt = 0;
    private PriorityQueue<Integer> low = new PriorityQueue<>();
    // 默认维护小顶堆
    private PriorityQueue<Integer> high = new PriorityQueue<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2.compareTo(o1);
        }
    });

    public void Insert(Integer num) {
        // 数量++
        cnt++;
        // 如果为奇数的话
        if ((cnt & 1) == 1) {
            // 由于奇数,需要存放在大顶堆上
            // 但是呢,现在你不知道num与小顶堆的情况
            // 小顶堆存放的是后半段大的数
            // 如果当前值比小顶堆上的那个数更大
            if (!low.isEmpty() && num > low.peek()) {
                // 存进去
                low.offer(num);
                // 然后在将那个最小的吐出来
                num = low.poll();
            } // 最小的就放到大顶堆,因为它存放前半段
            high.offer(num);
        } else {
            // 偶数的话,此时需要存放的是小的数
            // 注意无论是大顶堆还是小顶堆,吐出数的前提是得有数
            if (!high.isEmpty() && num < high.peek()) {
                high.offer(num);
                num = high.poll();
            } // 大数被吐出,小顶堆插入
            low.offer(num);
        }

    }

    public Double GetMedian() {// 表明是偶数
        double res = 0;
        // 奇数
        if ((cnt & 1) == 1) {
            res = high.peek();
        } else {
            res = (high.peek() + low.peek()) / 2.0;
        }
        return res;
    }
}

二、归并排序

在这里插入图片描述
思路:该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
在这里插入图片描述
在这里插入图片描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。
即输出P%1000000007

归并排序的改进,把数据分成前后两个数组(递归分到每个数组仅有一个数据项),
合并数组,合并时,出现前面的数组值array[i]大于后面数组值array[j],则前面
数组array[i]~array[mid]都是大于array[j]的,count += mid+1 - i
 
public class InversePairs {
    public int InversePairs(int [] array) {
        if(array==null||array.length==0)
            return 0;
        int[] copy = new int[array.length];
        int count = sort(array,copy,0,array.length-1);
        return count;
    }

    private int sort(int[] array, int[] copy, int low, int high) {
        if(low==high)
            return 0;
        int mid = (low+high)>>1;
        int leftCount = sort(array,copy,low,mid)%1000000007;
        int rightCount = sort(array,copy,mid+1,high)%1000000007;
        int count = 0;

        int loc = high;
        int i = mid;
        int j = high;
        while (i>=low&&j>=mid+1){
            if(array[i]>array[j]){
                count += j-mid;
                if(count>=1000000007)//数值过大求余
                {
                    count%=1000000007;
                }
                copy[loc] = array[i];
                i--;
                loc--;
            }
            else{
                copy[loc] = array[j];
                j--;
                loc--;
            }
        }
        for(;i>=low;i--)
        {
            copy[loc--]=array[i];
        }
        for(;j>mid;j--)
        {
            copy[loc--]=array[j];
        }
        for(int s=low;s<=high;s++)
        {
            array[s] = copy[s];
        }
        return (leftCount+rightCount+count)%1000000007;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值