题目:

数字在排序数组中出现的次数。

统计一个数字在排序数组中出现的次数。例如,输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4。

分析:

数组是有序的,自然想到用二分查找,二分查找到一个数据后,它的左右可能还有相同的数字,这时候,要向左右遍历,统计数量,考虑一个极端情况,假设数组中的数字全都相同,通过二分查找到中间数字后,继续向两侧判断和统计,此时时间复杂度是O(n),不是最佳的方案,还有更好的方法。

假设我们知道了相同值的第一个和最后一个,last-start+1就是出现的次数了,那么就需要找last和first,不同于前面的遍历查找,试想,在一些连续的,值相同的数组中,first和last和mid有什么区别呢?first的前一个要么是空,要么小于first的值,last的后一个要么是空,要么大于last的值,根据这个条件,可以区分开first,mid,last。找到mid后,继续使用二分的方法查找first和last,判别条件就是前面提到的判断相邻的元素,此时时间复杂度是O(logn)。

解法:

package com.wsy;

public class Main {
public static int[] array;
public static int length;
public static int k;

public static void main(String[] args) {
array = new int[]{1, 2, 3, 3, 3, 3, 4, 5};
k = 3;
length = array.length;
findKTimes();
}

public static int findFirstK(int start, int end) {
if (start > end) {
return -1;
}
int mid = (start + end) >> 1;
if (array[mid] == k) {
if (mid > 0 && array[mid - 1] != k || mid == 0) {
return mid;
} else {
end = mid - 1;
}
} else if (array[mid] > k) {
end = mid - 1;
} else {
start = mid + 1;
}
return findFirstK(start, end);
}

public static int findLastK(int start, int end) {
if (start > end) {
return -1;
}
int mid = (start + end) >> 1;
if (array[mid] == k) {
if (mid < length - 1 && array[mid + 1] != k || mid == length - 1) {
return mid;
} else {
start = mid + 1;
}
} else if (array[mid] > k) {
end = mid - 1;
} else {
start = mid + 1;
}
return findLastK(start, end);
}

public static void findKTimes() {
int start = findFirstK(0, length - 1);
int end = findLastK(0, length - 1);
if (start != -1 && end != -1) {
System.out.println(k + "的出现次数是:" + (end - start + 1));
}
}
}

题目:

0~n-1中缺失的数字。

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

分析:

有一个直观的解决方案,利用公式求前n项和,再减去实际数组中的和,这个差值,就是缺少的数字,此时时间复杂度是O(n)。数组中的数字是0~n-1,对应下标也是0~n-1,那么有一些数据和下标值相等,有一些数据和下标值不相等,我们找到第一个不相等的值,就能知道缺失的数字是多少了。

我们可以基于二分查找用如下过程查找:如果中间元素的值和下标相等,那么下一轮查找只需要查找右半边;如果中间元素的值和下标不相等,并且它前面的一个元素和下标值相等,那么中间的数字正好是缺失的数字;如果中间元素的值和下标不相等,并且它前面的一个元素和下标也不相等,那么下一轮查找我们只需要在左半边查找即可。

解法:

package com.wsy;

public class Main {
public static void main(String[] args) {
int[] array = new int[]{0, 1, 2, 3, 4, 5, 7};
findMissNumber(array);
}

public static void findMissNumber(int[] array) {
int length = array.length;
int start = 0, end = length - 1;
while (start <= end) {
int mid = (start + end) >> 1;
if (array[mid] != mid) {
if (mid == 0 || array[mid - 1] == mid - 1) {
System.out.println("缺失的数字是:" + mid);
return;
} else {
end = mid - 1;
}
} else {
start = mid + 1;
}
}
}
}

题目:

数组中数值和下标相等的元素。

假设一个单调递增的数组里的每个元素都是整数并且是唯一的。请编程实现一个函数,找出数组中任意一个数值等于其下标的元素。假如,在数组{-3,-1,1,3,5}中,数字3和它的下标相等。

分析:

还是使用二分法,查找的依据是下标和数值相同的点。

解法:

package com.wsy;

public class Main {
public static void main(String[] args) {
int[] array = new int[]{-3, -1, 1, 3, 5};
findMissNumber(array);
}

public static void findMissNumber(int[] array) {
int length = array.length;
int start = 0, end = length - 1;
while (start <= end) {
int mid = (start + end) >> 1;
if (array[mid] == mid) {
System.out.println("数值和下标相等的是:" + mid);
return;
} else if (array[mid] > mid) {
end = mid - 1;
} else {
start = mid + 1;
}
}
}
}