1. 數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。
如果直接排序,後進行選擇,則時間複雜度最優爲O(nlogn),這裏有兩種思路。
第一種,利用快速排序的思想,找到第n/2位置的數。然後掃描數組,確保這個數的確超過數組長度的一半。
第二種,設置兩個標記,一個記錄值,一個記錄次數,當後一個與前一個不同時,記錄數減一,當記錄數減少到0時候,需要更改值的標記。
時間複雜度都爲O(n),兩種方法代碼分別如下:
int MoreThanHalfNum_Solution1(int[] array) {
int length = array.length;
//基於快排思想
int loc = partition(array, 0, length - 1);
while (loc != length / 2) {
if (loc > length / 2) {
loc = partition(array, 0, loc - 1);
} else {
loc = partition(array, loc + 1, length - 1);
}
}
int value = array[loc];
int count = 0;
for (int i = 0; i < length; i++) {
if (array[i] == value)
count++;
}
if (count > length / 2)
return value;
else
return 0;
}
int partition(int[] array, int low, int high) {
if (low >= high) return low;
int temp = array[low];
while (low < high) {
while (low < high && array[high] >= temp) high--;
array[low] = array[high];
while (low < high && array[low] <= temp) low++;
array[high] = array[low];
}
array[low] = temp;
return low;
}
設置兩個標記來處理,最後還得驗證是否超過一半:
int MoreThanHalfNum_Solution2(int[] array) {
int length = array.length;
int key = array[0], times = 1;
for (int i = 1; i < length; i++) {
if (times == 0) {
key = array[i];
times++;
} else {
if (array[i] != key)
times--;
else
times++;
}
}
int count = 0;
for (int i = 0; i < length; i++) {
if (array[i] == key)
count++;
}
if (count > length / 2)
return key;
else
return 0;
}
2.把只包含素因子2、3和5的數稱作醜數(Ugly Number), 求按從小到大的順序的第N個醜數。
不考慮的效率的話,直接迭代去判斷每個數是否是醜數,直到得到第N個醜數。效率比較低下。
由於後面的醜數可以通過前面的醜數乘上因子獲得,現在設置三個標記來標記依次產生的最小丑數,這個醜數則爲下一個醜數,則這個醜數的標記前進一步,若多個標記產生的醜數都是最小的,則多個標記共同前進一步,直到獲得第N個醜數。
代碼如下:
int GetUglyNumber_Solution(int index) {
if (index < 2) return index;
int[] uglyArray = new int[index];
uglyArray[0] = 1;
int index_2 = 0, index_3 = 0, index_5 = 0;
int i = 0, max = 0;
while (i < index - 1) {
int max_2 = uglyArray[index_2] * 2;
int max_3 = uglyArray[index_3] * 3;
int max_5 = uglyArray[index_5] * 5;
max = max_2 <= max_3 ? max_2 : max_3;
max = max <= max_5 ? max : max_5;
uglyArray[++i] = max;
if (max == max_2)
index_2++;
if (max == max_3)
index_3++;
if (max == max_5)
index_5++;
}
return uglyArray[i];
}
3.輸出所有和爲S的連續正數序列。序列內按照從小至大的順序,序列間按照開始數字從小到大的順序。
若限制爲兩個數的和,可以首尾各設一個指針,計算兩個指針指向值的和,大於S,則尾指針向前移動,再迭代計算。若小於S,則首指針向後移動,計算和。一直到滿足要求爲止。
這個題目要求求出連續數字的和,不限元素個數。這裏繼續設置兩個指針,一個放在第一位,一個放在第二位,求累積和,小於S則後面指針向後移動,再累計比較。若大於S則前一個指針向後移動,再累計求和比較,直到滿足要求爲止,可以求出多個結果。
ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> array = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> one = new ArrayList<Integer>();
int len = sum / 2 + 1;
int start = 1, end = 2;
int total = start + end;
while (start != len) {
if (total == sum) {
for (int i = start; i <= end; i++) {
one.add(i);
}
array.add((ArrayList<Integer>)one.clone());
one.clear();
if (end == len)
break;
else {
total += ++end;
}
}
if (total < sum) {
total += ++end;
}
if (total > sum) {
total -= start++;
}
}
return array;
}
4.在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數
如果直接暴力來計算,可以想象成直接插入排序算法,當數組通過插入排序算法處理完畢時候,數字移動的個數就是逆序對的總數。時間複雜度沒有下面的算法優。
利用歸併算法。將數組分爲兩部分,將這兩部分再遞歸處理。當兩個數組合併成有序的序列時候,進行逆序統計,生成新排序數組是從數組尾部開始生成,也就是從兩個有序的數組中,先找最大的數。設置兩個指針,一個指向前一個有序數組的尾部,一個指向後一個有序數組的尾部。進行比較,當前一個大比後一個大的時候,逆序數增加後一個有序數組的頭部到指針處的元素個數(若後一指針索引爲j,頭部的索引爲low,則增加j-low+1個),前一個指針前移。當後一個指針大,則逆序數不變。
最後的總逆序數爲前一半數組的逆序數加上後一半數組的逆序數,再加上兩個數組合並過程中統計的逆序數,即爲總逆序數。
int InversePairs(int [] array) {
if(array==null||array.length==0)
return 0;
int length = array.length;
return merge(array,0,length-1);
}
int merge(int[] array, int low, int high) {
int count1 = 0, count2 = 0;
if (low < high) {
int mid = (low + high) / 2;
count1 = merge(array, low, mid);
count2 = merge(array, mid + 1, high);
count1 = count1 + count2 + mergeAndCount(array, low, mid, high);
}
return count1;
}
int mergeAndCount(int[] array, int low, int mid, int high) {
int[] b = new int[array.length];
for (int i = 0; i < b.length; i++) {
b[i] = array[i];
}
int count = 0;
int i, j, k;
for (i = mid, j = high, k = high; i >= low && j > mid; ) {
if (b[i] > b[j]) {
count += j - mid;
array[k--] = b[i--];
} else {
array[k--] = b[j--];
}
}
while (i >= low) array[k--] = b[i--];
while (j > mid) array[k--] = b[j--];
return count;
}
>5. 設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。
回溯法,設置一個標記,標記是否可以訪問。匹配成功一個字符後,則向四個方向繼續匹配,失敗則清除不可訪問標記。
代碼如下:
boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
int[] flag = new int[matrix.length];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++) {
if (isHas(matrix, rows, cols, i, j, str, 0, flag))
return true;
}
return false;
}
boolean isHas(char[] a, int rows, int cols, int i, int j, char[] str, int k, int[] flag) {
int index = i * cols + j;
if (i < 0 || j < 0 || i >= rows || j >= cols || flag[index] == 1 || a[index] != str[k])
return false;
if (k == str.length - 1)
return true;
flag[index] = 1;
if (isHas(a, rows, cols, i - 1, j, str, k + 1, flag) || isHas(a, rows, cols, i + 1, j, str, k + 1, flag) ||
isHas(a, rows, cols, i, j - 1, str, k + 1, flag) || isHas(a, rows, cols, i, j + 1, str, k + 1, flag))
return true;
flag[index] = 0;//失敗則去除標記
return false;
}