题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
定义:1、最大堆:当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。
2、最小堆:当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。
3、平衡二叉树(AVL):平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
4、二叉搜索树(二叉查找树):二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
5、红黑树:一种二叉查找树,但在每个节点增加一个存储位表示节点的颜色,可以是红或黑(非红即黑)。通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其它路径长出两倍,因此,红黑树是一种弱平衡二叉树(由于是弱平衡,可以看到,在相同的节点情况下,AVL树的高度低于红黑树),相对于要求严格的AVL树来说,它的旋转次数少,所以对于搜索,插入,删除操作较多的情况下,我们就用红黑树。
6、B树,B+,B-树:
B-树: B-tree树即B树,B即Balanced;B-tree就是指的B树,是一种多路搜索树(并不是二叉的);
B+树:是对B-树的变体,也是一种多路搜索树:
各种数据结构复杂度对比:
数据结构 | 插入数据的时间复杂度 | 得到中位数的时间复杂度 |
没有排序的数组 | O(1) | O(n) |
排序的数组 | O(n) | O(1) |
排序的链表 | O(n) | O(1) |
二叉搜索树 | 平均O(logN),最差O(n) | 平均O(logN),最差(n) |
AVL树 | O(logN) | O(1) |
最大堆和最小堆 | O(logN) | O(1) |
思路:用一个最大堆来实现左边数据的容器,用一个最小堆来实现右边数据的容器。往堆中插入一个数据的时间效率是O(logn)。
1、先把新的数据插入最大堆,接着把最大堆中的最大数字拿出来插入最小堆。
2、最终插入最小堆的数字是原最大堆中最大的数字。
import java.util.Comparator;
import java.util.PriorityQueue;
/**
* 数据流中的中位数
* @Author: AnNing
* @Description:
* @Date: Create in 17:35 2019/7/22
*/
public class Median {
/* 大顶堆,存储左半边元素
* 不指定Comparator,默认为小顶堆*/
private PriorityQueue<Integer> left = new PriorityQueue<Integer>(11, new Comparator<Integer>(){
@Override
public int compare(Integer i1, Integer i2) {
return i2-i1;
}
});
/* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */
private PriorityQueue<Integer> right = new PriorityQueue<Integer>();
/* 当前数据流读入的元素个数 */
private int N = 0;
public void Insert(Integer val) {
/* 插入要保证两个堆存于平衡状态 */
if (N % 2 == 0) {
/* N 为偶数的情况下插入到右半边。
* 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素来的大,
* 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边 */
left.add(val);
right.add(left.poll());
} else {
right.add(val);
left.add(right.poll());
}
N++;
}
public Double GetMedian() {
if (N % 2 == 0)
return (left.peek() + right.peek()) / 2.0;
else
return (double) right.peek();
}
}