双调数组(Bitonic Array)最大值与搜索问题
Question:
First, a bitonic array for this question is defined as one such that for some index
K
in an array of length N where 0 < K < N - 1 and 0 to K is a monotonically increasing sequence of integers, and K to N - 1 is a monotonically decreasing sequence of integers.Example: [1, 3, 4, 6, 9, 14, 11, 7, 2, -4, -9]. It monotonically increases from 1 to 14, then decreases from 14 to -9.
Given a bitonic array and element x, find the index of x in ~2log(n) time.
解法1:
如果没有时间复杂度~2log(n)的限制,一个很自然的方法就是找到单调增与单调减的分界点,即整个数组的最大值,然后对最大值左侧做二分查找,对最大值右侧做逆序的二分查找。此时,要解决的问题就是如果找到最大值点。
最大值点的充分必要条件是,它即大于左边的元素,又大于右边的元素。对于数组arr中的元素arr[p],如果arr[p]小于右边的元素arr[p + 1],则数组的最大值一定在p之前,否则最大值一定在p或者p之后。因此,可以考虑用二分法夹逼求得最大值所在位置。
int findMax(int[] array, int left, int right) { int mid; while (left < right) { mid = (left + right) / 2; if (array[mid] < array[mid + 1]) { left = mid + 1; } else { right = mid; } } return right; }
求出最大值之后,对左侧做二分查找,如果没有找到则继续对右侧做降序的二分查找。
int deBisearch(int[] array, int key, int left, int right) { int mid; while (left <= right) { mid = (left + right) / 2; if (array[mid] > key) { left = mid + 1; } else if (array[mid] < key) { right = mid - 1; } else { return mid; } } return -1; } int bisearch(int[] array, int key, int left, int right) { int mid; while (left <= right) { mid = (left + right) / 2; if (array[mid] < key) { left = mid + 1; } else if (array[mid] > key) { right = mid - 1; } else { return mid; } } return -1; } int bitonicSearch(int[] array, int key, int left, int right) { int indexOfMax = findMax(array, left, right); int result = bisearch(array, key, left, indexOfMax); if (result == -1) { result = deBisearch(array, key, indexOfMax + 1, right); } return result; }
解法2:
由于二分查找本身的时间复杂度就是~2log(n),因此再加上一次搜索最大值会变成~3log(n),不满足题目要求,因此需要开发一个不查找最大值的二分查找算法。
对于原始的双调数组,中点位置mid可能在上升区间 [0, K],或者下降区间 [K + 1, N -1] 两种情况。
对于待查找的key,如果 key > arr[mid],且mid在上升区间,那么应该继续在右侧[mid + 1, right]寻找;如果mid在下降区间,那么应该继续在左侧[left, mid - 1]递归寻找。这一待查找的区间可能仍是一个双调数组。
如果key < arr[mid],且mid在上升区间,此时应该在mid左侧[left, mid]做增序的二分查找,在右侧[mid, right]做降序的二分查找。因为从arr[mid]一直到最大值,数组每一个元素都大于key,因此即便二分查找的新的中点落在了mid右侧、最大值左侧的升序区间,也会由于大于key而向右移动,直到移动到降序区间。同理,如果mid在下降区间,要在右侧进行降序二分查找,在左侧做升序的二分查找。
int bitonicSearch(int[] array, int key, int left, int right) { if (left > right) { return -1; } int mid = (left + right) / 2; if (array[mid] < key) { // ascend if (array[mid] < array[mid + 1]) { return bitonicSearch(array, key, mid + 1, right); } // descend else { return bitonicSearch(array, key, left, mid - 1); } } else { int result = bisearch(array, key, left, mid); if (result == -1) { result = deBisearch(array, key, mid, right); } return result; }
}
References:
http://stackoverflow.com/questions/19372930/given-a-bitonic-array-and-element-x-in-the-array-find-the-index-of-x-in-2logn/24098821
http://codereview.stackexchange.com/questions/104317/find-maximum-element-in-bitonic-array