題目描述(一)
統計一個數字在排序數組中出現的次數。{1,2,3,3,3,3,4,5} 3出現了4次。
解題思路
- 當然可以無腦用哈希表或者直接遍歷,空間複雜度O(n),時間複雜度O(1)代碼略
- 優化:在找第一個k時,首先也是二分找到中間的k,然後和它上一個數字相比,如果還是k,則在前半段數組繼續二分,直到找到中間的數爲k且前一個不等於k。
- 用同樣的方法,找最後一個k
時間複雜度爲O(logn)
參考代碼:
package offer;
/**
* 在排序數組中查找數字 統計一個數字在排序數組中出現的次數
*/
public class Offer53 {
public static void main(String[] args) {
int nums[] = {2, 2, 2, 2, 2, 2, 3, 5};
int target = 2;
System.out.println(GetNumberOfK(nums, target));
}
private static int GetFirstK(int[] data, int k, int start, int end) {
if (start > end) {
return -1;
}
int middIndex = (start + end) / 2;
int middData = data[middIndex];
if (middData == k) {
if ((middIndex > 0 && data[middIndex - 1] != k) || middIndex == 0) {
return middIndex;
} else {
end = middIndex - 1;
}
} else if (middData > k) {
end = middIndex - 1;
} else {
start = middIndex + 1;
}
return GetFirstK(data, k, start, end);
}
private static int GetLastK(int[] data, int k, int start, int end) {
if (start > end) {
return -1;
}
int middIndex = (start + end) / 2;
int middData = data[middIndex];
if (middData == k) {
if ((middIndex < data.length - 1 && data[middIndex + 1] != k) || middIndex == end) {
return middIndex;
} else {
start = middIndex + 1;
}
} else if (middData > k) {
end = middIndex - 1;
} else {
start = middIndex + 1;
}
return GetLastK(data, k, start, end);
}
public static int GetNumberOfK(int[] data, int k) {
int number = 0;
if (data != null && data.length > 0) {
int first = GetFirstK(data, k, 0, data.length - 1);
int last = GetLastK(data, k, 0, data.length - 1);
if (first > -1 && last > -1) {
number = last - first + 1;
}
}
return number;
}
}
題目描述(二)
一個長度爲n-1的遞增排序數組中的所有數字都是唯一的,並且每個數字都在範圍0~n-1之內。在範圍0 ~ n-1內的n個數字中有且只有一個數字不在該數組中,請找出這個數字。
解題思路
-用公式n*(n-1)/2求出0~n-1所有數字之和s1,在求出數組所有數字之和s2,那個不在數組中的數字就是s1-s2的差,這種解法需要O(n)的時間
- 如果中間元素的值和它的下標相等,那麼下一輪只需要找它的右半邊
- 如果中間元素的值和它的下標不等,並且它前一個元素和它的下標相等,意味着這個中間的數字就是第一個值與下標不等的數字。
- 如果中間元素的值和它的下標不等,並且他前面的元素和它的下標相等不等,那麼只需查找左半邊。
參考代碼:
package offer;
/**
* 0-n-1中缺失的數字
*/
public class Offer53_2 {
public static void main(String[] args) {
int nums[] = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10};
//System.out.println(getMissingNum(nums));
System.out.println(getMissingNumH(nums));
}
/**
* 一般 O(n)
*
* @param nums
* @return
*/
private static int getMissingNum(int[] nums) {
if (nums == null || nums.length <= 0) {
return -1;
}
int sum1 = 0;
int sum2 = (nums[0] + nums[nums.length - 1]) * (nums.length + 1) / 2;
for (int i = 0; i < nums.length; i++) {
sum1 += nums[i];
}
return sum2 - sum1;
}
/**
* 改良
* {0, 1, 2, 3, 4, 5, 7, 8, 9, 10}
*
* @param arr
* @return
*/
private static int getMissingNumH(int[] arr) {
if (arr == null || arr.length <= 0)
return -1;
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = (low + high) >> 1;
if (arr[mid] != mid) {
if (mid == 0 || arr[mid - 1] == mid - 1)
return mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
}
題目描述(三)
一個長度爲n-1的遞增排序數組中的所有數字都是唯一的,並且每個數字都在範圍0到n-1之內。在範圍0到n-1的n個數字中有且只有一個數字不在該數組中,請找出這個數字。
解題思路
- 先獲取數組的中間數,若中間數的值和下標相等,則找到一個滿足條件的數;
- 若中間數的值大於它的下標,則根據數組遞增的特性可知:中間元素至少比他的下標大1,而且中間數以後的元素的值至少每個比前面大1,同時它們的下標每次也是增加1,所以右邊的這些元素的值都大於它們的下標(至少大1),因此需要繼續在左邊尋找。
- 同理,若中間數的值小於它的下標,則中間數左邊的那些元素的值也都小於它們的下標,因此需要繼續在右邊尋找。
參考代碼:
package offer;
/**
* 數組中數值和下標相等的元素
*/
public class Offer53_3 {
public static void main(String[] args) {
int nums[] = {-3, -1, 1, 3, 5};
//System.out.println(getMissingNum(nums));
System.out.println(getMissingNumH(nums));
}
private static int getMissingNumH(int[] arr) {
if (arr == null || arr.length <= 0)
return -1;
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) >> 1;
if (arr[mid] == mid) {
return mid;
}
if (arr[mid] > mid) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
附錄
該題源碼在我的 ?Github 上面!