在已排序和旋转的数组中搜索元素

70 阅读9分钟

在已排序和旋转的数组中搜索元素

给定一个大小为 N 的已排序且旋转的数组 arr[] 和一个目标值,任务是在数组中找到目标值。

注意: 请在 O(logN) 时间内找到元素并假设所有元素都是不同的。

例子:

输入:arr[] = {5, 6, 7, 8, 9, 10, 1, 2, 3}, key = 3
输出:在索引 8 处找到

输入:arr[] = {5, 6, 7, 8, 9, 10, 1, 2, 3}, key = 30
输出:未找到

输入:arr[] = {30, 40, 50, 10, 20}, key = 10
输出:在索引 3 处找到

方法一(寻找发生旋转的枢轴):解决问题的主要思路如下。

  • 对于排序(升序)和旋转的数组,枢轴元素是唯一一个其下一个元素小于它的元素。
  • 根据上述思想使用二分查找,即可找到枢轴。
    • 可以观察到,对于范围 [l, r] 中索引的搜索空间,其中中间索引为 mid,
      • 如果旋转发生在左半部分,那么显然 l 处的元素将大于 mid 处的元素。
      • 否则左半部分都是有序的,但 mid 处的元素将大于 r 处的元素。
  • 找到枢轴后,将数组分为两个子数组。
  • 现在各个子数组都是有序的,因此可以使用二分搜索来搜索目标元素。

请按照下面提到的步骤来实现这个想法:

  • 使用二分查找找出枢轴点。我们将low指针设置在第一个数组元素,将high指针设置在最后一个数组元素。
    • 我们将根据low和high计算mid。
    • 如果 mid-1 处的值大于 mid 处的值,则返回mid-1作为枢轴。
    • 否则,如果 mid+1 处的值小于 mid,则返回 mid 值作为枢轴。
    • 否则,如果low位置元素值大于中位元素值,则考虑左半部分。否则,请考虑右半部分。
  • 根据找到的枢轴将数组分成两个子数组。
  • 现在对两个子数组之一调用二分搜索。
    • 如果目标值大于第0个元素则在左侧数组中查找
    • 否则在右侧数组中搜索。
  • 如果在选定的子数组中找到该元素,则返回索引
  • 否则返回-1。

更好地理解

考虑 arr[] = {3, 4, 5, 1, 2}, key = 1
枢轴查找:
low = 0,high = 4:
=> mid = 2
=> arr[mid] = 5, arr[mid + 1] = 1
=> arr[mid] > arr[mid+1],
=> 因此枢轴 = mid = 2
数组分为两部分 {3, 4, 5}, {1, 2}
现在根据条件和目标值,我们需要在{1, 2}部分中查找
我们将对 {1, 2} 应用二分搜索。
low= 3 ,high= 4 。
=> mid = 3
=> arr[mid] = 1 ,key = 1,因此 arr[mid] = key 匹配。
=> 所需索引 = mid = 3
因此该目标元素位于索引 3 处。


下面是上述方法的实现:

C++

// C++ Program to search an element
// in a sorted and pivoted array

#include <bits/stdc++.h>
using namespace std;

// Standard Binary Search function
int binarySearch(int arr[], int low, int high, int key)
{
    if (high < low)
        return -1;

    int mid = (low + high) / 2;
    if (key == arr[mid])
        return mid;

    if (key > arr[mid])
        return binarySearch(arr, (mid + 1), high, key);

    return binarySearch(arr, low, (mid - 1), key);
}

// Function to get pivot. For array 3, 4, 5, 6, 1, 2
// it returns 3 (index of 6)
int findPivot(int arr[], int low, int high)
{
    // Base cases
    if (high < low)
        return -1;
    if (high == low)
        return low;

    // low + (high - low)/2;
    int mid = (low + high) / 2;
    if (mid < high && arr[mid] > arr[mid + 1])
        return mid;

    if (mid > low && arr[mid] < arr[mid - 1])
        return (mid - 1);

    if (arr[low] >= arr[mid])
        return findPivot(arr, low, mid - 1);

    return findPivot(arr, mid + 1, high);
}

// Searches an element key in a pivoted
// sorted array arr[] of size n
int pivotedBinarySearch(int arr[], int n, int key)
{
    int pivot = findPivot(arr, 0, n - 1);

    // If we didn't find a pivot,
    // then array is not rotated at all
    if (pivot == -1)
        return binarySearch(arr, 0, n - 1, key);

    // If we found a pivot, then first compare with pivot
    // and then search in two subarrays around pivot
    if (arr[pivot] == key)
        return pivot;

    if (arr[0] <= key)
        return binarySearch(arr, 0, pivot - 1, key);

    return binarySearch(arr, pivot + 1, n - 1, key);
}

