14. 时间效率(5)
题一:【数组中出现次数超过一半的数字】
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
法一:暴力破解——使用Map记录数组中每个数字出现的次数
1 import java.util.*;
2 public class Solution {
3 public int MoreThanHalfNum_Solution(int [] array) {
4 HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
5 int len = array.length;
6 for(int i=0;i<len;i++){
7 if(map.containsKey(array[i])){
8 map.replace(array[i],map.get(array[i])+1);
9 if(map.get(array[i])>=(len+2)/2){
10 return array[i];
11 }
12 }else{
13 map.put(array[i],1);
14 if(map.get(array[i])>=(len+2)/2){
15 return array[i];
16 }
17 }
18 }
19 return 0;
20 }
21 }
法二:也可以先将数组排序,再遍历查看是否有连续的(长度大于数组长度一半)元素。
略
题二:【最小的K个数】
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
分析:先排序,再取数组元素
1 import java.util.ArrayList;
2 public class Solution {
3 public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
4 ArrayList<Integer> list = new ArrayList<Integer>();
5 if(input==null||input.length==0||k<=0||k>input.length) return list;
6 quickSort(input,0,input.length-1);
7 for(int i=0;i<k;i++){
8 list.add(input[i]);
9 }
10 return list;
11 }
12 public void quickSort(int[] input,int left, int right){
13 int base = input[left];
14 int leftNum = left;
15 int rightNum = right;
16 while(left<right){
17 while(left<right&&input[right]>=base){
18 right--;
19 }
20 while(left<right&&input[left]<=base){
21 left++;
22 }
23 swap(input,left,right);
24 }
25 swap(input,leftNum,left);
26 quickSort(input,leftNum,left-1);
27 quickSort(input,right+1,rightNum);
28 }
29 public void swap(int[] input, int i, int j){
30 int tmp = input[i];
31 input[i] = input[j];
32 input[j] = tmp;
33 }
34 }
分析二:快排拓展,数组取最小k个元素,即0~k-1索引处的元素比k~input.length-1的值都小。可以每次将base索引和k-1比较。
1 import java.util.ArrayList;
2 public class Solution {
3 public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
4 ArrayList<Integer> list = new ArrayList<Integer>();
5 if(input==null||input.length==0||k<=0||k>input.length) return list;
6 int start = 0;
7 int end = input.length-1;
8 int index = quick(input,start,end);
9 while(index!=k-1){
10 if(index<k-1){
11 start = index+1;
12 }else{
13 end = index-1;
14 }
15 index = quick(input,start,end);
16 }
17 for(int i=0;i<k;i++){
18 list.add(input[i]);
19 }
20 return list;
21 }
22 public int quick(int[] input, int start, int end){
23 int base = input[start];
24 int startNum = start;
25 while(start<end){
26 while(start<end&&input[end]>=base){
27 end--;
28 }
29 while(start<end&&input[start]<=base){
30 start++;
31 }
32 swap(input,start,end);
33 }
34 swap(input,start,startNum);
35 return start;
36 }
37 public void swap(int[] input, int i, int j){
38 int tmp = input[i];
39 input[i] = input[j];
40 input[j] = tmp;
41 }
42 }
题三:【连续子数组的最大和】
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
分析:求数组中最大连续子数组的和;动态规划;
数组:[6 -3 -2 7 -15 1 2 2]
dp[i]:array[i]和之前所有元素的和最大值;
dp[i] = max{ array[i]+dp[i-1] , array[i] };//如果之前和当前值加在一起还没有当前值大,那么之前的那些都没有价值,还不如直接再从array[i]处开始计算。
sum:整个数组最大子数组的和,也就是dp[i]的最大值;
sum = max{ sum, dp[i] };
1 import java.lang.Math;
2 public class Solution {
3 public int FindGreatestSumOfSubArray(int[] array) {
4 int dp = array[0];//array[i]和之前所有元素的和最大值;
5 int sum = array[0];//整个数组最大子数组的和,也就是dp[i]的最大值
6 for(int i=1;i<array.length;i++){
7 dp = Math.max(array[i],array[i]+dp);
8 sum = Math.max(sum, dp);
9 }
10 return sum;
11 }
12 }
题四:【整数中1出现的次数】
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
分析:取余判定,暴力,时间复杂度O(n*k),k是位数。
1 public class Solution { 2 public int NumberOf1Between1AndN_Solution(int n) { 3 if(n<=0) return 0; 4 if(n==1) return 1; 5 int count = 1; 6 for(int i=2;i<=n;i++){ 7 int tmp = i; 8 while(tmp!=0){ 9 int remainder = tmp%10; 10 tmp=tmp/10; 11 if(remainder==1) count++; 12 } 13 } 14 return count; 15 } 16 }
题五:【把数组排成最小的数】
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
分析1:先比较每个数的最左位置的数,最小的放在最高位,例如 123 45 6 234,其中123最左边的1是该次比较的最小值,因此把123放在最高位。如果最左位相等,就比较左二位置,直到最后。
分析2:题目中返回值是String,则可以使用String将两个数字拼接。可以自定义一个函数比较字符串大小,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。遍历数组比较一遍之后可以得到排好序的字符串数组,最后可以将拼接整个字符串数组。
1 import java.util.ArrayList; 2 3 public class Solution { 4 public String PrintMinNumber(int [] numbers) { 5 String res = ""; 6 for(int i=0;i<numbers.length;i++){ 7 for(int j=i+1;j<numbers.length;j++){ 8 int a = Integer.valueOf(numbers[i]+""+numbers[j]); 9 int b = Integer.valueOf(numbers[j]+""+numbers[i]); 10 if(a>b){ 11 int tmp = numbers[i]; 12 numbers[i] = numbers[j]; 13 numbers[j] = tmp; 14 } 15 } 16 } 17 for(int i=0;i<numbers.length;i++){ 18 res = res+numbers[i]; 19 } 20 return res; 21 } 22 }