* 面试题41:数据流中的中位数
* 题目:如何得到一个数据流中的中位数?
* 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序后位于中间的数值
* 如果从数据流中读出偶数个数值,那么重欸书九时左右数值排序后中间两个数的平均值
*
* 思路:其中数据流中的数据是不断变化的,需要在数据增加之后将其读取到容器中,
* 其中容器可以使用数组,排序的链表,二叉搜索树,平衡的二叉搜索树(AVL树),最大堆+最小堆实现
* 其中最大堆+最小堆 得到中位数的时间复杂度为O(1) 插入新的匀速的时间复杂度O(logn)
*
* 最大堆+最小堆实现过程:
* 当数据的个数是奇数个时,中位数为p1,p2共同指向的数
* 当数据的个数为偶数个时,中位数位p1,p2分别指向的两个数的平均值
* 分析其过程发现容器中的数据被中位数分为两部分 :左侧都比右侧小,p1指向的是左侧中最大的,p2指向的是右侧中最小的
* 因而对两侧进行建堆,左侧为最大堆,右侧为最小堆,其中堆顶就是p1、p2指向的数
*
* 具体实现过程:
* 1>首先要保证数据平均的分配到两个堆中,两个堆中的数目之差不能够超过1,
* 因此规定:当数据的总数目(两个堆数目之和)为偶数时,将新数据插入到最小堆;若为奇数,将新数据插入到最大堆
* 2>还要保证最大堆中的数据都小于最小堆的数据:
* 当总数是偶数时,插入了最小堆,但是这个新的元素却比最大堆中的一些元素要小,应当是最大堆,
* 解决方法:在插入到最小堆之前,应当先将新元素插入到最大堆,然后把最大堆中的最大值取出插入到最小堆中,
* 这样就能保证插入的值都是比最大堆数值大了
* 当总数为奇数时,插入到最大堆,但是这个新元素却比最小堆中的一些元素要大,应当插入最小堆,
* 要上诉情况类似,不在赘述
*
* 可以使用优先队列、红黑树实现最大堆+最小堆
* 本例使用优先队列实现
* insert()方法读取数据流 GetMedian()方法获取数据流中的中位数
package Test;
import java.util.Collections;
import java.util.PriorityQueue;
public class No41GetMedian_PriorityQueue {
/*
* 面试题41:数据流中的中位数
* 题目:如何得到一个数据流中的中位数?
* 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序后位于中间的数值
* 如果从数据流中读出偶数个数值,那么重欸书九时左右数值排序后中间两个数的平均值
*
* 思路:其中数据流中的数据是不断变化的,需要在数据增加之后将其读取到容器中,
* 其中容器可以使用数组,排序的链表,二叉搜索树,平衡的二叉搜索树(AVL树),最大堆+最小堆实现
* 其中最大堆+最小堆 得到中位数的时间复杂度为O(1) 插入新的匀速的时间复杂度O(logn)
*
* 最大堆+最小堆实现过程:
* 当数据的个数是奇数个时,中位数为p1,p2共同指向的数
* 当数据的个数为偶数个时,中位数位p1,p2分别指向的两个数的平均值
* 分析其过程发现容器中的数据被中位数分为两部分 :左侧都比右侧小,p1指向的是左侧中最大的,p2指向的是右侧中最小的
* 因而对两侧进行建堆,左侧为最大堆,右侧为最小堆,其中堆顶就是p1、p2指向的数
*
* 具体实现过程:
* 1>首先要保证数据平均的分配到两个堆中,两个堆中的数目之差不能够超过1,
* 因此规定:当数据的总数目(两个堆数目之和)为偶数时,将新数据插入到最小堆;若为奇数,将新数据插入到最大堆
* 2>还要保证最大堆中的数据都小于最小堆的数据:
* 当总数是偶数时,插入了最小堆,但是这个新的元素却比最大堆中的一些元素要小,应当是最大堆,
* 解决方法:在插入到最小堆之前,应当先将新元素插入到最大堆,然后把最大堆中的最大值取出插入到最小堆中,
* 这样就能保证插入的值都是比最大堆数值大了
* 当总数为奇数时,插入到最大堆,但是这个新元素却比最小堆中的一些元素要大,应当插入最小堆,
* 要上诉情况类似,不在赘述
*
* 可以使用优先队列、红黑树实现最大堆+最小堆
* 本例使用优先队列实现
* insert()方法读取数据流 GetMedian()方法获取数据流中的中位数
* */
int count;//记录数据的总数量
//minHeap 最小堆 maxHeap 最大堆
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(Collections.reverseOrder());
public static void main(String[] args) {
// TODO Auto-generated method stub
No41GetMedian_PriorityQueue g = new No41GetMedian_PriorityQueue();
g.insert(1);
g.insert(2);
g.insert(3);
g.insert(4);
g.insert(5);
System.out.println(g.GetMidean());
}
//读取数据流
public void insert(int num) {
// TODO Auto-generated method stub
count++;
//当为总数count是奇数时,进入最大堆
if((count & 1) == 1) {
//保证左侧数据小于右侧数据
//先进入最小堆,然后弹出最小的根 从而保证右边大于左边
minHeap.offer(num);
maxHeap.offer(minHeap.poll());
}
else {
//同上
maxHeap.offer(num);
minHeap.offer(maxHeap.poll());
}
}
//获取中位数
private Double GetMidean() {
// TODO Auto-generated method stub
if(count == 0)
return null;
//若总数为奇数,则返回最大堆的根节点peek(获取head)
if((count & 1) == 1) {
return Double.valueOf(maxHeap.peek());
}
else {
return Double.valueOf(maxHeap.peek()+minHeap.peek())/2;
}
}
}
* 本例使用红黑树实现
package Test;
import java.util.TreeSet;
public class No41GetMedian_RedBlackTree {
/*
* 面试题41:数据流中的中位数
* 题目:如何得到一个数据流中的中位数?
* 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序后位于中间的数值
* 如果从数据流中读出偶数个数值,那么重欸书九时左右数值排序后中间两个数的平均值
*
* 思路:其中数据流中的数据是不断变化的,需要在数据增加之后将其读取到容器中,
* 其中容器可以使用数组,排序的链表,二叉搜索树,平衡的二叉搜索树(AVL树),最大堆+最小堆实现
* 其中最大堆+最小堆 得到中位数的时间复杂度为O(1) 插入新的匀速的时间复杂度O(logn)
*
* 最大堆+最小堆实现过程:
* 当数据的个数是奇数个时,中位数为p1,p2共同指向的数
* 当数据的个数为偶数个时,中位数位p1,p2分别指向的两个数的平均值
* 分析其过程发现容器中的数据被中位数分为两部分 :左侧都比右侧小,p1指向的是左侧中最大的,p2指向的是右侧中最小的
* 因而对两侧进行建堆,左侧为最大堆,右侧为最小堆,其中堆顶就是p1、p2指向的数
*
* 具体实现过程:
* 1>首先要保证数据平均的分配到两个堆中,两个堆中的数目之差不能够超过1,
* 因此规定:当数据的总数目(两个堆数目之和)为偶数时,将新数据插入到最小堆;若为奇数,将新数据插入到最大堆
* 2>还要保证最大堆中的数据都小于最小堆的数据:
* 当总数是偶数时,插入了最小堆,但是这个新的元素却比最大堆中的一些元素要小,应当是最大堆,
* 解决方法:在插入到最小堆之前,应当先将新元素插入到最大堆,然后把最大堆中的最大值取出插入到最小堆中,
* 这样就能保证插入的值都是比最大堆数值大了
* 当总数为奇数时,插入到最大堆,但是这个新元素却比最小堆中的一些元素要大,应当插入最小堆,
* 要上诉情况类似,不在赘述
*
* 可以使用优先队列、红黑树实现最大堆+最小堆
* 本例使用红黑树实现
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No41GetMedian_RedBlackTree g = new No41GetMedian_RedBlackTree();
g.insert(1);
g.insert(2);
g.insert(3);
g.insert(4);
System.out.println(g.GetMidean());
}
TreeSet<Integer> minHeap = new TreeSet<Integer>();
TreeSet<Integer> maxHeap = new TreeSet<Integer>();
//insertd读取数据流 GetMedian获取中位数
public void insert(int i) {
// TODO Auto-generated method stub
//若总数为奇数 则进入最大堆
if(((minHeap.size() + maxHeap.size()) & 1) == 1) {
minHeap.add(i);
maxHeap.add(minHeap.pollFirst());
}
else{
maxHeap.add(i);
minHeap.add(maxHeap.pollFirst());
}
}
//获取中位数
private Double GetMidean() {
// TODO Auto-generated method stub
if(((minHeap.size() + maxHeap.size()) & 1) == 0) {
return null;
}
if(((minHeap.size() + maxHeap.size()) & 1) == 1) {
return Double.valueOf(maxHeap.pollFirst());
}
else {
return Double.valueOf(maxHeap.pollFirst() + minHeap.pollFirst())/2;
}
}
}
* 面试题42:连续子数组的最大和
* 题目:输入一个整型数组,数组中有正数、负数。
* 数组中的一个或连续多个整数组成一个子数组。
* 求所有子数组的和的最大值。
* 要求时间复杂度为O(n)
*
* 思路:设置两个变量:一个记录当前和 一个记录最大和 返回结果为最大和的值
* 若当前和<0 则将其设置为当前变量的值
* 若当前和>=0 则与当前变量的值进行累加
* 若当前和 > 最大和 则将最大和设置为当前和,变成最大的
package Test;
public class No42FindGreatestSumOfSubArray {
/*
* 面试题42:连续子数组的最大和
* 题目:输入一个整型数组,数组中有正数、负数。
* 数组中的一个或连续多个整数组成一个子数组。
* 求所有子数组的和的最大值。
* 要求时间复杂度为O(n)
*
* 思路:设置两个变量:一个记录当前和 一个记录最大和 返回结果为最大和的值
* 若当前和<0 则将其设置为当前变量的值
* 若当前和>=0 则与当前变量的值进行累加
* 若当前和 > 最大和 则将最大和设置为当前和,变成最大的
*
*
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No42FindGreatestSumOfSubArray f = new No42FindGreatestSumOfSubArray();
int[] array = {1,-2,3,10,-4,7,2,-5};
System.out.println("数组中最大的子数组之和为:"+f.FindGreatestSumOfSubArray(array));
}
public int FindGreatestSumOfSubArray(int[] array) {
// TODO Auto-generated method stub
if(array == null || array.length == 0)
return -1;
//一个存储最大和 一个存储当前变量之和
int cur = array[0];
int greatest = array[0];
for(int i = 1;i < array.length;i++) {
//若当前和<0 则将其设置为当前变量
//若当前和>=0 则当前变量进行累加
if(cur < 0)
cur = array[i];
else {
cur += array[i];
}
//若当前和>最大和 则将最大和设置为当前和 变为最大的
if(cur > greatest)
greatest = cur;
}
return greatest;
}
}
* 面试题43:1—n整数中1出现的次数(背!!!!)
* 题目:输入一个整数n,求1-n这n个整数中的十进制表示中1出现的次数
* 例如:输入12,1-12这些整数中包含1 的数字有1、10、11、12 1一共出现了5次(11算作2次)
* (求出任意非负整数区间的1出现的个数)
*
* 思路:总结规律发现: 时间复杂度O(k) k为位数
* 当前位的值 = 0时,1出现的次数为: 高位值*当前位位数
* 当前位的值 = 1时,1出现的次数为: 高位值*当前位位数 + 当前位数值 % 当前位位数 + 1
* 当前位的值 > 1时,1出现的次数为: (高位值 + 1)*当前位位数
*
* 例如:n = 21305 每一位出现1的情况统计
* 万位(>1):(21305 / 100000 + 1) * 10000 = 10000次
* 千位(=1):(21305 / 10000 ) * 1000 + 21305 % 1000 + 1 = 2306次
* 百位(>1):(21305 / 1000 + 1) * 100 = 2200次
* 十位(=0):(21305 / 100) * 10 = 2130次
* 个位(>1):(21305 /10 + 1) * 1 = 2131次
https://github.com/linpeiyou/CodingInterviews/blob/master/src/com/peige/algo/_43_NumberOf1.java
package Test;
public class No43NumberOf1Between1AndN {
/*
* 面试题43:1—n整数中1出现的次数
* 题目:输入一个整数n,求1-n这n个整数中的十进制表示中1出现的次数
* 例如:输入12,1-12这些整数中包含1 的数字有1、10、11、12 1一共出现了5次(11算作2次)
* (求出任意非负整数区间的1出现的个数)
*
* 思路:总结规律发现: 时间复杂度O(k) k为位数
* 当前位的值 = 0时,1出现的次数为: 高位值*当前位位数
* 当前位的值 = 1时,1出现的次数为: 高位值*当前位位数 + 当前位数值 % 当前位位数 + 1
* 当前位的值 > 1时,1出现的次数为: (高位值 + 1)*当前位位数
*
* 例如:n = 21305 每一位出现1的情况统计
* 万位(>1):(21305 / 100000 + 1) * 10000 = 10000次
* 千位(=1):(21305 / 10000 ) * 1000 + 21305 % 1000 + 1 = 2306次
* 百位(>1):(21305 / 1000 + 1) * 100 = 2200次
* 十位(=0):(21305 / 100) * 10 = 2130次
* 个位(>1):(21305 /10 + 1) * 1 = 2131次
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No43NumberOf1Between1AndN number = new No43NumberOf1Between1AndN();
int n = 21305;
System.out.println("1出现的次数为:" + number.NumberOf1Between1AndN(n));
}
private int NumberOf1Between1AndN(int n) {
// TODO Auto-generated method stub
if(n <= 0) {
return 0;
}
int sum = 0;
for(int k = 1;n/k > 0;k *= 10) {
int mod = n/k % 10;
if(mod > 1) {
sum += ((n / (k * 10)) + 1) *k ;
}
else if(mod == 1) {
sum += (n / (k * 10))*k + (n % k) + 1;
}
//仅剩mod == 0
else{
sum += (n / (k * 10)) * k;
}
}
return sum;
}
}