程式設計題-- 找出指定數在陣列中的範圍
阿新 • • 發佈:2019-01-06
1.題目
輸入一個排好序的整數陣列,找到指定目標數的開始和結束位置。如果指定的數字不在陣列中,則輸出 [-1,-1]。例如,輸入陣列為[5, 7, 7, 8, 8, 10], 目標數為8, 輸出[3, 4].本題會人工判題,要求時間複雜度O(logn) (來源於牛客網歡聚時代線上筆試程式設計題)
2.解法
使用二分法尋找目標數的開始和結束位置(注:這裡在用二分法找到目標數後不能用線性遍歷的方式找開始或結束位置,原因詳見程式碼註釋)。
這個問題的考察點一個是二分查詢的使用,另外是邊值情況是否考慮周全。
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int target = scanner.nextInt();
scanner.nextLine();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = scanner.nextInt();
}
// begin和end分別為陣列的起始和結束位置
int begin = 0;
int end = n - 1;
// beginFoundLocation記錄找到的目標值第一次出現的地方
int beginFoundLocation = -1;
// endFoundLocation 記錄找到的目標值最後一次出現的地方
int endFoundLocation = -1;
endFoundLocation = findLastLocation(begin, end, arr, target);
beginFoundLocation = findFirstLocation(begin, end, arr, target);
System.out.println("[" + beginFoundLocation + "," + endFoundLocation + "]");
}
// 在arr陣列的begin到end的範圍內,尋找target第一次出現的位置
public static int findFirstLocation(int begin, int end, int[] arr, int target) {
// 記錄中間點的位置及其值
int middle = 0;
int middleValue = arr[0];
while (begin <= end) {
/*
* 二分查詢:當中間位置點的值比target要小的時候就將尋找的起始位置(begin)設定為中間位置的下一個位置,
* 當中間位置點的值比target要大的時候就將尋找的終止位置(end)設為中間位置的下一個位置
*/
middle = begin + (end - begin) / 2;
middleValue = arr[middle];
/*
* 注意:當中間位置的點的值正好是target的時候,需要看該位置的前一個位置點的值是否也為target,這就分為以下三種情況
* 1.可能此時中間位置已經是0,也就是指向了陣列的第一個元素,它沒有前一個位置點,說明target第一次出現的位置就是0
* 2.中間位置不是0,而且中間位置的前一個位置的值也是target,則繼續用二分法在前面查詢target
* (注:這裡不能用線性探測的方法,因為當輸入陣列元素值全部正好是target的時候,就成了遍歷整個陣列前半段,
* 同理找最後target出現的位置也不能用線性探測法)。
* 3.中間位置不是0,而且中間位置的前一個位置的值不是target,則正好該中間位置就是target第一次出現的位置,返回就好了。
*/
if (middleValue < target) {
begin = middle + 1;
} else if (middleValue > target) {
end = end - 1;
} else if (middle > 0 && arr[middle - 1] == target) {
end = middle - 1;
} else if (middle > 0 && arr[middle - 1] != target) {
return middle;
} else if (middle == 0) {// 這裡需要判定下,因為會有begin=end=0的情況,會死迴圈。
return 0;
}
}
// 當迴圈條件不滿足的時候,可能是找到了target第一次出現的位置,也可能是target根本不在陣列中
if (arr[middle] != target) {
return -1;
} else {
return middle;
}
}
// 尋找target最後出現的位置,具體原理和尋找最初出現的位置相似
public static int findLastLocation(int begin, int end, int[] arr, int target) {
int middle = 0;
int middleValue = arr[0];
int lastArr = arr.length - 1;
while (begin <= end) {
middle = begin + (end - begin) / 2;
middleValue = arr[middle];
if (middleValue < target) {
begin = middle + 1;
} else if (middleValue > target) {
end = end - 1;
} else if (middle < lastArr && arr[middle + 1] == target) {
begin = middle + 1;
} else if (middle < lastArr && arr[middle + 1] != target) {
return middle;
} else if (middle == lastArr) {// 同樣是為了防止死迴圈,需要判斷下
return lastArr;
}
}
if (arr[middle] != target) {
return -1;
} else {
return middle;
}
}
}