// Driver program to check above functions
int main()
{
    // Let us search 3 in below array
    int arr1[] = { 5, 6, 7, 8, 9, 10, 1, 2, 3 };
    int n = sizeof(arr1) / sizeof(arr1[0]);
    int key = 3;

    // Function calling
    cout << "Index of the element is : "
         << pivotedBinarySearch(arr1, n, key);

    return 0;
}

C

/* Program to search an element in
   a sorted and pivoted array*/
#include <stdio.h>

int findPivot(int[], int, int);
int binarySearch(int[], int, int, int);

/* Searches an element key in a pivoted
   sorted array arrp[] of size n */
int pivotedBinarySearch(int arr[], int n, int key)
{
    int pivot = findPivot(arr, 0, n - 1);

    // If we didn't find a pivot,
    // then array is not rotated at all
    if (pivot == -1)
        return binarySearch(arr, 0, n - 1, key);

    // If we found a pivot, then first
    // compare with pivot and then
    // search in two subarrays around pivot
    if (arr[pivot] == key)
        return pivot;
    if (arr[0] <= key)
        return binarySearch(arr, 0, pivot - 1, key);
    return binarySearch(arr, pivot + 1, n - 1, key);
}

/* Function to get pivot. For array
   3, 4, 5, 6, 1, 2 it returns 3 (index of 6) */
int findPivot(int arr[], int low, int high)
{
    // base cases
    if (high < low)
        return -1;
    if (high == low)
        return low;

    int mid = (low + high) / 2; /*low + (high - low)/2;*/
    if (mid < high && arr[mid] > arr[mid + 1])
        return mid;
    if (mid > low && arr[mid] < arr[mid - 1])
        return (mid - 1);
    if (arr[low] >= arr[mid])
        return findPivot(arr, low, mid - 1);
    return findPivot(arr, mid + 1, high);
}

/* Standard Binary Search function*/
int binarySearch(int arr[], int low, int high, int key)
{
    if (high < low)
        return -1;
    int mid = (low + high) / 2; /*low + (high - low)/2;*/
    if (key == arr[mid])
        return mid;
    if (key > arr[mid])
        return binarySearch(arr, (mid + 1), high, key);
    return binarySearch(arr, low, (mid - 1), key);
}

/* Driver program to check above functions */
int main()
{
    // Let us search 3 in below array
    int arr1[] = { 5, 6, 7, 8, 9, 10, 1, 2, 3 };
    int n = sizeof(arr1) / sizeof(arr1[0]);
    int key = 3;
    printf("Index of the element is : %d",
           pivotedBinarySearch(arr1, n, key));
    return 0;
}

Java

/* Java program to search an element
   in a sorted and pivoted array*/
import java.io.*;
class Main {

    /* Searches an element key in a
       pivoted sorted array arrp[]
       of size n */
    static int pivotedBinarySearch(int arr[], int n,
                                   int key)
    {
        int pivot = findPivot(arr, 0, n - 1);

        // If we didn't find a pivot, then
        // array is not rotated at all
        if (pivot == -1)
            return binarySearch(arr, 0, n - 1, key);

        // If we found a pivot, then first
        // compare with pivot and then
        // search in two subarrays around pivot
        if (arr[pivot] == key)
            return pivot;
        if (arr[0] <= key)
            return binarySearch(arr, 0, pivot - 1, key);
        return binarySearch(arr, pivot + 1, n - 1, key);
    }

    /* Function to get pivot. For array
       3, 4, 5, 6, 1, 2 it returns
       3 (index of 6) */
    static int findPivot(int arr[], int low, int high)
    {
        // base cases
        if (high < low)
            return -1;
        if (high == low)
            return low;

        /* low + (high - low)/2; */
        int mid = (low + high) / 2;
        if (mid < high && arr[mid] > arr[mid + 1])
            return mid;
        if (mid > low && arr[mid] < arr[mid - 1])
            return (mid - 1);
        if (arr[low] >= arr[mid])
            return findPivot(arr, low, mid - 1);
        return findPivot(arr, mid + 1, high);
    }

    /* Standard Binary Search function */
    static int binarySearch(int arr[], int low, int high,
                            int key)
    {
        if (high < low)
            return -1;

        /* low + (high - low)/2; */
        int mid = (low + high) / 2;
        if (key == arr[mid])
            return mid;
        if (key > arr[mid])
            return binarySearch(arr, (mid + 1), high, key);
        return binarySearch(arr, low, (mid - 1), key);
    }

