【手撕代码】剑指offer-数据流的中位数

描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

解题思路

计算数据流的中位数难点:数据流是一个动态的数据,在求解中位数时需要考虑动态排序计算当前位置中位数。二叉平衡树可以针对数据流实现实现动态排序,但传统的二叉平衡树只会考虑左右子树的深度导致树失去平衡,本题所面对的场景则需要考虑左右字数节点数差值导致树失去平衡。因此插入新节点后我们需要检查是否因为插入新的节点从而导致树失去平衡,失去平衡的条件即为左右子树节点数差值大于1。树右旋时,需要找到左子树最大值并将其设为根节点。树左旋时,需要找到右子树最小值并将其设为根节点。

public class Solution {
    static class AvlNode {
        // 节点数
        int count;
        // 节点元素值
        int val;
        // 左孩子
        AvlNode left;
        // 右孩子
        AvlNode right;

        public AvlNode(int val) {
            this.val = val;
            this.count = 1;
        }
    }

    public static int getCount(AvlNode node) {
        return node == null ? 0 : node.count;
    }

    AvlNode root;

    public void Insert(Integer num) {
        root = Insert(root, num);
    }

    public AvlNode Insert(AvlNode node, int num) {
        if (node == null) {
            return new AvlNode(num);
        }
        if (num <= node.val) {
            node.left = Insert(node.left, num);
            node = balance(node, num);
        } else {
            node.right = Insert(node.right, num);
            node = balance(node, num);
        }
        node.count=getCount(node.left)+getCount(node.right)+1;
        return node;
    }

    public AvlNode balance(AvlNode node, int num) {
        // 左子树失去平衡
        if (getCount(node.left) - getCount(node.right) == 2) {
            if (num < node.left.val) {
                // LL型
                node = rotateWithLeftChild(node);
            } else {
                // LR型
                node = doubleRotateWithLeftChild(node);
            }
        }

        // 右子树失去平衡
        if (getCount(node.right) - getCount(node.left) == 2) {
            if (num > node.right.val) {
                // RR型
                node = rotateWithRightChild(node);
            } else {
                // RL型
                node = doubleRotateWithRightChild(node);
            }
        }

        return node;
    }

    public AvlNode rotateWithLeftChild(AvlNode node) {
        AvlNode leftChild = node.left;
        AvlNode parent = null;
        while (leftChild.right != null) {
            leftChild.count--;
            parent = leftChild;
            leftChild = leftChild.right;
        }
        if (leftChild != node.left) {
            // 保存最大值节点左孩子
            parent.right = leftChild.left;
            // 将最大值左孩子设置为根节点左孩子
            leftChild.left = node.left;
        }
        leftChild.right = node;
        node.left = null;
        // 更新右子树高度
        node.count=getCount(node.left)+getCount(node.right)+1;
        // 更新根节点高度
        leftChild.count=getCount(leftChild.left)+getCount(leftChild.right)+1;
        // 新的根节点
        return leftChild;
    }

    public AvlNode rotateWithRightChild(AvlNode node) {
        AvlNode rightChild = node.right;
        AvlNode parent = null;
        while (rightChild.left != null) {
            rightChild.count--;
            parent = rightChild;
            rightChild = rightChild.left;
        }
        if (rightChild != node.right) {
            // 保存最小值节点右孩子
            parent.left = rightChild.right;
            // 将最大值左孩子设置为根节点左孩子
            rightChild.right = node.right;
        }
        rightChild.left = node;
        node.right = null;
        // 更新左子树高度
        node.count=getCount(node.left)+getCount(node.right)+1;
        // 更新根节点高度
        rightChild.count=getCount(rightChild.left)+getCount(rightChild.right)+1;
        // 新的根节点
        return rightChild;
    }

    // 在左子树内部插入导致失去平衡-左右
    public AvlNode doubleRotateWithLeftChild(AvlNode node) {
        // 左子树左旋
        node.left = rotateWithRightChild(node.left);
        // 整棵树进行右旋
        node = rotateWithLeftChild(node);
        return node;
    }

    // 在右子树左侧插入导致失去平衡-右左
    public AvlNode doubleRotateWithRightChild(AvlNode node) {
        // 左子树右旋
        node.right = rotateWithLeftChild(node.right);
        // 整棵树进行左旋
        node = rotateWithRightChild(node);
        return node;
    }

    public Double GetMedian() {
        int leftCount =getCount(root.left);
        int rightCount = getCount(root.right);
        // 左右子树节点数相等,根节点为中位数
        if (leftCount == rightCount) {
            return root.val * 1.0;
        }
        // 左子树节点数多于右子树节点数
        if (leftCount > rightCount) {
            AvlNode node = root.left;
            while (node.right != null) {
                node = node.right;
            }
            return (node.val + root.val) / 2.0;
        } 
        // 右子树节点数多于左子树节点数
        else {
            AvlNode node = root.right;
            while (node != null && node.left != null) {
                node = node.left;
            }
            return (node.val + root.val) / 2.0;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Teamo.Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值