双调数组(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

 

posted @ 2016-10-04 21:15  厚礼  阅读(1126)  评论(0编辑  收藏  举报