    // main function
    public static void main(String args[])
    {
        // Let us search 3 in below array
        int arr1[] = { 5, 6, 7, 8, 9, 10, 1, 2, 3 };
        int n = arr1.length;
        int key = 3;
        System.out.println(
            "Index of the element is : "
            + pivotedBinarySearch(arr1, n, key));
    }
}

Python

# Python Program to search an element
# in a sorted and pivoted array

# Searches an element key in a pivoted
# sorted array arrp[] of size n
def pivotedBinarySearch(arr, n, key):

    pivot = findPivot(arr, 0, n-1)

    # If we didn't find a pivot,
    # then array is not rotated at all
    if pivot == -1:
        return binarySearch(arr, 0, n-1, key)

    # If we found a pivot, then first
    # compare with pivot and then
    # search in two subarrays around pivot
    if arr[pivot] == key:
        return pivot
    if arr[0] <= key:
        return binarySearch(arr, 0, pivot-1, key)
    return binarySearch(arr, pivot + 1, n-1, key)


# Function to get pivot. For array
# 3, 4, 5, 6, 1, 2 it returns 3
# (index of 6)
def findPivot(arr, low, high):

    # base cases
    if high < low:
        return -1
    if high == low:
        return low

    # low + (high - low)/2;
    mid = int((low + high)/2)

    if mid < high and arr[mid] > arr[mid + 1]:
        return mid
    if mid > low and arr[mid] < arr[mid - 1]:
        return (mid-1)
    if arr[low] >= arr[mid]:
        return findPivot(arr, low, mid-1)
    return findPivot(arr, mid + 1, high)

# Standard Binary Search function
def binarySearch(arr, low, high, key):

    if high < low:
        return -1

    # low + (high - low)/2;
    mid = int((low + high)/2)

    if key == arr[mid]:
        return mid
    if key > arr[mid]:
        return binarySearch(arr, (mid + 1), high,
                            key)
    return binarySearch(arr, low, (mid - 1), key)


# Driver program to check above functions
# Let us search 3 in below array
if __name__ == '__main__':
    arr1 = [5, 6, 7, 8, 9, 10, 1, 2, 3]
    n = len(arr1)
    key = 3
    print("Index of the element is : ", \
          pivotedBinarySearch(arr1, n, key))

C#

// C# program to search an element
// in a sorted and pivoted array
using System;

class main {

    // Searches an element key in a
    // pivoted sorted array arrp[]
    // of size n
    static int pivotedBinarySearch(int[] arr,
                                   int n, int key)
    {
        int pivot = findPivot(arr, 0, n - 1);

        // If we didn't find a pivot, then
        // array is not rotated at all
        if (pivot == -1)
            return binarySearch(arr, 0, n - 1, key);

        // If we found a pivot, then first
        // compare with pivot and then
        // search in two subarrays around pivot
        if (arr[pivot] == key)
            return pivot;

        if (arr[0] <= key)
            return binarySearch(arr, 0, pivot - 1, key);

        return binarySearch(arr, pivot + 1, n - 1, key);
    }

    /* Function to get pivot. For array
    3, 4, 5, 6, 1, 2 it returns
    3 (index of 6) */
    static int findPivot(int[] arr, int low, int high)
    {
        // base cases
        if (high < low)
            return -1;
        if (high == low)
            return low;

        /* low + (high - low)/2; */
        int mid = (low + high) / 2;

        if (mid < high && arr[mid] > arr[mid + 1])
            return mid;

        if (mid > low && arr[mid] < arr[mid - 1])
            return (mid - 1);

        if (arr[low] >= arr[mid])
            return findPivot(arr, low, mid - 1);

        return findPivot(arr, mid + 1, high);
    }

    /* Standard Binary Search function */
    static int binarySearch(int[] arr, int low,
                            int high, int key)
    {
        if (high < low)
            return -1;

        /* low + (high - low)/2; */
        int mid = (low + high) / 2;

        if (key == arr[mid])
            return mid;
        if (key > arr[mid])
            return binarySearch(arr, (mid + 1), high, key);

        return binarySearch(arr, low, (mid - 1), key);
    }

    // Driver Code
    public static void Main()
    {
        // Let us search 3 in below array
        int[] arr1 = { 5, 6, 7, 8, 9, 10, 1, 2, 3 };
        int n = arr1.Length;
        int key = 3;
        Console.Write("Index of the element is : "
                      + pivotedBinarySearch(arr1, n, key));
    }
}

Javascript

