剑指 Offer 41. 数据流中的中位数

class MedianFinder {

        ArrayList<Integer> listBigNums = new ArrayList<>();
        ArrayList<Integer> listSmallNums = new ArrayList<>();

        public MedianFinder() {

        }

        public void addNum(int num) {
            if (listBigNums.size() > 0 && num > listBigNums.get(0)) {
                int position = findInsertPosition(num, listBigNums);
                listBigNums.add(position, num);
            } else {
                int position = findInsertPosition(num, listSmallNums);
                listSmallNums.add(position, num);
            }
            reviewList();
        }

        public double findMedian() {
            if (listBigNums.isEmpty() && listSmallNums.isEmpty())
                return 0;
            else if (listBigNums.isEmpty()) {
                return listSmallNums.get(0);
            } else {
                if (listSmallNums.size() > listBigNums.size()) {
                    return listSmallNums.get(listSmallNums.size() - 1);
                } else {
                    return (listSmallNums.get(listSmallNums.size() - 1) + listBigNums.get(0)) / 2.0;
                }
            }
        }

        //二分查找插入位置。
        public int findInsertPosition(int number, ArrayList<Integer> list) {
            if (list.isEmpty())
                return 0;
            if(list.size() == 1) {
                if(list.get(0) < number) {
                    return 1;
                } else
                    return 0;
            }
            int left = 0;
            int right = list.size() - 1;

            while (left < right - 1) {
                int mid = (left + right) / 2;
                if (number < list.get(mid)) {
                    right = mid;
                } else if (number > list.get(mid)) {
                    left = mid;
                } else {
                    return mid;
                }
            }
            if (list.get(left) <= number && list.get(right) >= number)
                return right;
            else if (list.get(left) > number)
                return left;
            else
                return right + 1;
        }

        //平衡两个列表
        public void reviewList() {
            if ((listSmallNums.size() - listBigNums.size()) > 1) {
                listBigNums.add(0, listSmallNums.get(listSmallNums.size() - 1));
                listSmallNums.remove(listSmallNums.size() - 1);
            } else if (listBigNums.size() > listSmallNums.size()) {
                listSmallNums.add(listBigNums.get(0));
                listBigNums.remove(0);
            }
        }
    }

维护两个升序排列的列表,小列表和大列表。

维护规则:

        1.小列表的末尾数字比大列表的末尾数字小

        2.小列表的长度等于大列表的长度+1,或者等于大列表的长度。

        3.两个列表都是从小到大排序的。

插入规则:

        1.通过列表首位判断数字插入哪个列表。

        2.通过二分法找到列表的插入位置,并插入数字。

        3.插入列表之后,判断维护规则是否被打破,如果打破了维护规则,就通过移动两个列表首位数字来保存维护规则的平衡。

 找中位数规则:

        通过小列表的末尾数字和大列表的第一个数字,来计算中位数。

 

按照上面思路编码实现,时间复杂度维持在LogN。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值