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。