<script>
/* JavaScript Program to search an element
   in a sorted and pivoted array*/

/* Standard Binary Search function*/
function binarySearch( arr, low,
                  high, key){
    if (high < low)
        return -1;

    let mid = Math.floor((low + high) / 2); /*low + (high - low)/2;*/
    if (key == arr[mid])
        return mid;

    if (key > arr[mid])
        return binarySearch(arr, (mid + 1), high, key);

    // else
    return binarySearch(arr, low, (mid - 1), key);
}

/* Function to get pivot. For array 3, 4, 5, 6, 1, 2
   it returns 3 (index of 6) */
function findPivot( arr, low, high){
    // base cases
    if (high < low)
        return -1;
    if (high == low)
        return low;

    let mid = Math.floor((low + high) / 2); /*low + (high - low)/2;*/
    if (mid < high && arr[mid] > arr[mid + 1])
        return mid;

    if (mid > low && arr[mid] < arr[mid - 1])
        return (mid - 1);

    if (arr[low] >= arr[mid])
        return findPivot(arr, low, mid - 1);

    return findPivot(arr, mid + 1, high);
}

/* Searches an element key in a pivoted
   sorted array arr[] of size n */
function pivotedBinarySearch( arr, n, key){
    let pivot = findPivot(arr, 0, n - 1);

    // If we didn't find a pivot,
    // then array is not rotated at all
    if (pivot == -1)
        return binarySearch(arr, 0, n - 1, key);

    // If we found a pivot, then first compare with pivot
    // and then search in two subarrays around pivot
    if (arr[pivot] == key)
        return pivot;

    if (arr[0] <= key)
        return binarySearch(arr, 0, pivot - 1, key);

    return binarySearch(arr, pivot + 1, n - 1, key);
}

/* Driver program to check above functions */
// Let us search 3 in below array
let arr1 = [ 5, 6, 7, 8, 9, 10, 1, 2, 3 ];
let n = arr1.length;
let key = 3;
// Function calling
document.write( "Index of the element is : "
         + pivotedBinarySearch(arr1, n, key));

</script>

PHP

<?php
// PHP Program to search an element
// in a sorted and pivoted array

// Standard Binary Search function
function binarySearch($arr, $low,
                      $high, $key)
{
    if ($high < $low)
        return -1;

    /*low + (high - low)/2;*/
    $mid = floor($low + $high) / 2;

    if ($key == $arr[$mid])
        return $mid;

    if ($key > $arr[$mid])
        return binarySearch($arr, ($mid + 1),
                                $high, $key);

    else
        return binarySearch($arr, $low,
                      ($mid -1), $key);
}

// Function to get pivot.
// For array 3, 4, 5, 6, 1, 2
// it returns 3 (index of 6)
function findPivot($arr, $low, $high)
{

    // base cases
    if ($high < $low)
        return -1;
    if ($high == $low)
        return $low;

    /*low + (high - low)/2;*/
    $mid = ($low + $high)/2;
    if ($mid < $high and $arr[$mid] >
                     $arr[$mid + 1])
        return $mid;

    if ($mid > $low and $arr[$mid] <
                    $arr[$mid - 1])
        return ($mid - 1);

    if ($arr[$low] >= $arr[$mid])
        return findPivot($arr, $low,
                          $mid - 1);

    return findPivot($arr, $mid + 1, $high);
}

// Searches an element key
// in a pivoted sorted array
// arr[] of size n */
function pivotedBinarySearch($arr, $n, $key)
{
    $pivot = findPivot($arr, 0, $n - 1);

    // If we didn't find a pivot,
    // then array is not rotated
    // at all
    if ($pivot == -1)
        return binarySearch($arr, 0,
                       $n - 1, $key);

    // If we found a pivot,
    // then first compare
    // with pivot and then
    // search in two subarrays
    // around pivot
    if ($arr[$pivot] == $key)
        return $pivot;

    if ($arr[0] <= $key)
        return binarySearch($arr, 0,
                   $pivot - 1, $key);

        return binarySearch($arr, $pivot + 1,
                                $n - 1, $key);
}

// Driver Code
// Let us search 3
// in below array
$arr1 = array(5, 6, 7, 8, 9, 10, 1, 2, 3);
$n = count($arr1);
$key = 3;

// Function calling
echo "Index of the element is : ",
      pivotedBinarySearch($arr1, $n, $key);

?>

输出

Index of the element is : 8



时间复杂度:O(log N) 二分查找需要 log n 次比较才能找到元素。 辅助复杂度:O(1)


如何处理重复项? 请听下回分解