一、二分查找法(無重複數)
1、算法思想
針對一個有序數據集合a(無重複數),查找元素x的下標位置。我們可以將n個元素分成大致相等的兩部分,取a[n/2]與x做比較,如果x=a[n/2],則找到了x,算法中止;如果x<a[n/2],則只要在數組a的左半部分繼續搜索x;如果x>a[n/2],則只要在數組a的右半部搜索x。
2、時間複雜度:O(logn)
3、代碼實現(Java)
(1)通過while循環實現:
public static void main(String[] args) {
int[] a = {1, 3, 5, 6, 10, 12};
System.out.println(binarySearch(a, 10));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid;
while (low <= high) {
mid = low + (high - low) / 2;
if (x == a[mid]) {
return mid;
}
else if (x < a[mid]) {
high = mid - 1;
}
else {
low = mid + 1;
}
}
return -1;
}
(2)通過for循環實現
public static void main(String[] args) {
int[] a = {1, 3, 5, 6, 10, 12};
System.out.println(binarySearch(a, 10));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid = low + (high - low) / 2;
for (int i = mid; low <= high; mid = low + (high - low) / 2) {
if (x == a[mid]) {
return mid;
}
else if (x < a[mid]) {
high = mid - 1;
}
else {
low = mid + 1;
}
}
return -1;
}
二、二分查找(有重複數)
如果一個n個數的有重複數的有序數據集合a,如果查找的x有連續多個,返回一個列表,保存x的起始位置;如果查找的x只有一個,則直接返回該數下標;如果沒找到,則返回列表-1。
public static void main(String[] args) {
int[] a = {1, 3, 3, 3, 5, 6, 10, 12, 12};
System.out.println(binarySearch(a, 12));
}
// 有序數組,有重複數,返回查找數的起始位置
public static ArrayList<Integer> binarySearch(int[] a, int x) {
ArrayList<Integer> result = new ArrayList<>();
if (a == null || a.length == 0) {
result.add(-1);
return result;
}
int low = 0;
int high = a.length - 1;
int mid = low + (high - low) / 2;
int start, end;
boolean isExist = false; // 記錄是否找到x
while(low <= high) {
mid = low + (high - low) / 2;
if (x < a[mid]) {
high = mid - 1;
}
else if (x > a[mid]) {
low = mid + 1;
}
else {
isExist = true; // 查找到至少一個x
break;
}
}
// 如果沒有找到x,則直接返回-1
if (!isExist) {
result.add(-1);
return result;
}
// 如果已經找到了元素,則先從左邊繼續找
start = mid;
end = mid;
// 如果起始位置大於等於0且a[start] == x,則繼續
while(start >= 0 && a[start] == x) {
start--;
}
start = start + 1;
// 如果終點位置小於等於數組長度且a[end] == x,則繼續
while(end <= high && a[end] == x) {
end++;
}
end = end - 1;
// 如果起始位置相等,說明只找到一個數
if (start == end) {
result.add(start);
}
else {
result.add(start);
result.add(end);
}
return result;
}
三、二分查找第一個等於給定的數的下標
public static void main(String[] args) {
int[] a = {1, 3, 3, 3, 3, 6, 10, 12, 12};
System.out.println(binarySearch(a, 3));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid;
while(low <= high) {
mid = low + (high - low) / 2;
if (x < a[mid]) {
high = mid - 1;
}
else if(x > a[mid]){
low = mid + 1;
}
// 否則,x == a[mid],至少找到一個值等於x
else {
// 如果中間位置等於最左邊,或者a[mid - 1] != x,說明再往左沒有找到等於x的值,則此時第一個等於x的值位置就是mid
if (mid == low || a[mid - 1] != x) {
return mid;
}
high = mid - 1;
}
}
return -1;
}
四、二分查找最後一個等於給定的數的下標
public static void main(String[] args) {
int[] a = {1, 3, 3, 3, 3, 6, 10, 12, 12};
System.out.println(binarySearch(a, 3));
}
public static int binarySearch(int[] a, int x) {
if (a == null || a.length == 0) {
return -1;
}
int low = 0;
int high = a.length - 1;
int mid;
while (low <= high) {
mid = low + (high - low) / 2;
if (x < a[mid]) {
high = mid - 1;
}
else if(x > a[mid]){
low = mid + 1;
}
else {
if (mid == high || a[mid + 1] != x) {
return mid;
}
low = mid + 1;
}
}
return -1;
}