判斷字元陣列中是否所有的字元都只出現過一次 & 在有序但含有空的陣列中查詢字串
阿新 • • 發佈:2022-04-17
判斷字元陣列中是否所有的字元都只出現過一次
《程式設計師程式碼面試指南》第81題 P261 難度:要求1:士★☆☆☆ 要求2:尉★★☆☆
要求1很簡單,時間複雜度為O(N),遍歷一遍chas,用map記錄每種字元的出現情況即可。書中使用了長度固定的陣列,也可以使用雜湊表來實現。
public boolean isUnique1(char[] chas) { if (chas == null) { return true; } boolean[] map = new boolean[256]; for (int i = 0; i < chas.length; i++) { if (map[chas[i]]) { return false; } map[chas[i]] = true; } return true; }
要求2的整體思路是先將chas排序,排序後相同的字元就放在一起。所以問題的關鍵是選擇什麼樣的排序演算法,使得額外空間複雜度為O(1),並且時間複雜度儘量低。
public boolean isUnique2(char[] chas) { if (chas == null) { return true; } heapSort(chas); for (int i = 1; i < chas.length; i++) { if (chas[i] == chas[i - 1]) { return false; } } return true; } public void heapSort(char[] chas) { for (int i = 0; i < chas.length; i++) { heapInsert(chas, i); } for (int i = chas.length - 1; i > 0; i--) { swap(chas, 0, i); heapify(chas, 0, i); } } public void heapInsert(char[] chas, int i) { int parent = 0; while (i != 0) { parent = (i - 1) / 2; if (chas[parent] < chas[i]) { swap(chas, parent, i); i = parent; } else { break; } } } public void heapify(char[] chas, int i, int size) { int left = i * 2 + 1; int right = i * 2 + 2; int largest = i; while (left < size) { if (chas[left] > chas[i]) { largest = left; } if (right < size && chas[right] > chas[largest]) { largest = right; } if (largest != i) { swap(chas, largest, i); } else { break; } i = largest; left = i * 2 + 1; right = i * 2 + 2; } } public void swap(char[] chas, int index1, int index2) { char tmp = chas[index1]; chas[index1] = chas[index2]; chas[index2] = tmp; }
在有序但含有空的陣列中查詢字串
《程式設計師程式碼面試指南》第82題 P263 難度:尉★★☆☆
【解答】
本題的解法儘可能多地使用了二分查詢,具體過程如下:
- 假設在strs[left..right]上進行查詢的過程,全域性整型變數res表示字串str在strs中最左的位置。初始時,left=0,right=strs.length-1, res=-1。
- 令mid=(left+right)/2,則strs[mid]為strs[left..right]中間位置的字串。
- 如果字串strs[mid]與str一樣,說明找到了str,令res=mid。但要找的是最左的位置
- 如果字串strs[mid]與str不一樣,並且strs[mid]!=null,此時可以比較strs[mid]和str,如果strs[mid]的字典順序比str小,說明整個左半區不會出現str,需要在右半區尋找,所以令left=mid+1,然後重複步驟2。
- 如果字串strs[mid]與str不一樣,並且strs[mid]==null,此時從mid開始,從右到左遍歷左半區(即strs[left..mid])。如果整個左半區都為null,那麼繼續用二分的方式在右半區上查詢(即令left=mid+1),然後重複步驟2。如果整個左半區不都為null,假設從右到左遍歷strs[left..mid]時,發現第一個不為null的位置是i,那麼把str和strs[i]進行比較。如果strs[i]字典順序小於str,同樣說明整個左半區沒有str,令left=mid+1,然後重複步驟2。如果strs[i]字典順序等於str,說明找到str,令res=mid,但要找的是最左的位置,還要在strs[left..i-1]上尋找,看有沒有更左的str出現,所以令right=i-1,然後重複步驟2。如果strs[i]字典順序大於str,說明strs[i..right]上都沒有str,需要在strs[left..i-1]上查詢,所以令right=i-1,然後重複步驟2。
具體過程請參看如下程式碼中的getlndex方法。
public int getIndex(String[] strs, String str) {
if (strs == null || strs.length == 0 || str == null) {
return -1;
}
int res = -1;
int left = 0;
int right = strs.length - 1;
int mid = 0;
int i = 0;
while (left <= right) {
mid = (left + right) / 2;
if (strs[mid] != null && strs[mid].equals(str)) {
res = mid;
right = mid - 1;
} else if (strs[mid] != null) {
if (strs[mid].compareTo(str) < 0) {
left = mid + 1;
} else {
right = mid - 1;
}
} else {
i = mid;
while (strs[i] == null && --i >= left)
;
if (i < left || strs[i].compareTo(str) < 0) {
left = mid + 1;
} else {
res = strs[i].equals(str) ? i : res;
right = i - 1;
}
}
}
return res;
}