剑指37.数字在升序数组中出现的次数
题目描述
统计一个数字在升序数组中出现的次数。
思路
考察的是二分查找,最优解时间复杂度为O(logn)。 二分的前提:有序(一提到有序,必须立马想到二分!)
思路1:先用二分查找找到指定的数字,如果存在,再在两侧顺序扫描第一个和最后一个。因为要查找的数字可能出现n次,所以时间复杂度为O(n)。因此,这种方法和直接从头到尾扫描整个数组统计某个数字出现的次数的方法是一样的。
思路2:用二分查找「递归or非递归」分别找到第一个k和最后一个k,然后二者位置index做差即可。 (时间复杂度为O(logn))
思路3:因为data中都是整数,因此可以不搜索k的两个位置,而是搜索k-0.5和k+0.5这两个数应该插入的位置,然后相减即可。
public class Solution { public int GetNumberOfK(int [] array , int k) { if (array == null || array.length == 0) return 0; return help(array,k+1) - help(array,k); } private int help(int[] arr, int target) { int l = 0, r = arr.length - 1; while (l <= r) { int mid = l + (r - l) / 2; if (arr[mid] >= target) { r = mid - 1; }else { l = mid + 1; } } return l; } }
解法2
2.1非递归
public class Solution { private int findFirstPosition(int[] arr, int k){ int low = 0; int high = arr.length - 1; while (low < high){ int mid = low + ((high - low) >> 1); if (k == arr[mid]){ if (mid - 1 >= 0 && arr[mid - 1] == k){ high = mid - 1; }else{ return mid; } }else if(k < arr[mid]){ high = mid - 1; }else{ low = mid + 1; } } return low; } private int findLastPosition(int[] arr, int k){ int low = 0; int high = arr.length - 1; while (low < high){ int mid = low + ((high - low) >> 1); if (k == arr[mid]){ if (mid + 1 < arr.length && arr[mid + 1] == k){ low = mid + 1; }else{ return mid; } }else if(k < arr[mid]){ high = mid - 1; }else{ low = mid + 1; } } return low; } public int GetNumberOfK(int [] array , int k) { if (array == null || array.length == 0) return 0; int first = findFirstPosition(array,k); if (array[first] != k) return 0; // 如果数组中不存在k int last = findLastPosition(array,k); return last - first + 1; } }
2.2 递归
public class Solution { public int GetNumberOfK(int [] array , int k) { if (array == null || array.length == 0) return 0; int first = findFirstPosition(array,0,array.length-1,k); if (first == -1) return 0; // 如果数组中不存在k int last = findLastPosition(array,0,array.length-1,k); return last - first + 1; } private int findFirstPosition(int[] array, int low, int high, int k) { if (low > high) //递归结束条件(说明左边没有找到k) return -1; int mid = low + ((high - low) >> 1); if (array[mid] == k){ if (mid - 1 >=0 && array[mid - 1] == k) high = mid - 1; else return mid; }else if (array[mid] > k){ high = mid - 1; }else{ low = mid + 1; } return findFirstPosition(array,low,high,k); } private int findLastPosition(int[] array, int low, int high, int k) { if (low > high) //递归结束条件(说明右边没有找到k) return -1; int mid = low + ((high - low) >> 1); if (array[mid] == k){ if (mid + 1 < array.length && array[mid + 1] == k) low = mid + 1; else return mid; }else if (array[mid] > k){ high = mid - 1; }else{ low = mid + 1; } return findLastPosition(array,low,high,k); } }
解法3
public class Solution { // 因为data中都是整数,因此可以不搜索k的两个位置,而是搜索k-0.5和k+0.5 // 这两个数应该插入的位置,然后相减即可。 public int GetNumberOfK(int [] array , int k) { if (array == null || array.length == 0) return 0; return search(array,k + 0.5) - search(array,k - 0.5); } private int search(int[] arr, double num) { int low = 0; int high = arr.length - 1; while (low <= high){ int mid = low + ((high - low) >> 1); if (num < arr[mid]){ high = mid - 1; }else{ // num 比中间的数要大 low = mid + 1; } } return low; } }
Note:寻找前一个数应该是最后 <k 的整数,而寻找后面一个则应该寻找第一个 >k 的整数。 另外,如果数组没有这个数,那么两次获取的位置是一样的,相减得到0