剑指Offer面试题(第二十六天)面试题41(PriorityQueue)、41(RadBlackTree)、42

* 面试题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;
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值