《剑指offer》算法题第八天
今日题目(对应书上第39~42题):
- 数组中出现次数超过一半的数字
- 最小的k个数(top k,重点!)
- 数据流中的中位数
- 连续子数组的最大和
今天的题目都比较经典,特别是第2题。
1. 数组中出现次数超过一半的数字
题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路:
有两种方法,
一,利用类似于快排的思想,寻找数组中的中位数,然后再检查是否满足出现次数。
二,根据数组的特点来做。
代码如下:
1 //方法一,快排 2 public class Solution { 3 public int MoreThanHalfNum_Solution(int [] array) { 4 if(array.length == 0) return 0; 5 int start = 0, end = array.length-1; 6 int mid = array.length>>1; 7 while(start < end){ 8 int ind = partition(array,start,end); 9 if(ind == mid) 10 break; 11 if(ind > mid) 12 end = ind-1; 13 if(ind < mid) 14 start = ind+1; 15 } 16 if(check(array,array[mid])) 17 return array[mid]; 18 else return 0; 19 } 20 21 public boolean check(int[] nums,int result){ 22 int times = 0; 23 for(int n:nums){ 24 if(n == result) 25 times++; 26 } 27 return times*2 > nums.length; 28 } 29 30 public int partition(int[] nums,int start,int end){ 31 int target = nums[end]; 32 int res = start; 33 for(int i = start; i < end; i++){ 34 if(nums[i] < target){ 35 int swap = nums[i]; 36 nums[i] = nums[res]; 37 nums[res] = swap; 38 res++; 39 } 40 } 41 nums[end] = nums[res]; 42 nums[res] = target; 43 return res; 44 } 45 } 46 47 48 //方法二 49 public int MoreThanHalfNum_Solution(int [] array) { 50 if(array.length == 0) return 0; 51 int result = array[0]; 52 int times = 1; 53 for(int n:array){ 54 if(times == 0){ 55 result = n; 56 times = 1; 57 }else if(result == n) 58 times++; 59 else 60 times--; 61 } 62 if(check(array,result)) 63 return result; 64 else return 0; 65 }
2. 最小的k个数
题目描述: 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路:
这道题是经典的top K问题,有两种解法:
1,运用快排,找出第K个数的位置,将前面的数输出
2,利用容量为K的最大堆,循环数组,每次替换掉堆中最大的数
代码如下:
1 //快排 2 public class Solution { 3 public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { 4 ArrayList<Integer> res = new ArrayList(); 5 if(k > input.length || k == 0) return res; 6 int start = 0, end = input.length-1; 7 int ind = partition(input,start,end); 8 while(ind != k-1){ 9 if(ind > k-1){ 10 end = ind-1; 11 }else{ 12 start = ind+1; 13 } 14 ind = partition(input,start,end); 15 } 16 for(int i = 0;i < k; i++) 17 res.add(input[i]); 18 return res; 19 } 20 21 public int partition(int[] nums,int start,int end){ 22 int target = nums[end]; 23 int ind = start; 24 for(int i = start; i < end;i++){ 25 if(nums[i] < target){ 26 int swap = nums[i]; 27 nums[i] = nums[ind]; 28 nums[ind] = swap; 29 ind++; 30 } 31 } 32 nums[end] = nums[ind]; 33 nums[ind] = target; 34 return ind; 35 } 36 } 37 38 39 //利用最大堆 40 public class Solution { 41 public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { 42 if(k > input.length || k < 1) return new ArrayList(); 43 PriorityQueue<Integer> maxHeap = new PriorityQueue(k,new Comparator<Integer>(){ 44 public int compare(Integer o1,Integer o2){ 45 return o2.compareTo(o1); 46 } 47 }); 48 for(int i = 0; i < input.length; i++){ 49 if(maxHeap.size() < k) 50 maxHeap.add(input[i]); 51 else{ 52 if(maxHeap.peek() > input[i]){ 53 maxHeap.poll(); 54 maxHeap.add(input[i]); 55 } 56 } 57 } 58 return new ArrayList(maxHeap); 59 } 60 }
3.数据流中的中位数
题目描述:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
思路:
这道题的关键是选择一个怎样的数据结构来维护数据流,可以使插入和查询的速度都比较理想。在这边维护了一个最大堆和一个最小堆,最大堆存储中位数左边的数字,最小堆存储中位数右边的数字。
代码如下:
1 public class Solution { 2 int count = 0; 3 PriorityQueue<Integer> min = new PriorityQueue(); 4 PriorityQueue<Integer> max = new PriorityQueue(new Comparator<Integer>(){ 5 public int compare(Integer o1,Integer o2){ 6 return o2-o1; 7 } 8 }); 9 public void Insert(Integer num) { 10 if((count&1) == 0){ 11 min.offer(num); 12 max.offer(min.poll()); 13 }else{ 14 max.offer(num); 15 min.offer(max.poll()); 16 } 17 count++; 18 } 19 20 public Double GetMedian() { 21 if(((min.size()+max.size())&1) == 0) 22 return (min.peek()+max.peek())/2.0; 23 else 24 return max.peek()*1.0; 25 } 26 }
4.连续子数组的最大和
题目描述:
输入一个整数数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
思路:
这道题也算是比较经典的一道题了,面经里面经常看到,下面给出两种解法。
代码如下:
1 //解法一 2 public class Solution { 3 public int FindGreatestSumOfSubArray(int[] array) { 4 int max = Integer.MIN_VALUE; 5 int sum = 0; 6 for(int n:array){ 7 if(sum <= 0) 8 sum = n; 9 else 10 sum += n; 11 max = max>sum?max:sum; 12 13 } 14 return max; 15 } 16 } 17 18 19 //解法二,动态规划 20 public class Solution { 21 public int FindGreatestSumOfSubArray(int[] array) { 22 int[] dp = new int[array.length]; 23 dp[0] = array[0]; 24 for(int i = 1; i < array.length; i++){ 25 if(dp[i-1] < 0) 26 dp[i] = array[i]; 27 else 28 dp[i] = array[i] + dp[i-1]; 29 } 30 int res = dp[0]; 31 for(int n:dp) 32 res = n>res?n:res; 33 return res; 34 } 35 }