在Sorted Rotatable Array中查找最小数字

时间:2011-12-16 10:27:09

标签: algorithm

我在一次采访中遇到了这个问题。请帮我解决问题。

问题是:

  

你已经对可旋转阵列进行了分类,i。即数组包含已排序的元素,它可以循环旋转,就像数组中的元素是[5,6,10,19,20,29]然后旋转第一次数组成为[29,5,6,10,19] ,20],第二次变为[20,29,5,6,10,19]等等。

     

所以你需要在任何一点找到数组中最小的元素。您将不会被提供数字时间数组旋转。只是给出旋转的数组元素并找出其中最小的元素。在这种情况下,输出应为5。

16 个答案:

答案 0 :(得分:35)

方法1:

您可以在O(logN)时间内完成此操作。

使用修改后的二进制搜索来查找作为索引i的旋转点,使其成为arr[i] > arr[i+1]

示例:

[6,7,8,9,1,2,3,4,5]
       ^
       i

对两个子阵列(arr[1], arr[2], .., arr[i])
(arr[i+1], arr[i+2], ..., arr[n])进行了排序。

答案是min(arr[1], arr[i+1])

方法2:

当您将已排序的旋转数组拆分为两半(arr[1],..,arr[mid])(arr[mid+1],..,arr[n])时,其中一个始终排序,另一个始终具有最小值。我们可以直接使用修改后的二进制搜索来继续搜索未分类的一半

// index of first element
l = 0

// index of last element.
h = arr.length - 1

// always restrict the search to the unsorted 
// sub-array. The min is always there.
while (arr[l] > arr[h]) {
        // find mid.
        mid = (l + h)/2
        // decide which sub-array to continue with.
        if (arr[mid] > arr[h]) {
                l = mid + 1
        } else {
                h = mid
        }
}
// answer
return arr[l]

答案 1 :(得分:2)

如果重复数据元素,如{8,8,8,8,8}或{1,8,8,8,8}或{8,1,8,8,8}或{,则上述算法失败8,8,1,8,8}或{8,8,8,8,1}

// solution pasted below will work all test cases :)

//break the array in two subarray and search for pattern like a[mid]>a[mid+1]
// and return the min position

public static int smallestSearch(int[] array,int start,int end)
{   
        if(start==end)
        return array.length;

        int mid=(start+end)/2;

        if(array[mid]>array[mid+1])
        return min(mid+1,smallestSearch(array,start,mid),smallestSearch(array,mid+1,end));
        else
        return min(smallestSearch(array,start,mid),smallestSearch(array,mid+1,end));
}

public static int min(int a,int b)
{
    if(a==b)
    return a;
    else if(a<b)
        return a;
        else 
        return b; 
}
public static int min(int a,int b,int c)
{
    if(a<c)
    {
        if(a<b)
        {
            return a;
        }
        else
        {
            return b;
        }
    }
    else
    {
        if(b<c)
        return b;
        else
        return c;
    }

}

答案 2 :(得分:1)

要查找已排序的旋转数组中的最小数字: 使用二进制搜索概念

public class RotatedSortedArrayWithoutDuplicates1 {

    public static void main(String[] args) {
        int[] a = { 4, 6, 8, 10, 34, 56, 78, 1, 3 };

        System.out.println(findMin(a));

    }

    private static int findMin(int[] a) {
        if (a.length == 0 || a == null) {
            return -1;
        }
        int start = 0;
        int last = a.length - 1;
        while (start + 1 < last) {
            int mid = start + (last - start) / 2;
            int m = a[mid];
            int s = a[start];
            int l = a[last];

            if (m > l) {
                start = mid + 1;
            }
            if (m < l) {
                last = mid;
            } else {
                last--;
            }

        } // while

        if (a[start] > a[last]) {
            return a[last];
        } else {
            return a[start];
        }

    }
}

但如果你不想使用二进制搜索,那么:

public class Abc {
    public static void main(String[] args) {
        int[] a = { 4, 5, 6, 7, 7, 8, 9, 1, 1, 2, 3, 3 };
        System.out.println(findMin(a));
    }

    public static int findMin(int[] a) {

        int min = a[a.length - 1];

        for (int i = 0; i < a.length; i++) {
            if (min > a[i]) {
                min = a[i];
                break;
            }
        }
        return min;

    }// findmin
}// end

以下是Python中的代码:

def fix(a):
    min = a[0]
    for i in range(len(a)):
        if(min > a[i]):
            min = a[i]
            break

    return min        


a = [2, 2,3,4,1,2]
print(fix(a))

答案 3 :(得分:0)

我的代码在下面,算法作为注释。甚至可以用于重复的元素。

//Find Min in Rotated Sorted Array
//Example: int array[10] = {7, 8, 9, 10, 11, 12, 3, 4, 5, 6};
// Min in the above array is 3
// Solution: Recursively search (Modified binary search) for the Pivot where is the smallest Element is present
// Algorithm: 
// 1) Find the Mid of the Array 
// 2) call the recursive function on segment of array in which there is a deviation in the order
// If (A[low] > A[mid]) array segment in which deviation in the order present is (low, mid)
// If (A[low] < A[mid]) array segment in which deviation in the order present is (mid + 1, high) 
// Time Complexity: O(logn)
// Space Complexity: is of the recursive function stack that is being used

#define MIN(x,y) (x) <= (y) ? (x): (y)

int MininRotatedSortedArray(int A[], int low, int high)
{
    if(low > high)
        return -1;

    if(low == high - 1)
        return MIN(A[low], A[high]);

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

    if(A[low] > A[mid])
        return MininRotatedSortedArray(A, low, mid);
    else if(A[low] < A[mid])
        return MininRotatedSortedArray(A, mid + 1, high);
    else
        return A[mid];
}

答案 4 :(得分:0)

这可以在O(1)时间最佳情况,O(n)时间最差情况和平均O(lg n)时间内完成。

对于旋转的排序数组,如果数组中的第一个元素小于数组中的最后一个元素,则排序的数组不会旋转(或旋转0位置)。最小元素只是第一个元素。

如果中间元素小于最后一个元素,则最小元素位于[first,middle]。

如果中间元素大于最后一个元素,那么最小元素在[middle + 1,last]中。

如果中间元素等于最后一个元素,则有两个子案例:

  1. 第一个元素大于最后一个元素,在这种情况下,最小元素在[first,middle + 1]中;
  2. 第一个元素等于最后一个元素,在这种情况下,不确定拒绝任何一半的数组。减少线性搜索。例如,对于诸如[5,5,5,1,5]和[5,1,5,5,5]之类的数组,仅通过检查第一个,最后一个和中间元素是不可能的(因为它们都是相等的) )数组的哪一半是最小元素。
  3. 我用C ++编写了以下代码来解决这个问题,它应该处理所有情况(空数组,重复元素)。

    template <typename Iterator>
    Iterator rotated_min(Iterator begin, Iterator end)
    {
        while (begin != end)
        {
            if (*begin < *(end - 1))
            {
                return begin;
            }
            Iterator mid = begin + (end - 1 - begin) / 2;
            if (*mid < *(end - 1))
            {
                end = mid + 1;
            }
            else if (*mid > *(end - 1))
            {
                begin = mid + 1;
            }
            else 
            {
                if (*begin > *(end - 1))
                {
                    end = mid + 1;
                    ++begin;
                }
                else 
                {
                    //reduce to linear search
                    typename ::std::iterator_traits<Iterator>::value_type min_value = *begin;
                    Iterator min_element = begin;
                    for (Iterator i = begin + 1; i != end; ++i)
                    {
                        if (*i < min_value)
                        {
                            min_value = *i;
                            min_element = i;
                        }
                    }
                    return min_element;
                }
            }
        }
        return begin;
    }
    

答案 5 :(得分:0)

在循环排序数组中查找最小索引

Example : [7,8,9,1,2,3,4,5,6]

int findMinIndex(int []a, int start, int end)
{
    int mid = (start + end)/2;

    if (start - end == 1)
        if(a[start] < a[end])
        return start;
        else
        return end;

    if (a[mid] > a[end]){

    return findMinIndex(a,mid,end);

    }

    else{

      return  findMinIndex(a,start,mid);
    }

    return -1; //Not found
}

答案 6 :(得分:0)

如果有人需要它..我在java中的实现..

负责排序/未排序的升序//降序用例..

缺点是它仍然会执行log n步骤来在没有旋转的完美排序集中查找min值。

http://ideone.com/fork/G3zMIb

/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        int [] a = {3,3,0,2,2,2,2,1,2,2,2,2,2,2,2,2,2};
        System.out.println(recursiveSplit(a,0,a.length-1));
    }

    public static int findMin(int x, int y){
        if(x<=y){
            return x;
        }else{
            return y;
        }
    }

    public static int recursiveSplit(int[] arr , int a , int b){
        int mid = (int) Math.floor(a + (b-a)/2);
        int h1_l = a;
        int h1_u = mid;
        int h2_l = mid+1;
        int h2_u = b;
        int x=0;
        int y=0;

        //single element
        if(a==b){
            return arr[a];
        }

        //adjacent positions
        if(h1_u-h1_l==1){
            x=findMin(arr[h1_u],arr[h1_l]);
        }else{
            //else split
            x=recursiveSplit(arr,h1_l,h1_u);
        }

        if(h2_u-h2_l==1){
            y=findMin(arr[h2_u],arr[h2_l]);
        }else{
            y=recursiveSplit(arr, h2_l,h2_u);
        }

        return findMin(x, y);
    }
}

欢迎使用错误/建议/失败的用例

答案 7 :(得分:0)

    public int findMin(int[] num) {
        return findMin(num, 0, num.length-1);
    }
    public int findMin(int[] num, int left, int right){
        if(left==right) return num[left];
        if(left+1==right) return Math.min(num[left], num[right]);
        int mid = (left+right)/2;
        if(num[mid]>num[right]){
            return findMin(num,mid+1,right);
        }else if(num[mid]<num[right]){
            return findMin(num,left,mid);
        }else{
            if(num[mid]==num[left]){
                return Math.min(findMin(num,left,mid), findMin(num,mid,right));
            }else{
                return findMin(num,left,mid);
            }
        }
    }

答案 8 :(得分:0)

以下算法需要log(n)时间。假设数组没有重复。

public int findMin(int[] num) {
    if(num.length == 0) return -1
    int r = num.length-1, l = 0;
    while(l<r){
        if(num[l]<=num[r]) return num[l]; //when not rotated, return the left most value
        int mid = (r+l)/2;
        if(num[mid]<num[r]){
            r = mid;
        }else{
            l = mid+1;
        }
    }
    return num[l];
}

答案 9 :(得分:0)

我是使用略微修改的二进制搜索版本完成的。我在这里做的是根据最小值的不同向左或向右移动。例如,如果中间元素小于最左边的元素,则在升序数组中,最小值可能在左侧。在通过数组递归时,我也会跟踪最小值。递归一直持续到结束,然后返回最新的min。这也适用于重复的元素。

public static void main(String[] args) throws IOException {

    int[] rotated = {6, 7, 8, 1, 2, 3, 4, 5};
    int min = findMin(rotated);
    System.out.println(min);//1


}

public static int findMin(int[] sorted) {
    return findMinRecursively(sorted, sorted[0], 0, (sorted.length - 1));
}


private static int findMinRecursively(int[] sorted, int min, int leftIndex, int rightIndex) {

    if (leftIndex > rightIndex) {
        return min;
    }

    int midIndex = (leftIndex + rightIndex) / 2;
    if (sorted[midIndex] < min) {
        min = sorted[midIndex];
    }

    if (sorted[midIndex] < sorted[leftIndex]) {
        return findMinRecursively(sorted, min, leftIndex, (midIndex - 1));
    } else {
        return findMinRecursively(sorted, min, (midIndex + 1), rightIndex);
    }
}

答案 10 :(得分:0)

问题:在已排序的旋转数组中找到最小值。 解决方案1:使用二进制搜索

class Test18 {
    public static void main(String[] args) {
        int[] a = { 5, 6, 7, 8, 9, 10, 1, 2, 3 };

        System.out.println(findmin(a));

    }

    // find min in a sorted rotated array
    private static int findmin(int[] a) {
        int start = 0;
        int last = a.length - 1;
        while (start + 1 < last) {
            int mid = start + (last - start) / 2;
            if (a[mid] > a[last]) {
                start = mid + 1;
            }
            if (a[mid] < a[last]) {
                last = mid;
            } else {
                mid--;
            }

        } // while
        if (a[start] > a[last]) {
            return a[last];
        } else {
            return a[start];
        }

    }
}

答案 11 :(得分:0)

def searchinrotatedarray(arr1,l,h):
    if l>h:
        return arr1[0]
    if h==l:
        return arr1[l]
    mid=(l+h)//2
    if mid<h and arr1[mid+1]<arr1[mid]:
        return arr1[mid+1]
    elif mid>l and arr1[mid-1]<arr1[mid]:
        return arr1[mid]
    elif arr1[mid]<arr1[h]:
        return searchinrotatedarray(arr1,l,mid-1)
    else:
        return searchinrotatedarray(arr1,mid+1,h)

首先,if语句检查偶数数组是否完全旋转。在这种情况下,第一个元素是min。如果list的长度为1,则该元素为min。 否则,如果中元素小于最后一个元素,则继续在下半部分查找,否则在上半部分查找

答案 12 :(得分:0)

df<-read.table(header=TRUE, text="uniqueID favFruits favVeggie State favColor
john@mail.com NA carrots CA Green
jill@mail.com apples NA FL NA
john@mail.com grapes beets CA Red
jill@mail.com cherries beans FL Blue
jill@mail.com pineapple beans FL Blue 
john@mail.com grapes beets CA Yellow")


library(dplyr)
 answer<- df %>% group_by(uniqueID) %>% summarize_all(list(~toString(unique(.))) ) 

print(answer)
# A tibble: 2 x 5
  uniqueID      favFruits                   favVeggie      State favColor          
  <fct>         <chr>                       <chr>          <chr> <chr>             
1 jill@mail.com apples, cherries, pineapple NA, beans      FL    NA, Blue          
2 john@mail.com NA, grapes                  carrots, beets CA    Green, Red, Yellow

答案 13 :(得分:0)

这是我使用递归的pythonic解决方案:

时间复杂度为O(log(n))&空间复杂度为O(1)

    class Solution(object):
       def findMin(self, nums):
          left = 0
          right = len(nums) -1
          mid = len(nums) // 2

          if len(nums) == 0:
             return -1
         if len(nums) == 1:
            return nums[left]
         if len(nums) == 2:
            return min(nums[left], nums[right])

         if nums[left] < nums[right]:
            return nums[left]
         elif nums[mid] > nums[left]:
            return self.findMin(nums[mid + 1: ])
         elif nums[mid] < nums[left]:
            return self.findMin(nums[: mid + 1])

答案 14 :(得分:0)

这是一个非常简单的答案,它将适用于所有测试用例:

int a[] = {5,6,7,1,2,3,4};
int a[] = {1,2,3};
int a[] = {3,2,1};
int a[] = {3,1,2};
int a[] = {2,2,2,2};

public class FindSmallestNumberInSortedRotatedArray {

    public static void main(String[] args) {
        int a[] = { 4, 5, 6, 7, 1, 2, 3 };
        int j = a.length - 1;
        int i = 0;
        while (i < j) {
            int m = (i + j) / 2;
            if (a[m] < a[m + 1] && a[m] < a[m - 1]) {
                System.out.println(a[m] + "is smallest element ");
                break;
            } else if (a[m] > a[m + 1] && a[m - 1] > a[m + 1]) {
                i = m + 1;
            } else {
                j = m - 1;
            }

        }
        if (i == j)
            System.out.println(a[i] + " is smallest element");

    }

答案 15 :(得分:0)

使用递归二进制搜索方法,对具有重复项和不具有重复项的两个数组进行求解。

唯一数组

enter image description here

var min = (A, l, h) => {
    if(l >= h) return A[l];
    
    let  m = Math.floor((l + h) / 2);
    // as its unique
    if(A[m] > A[h]) {
        // go towards right as last item is small than mid
        return min(A, m + 1, h);
    } else if(A[m] > A[m - 1]) {
        // go towards left as prev item is smaller than current
        return min(A, l, m - 1);
    } else {
        // right and left is greater than current
        return A[m];
    }
}
/**
 * @param {number[]} nums
 * @return {number}
 */
var findMin = function(nums) {
    // If array is rotated nums.length time or same as it is
    if(nums[0] < nums[nums.length - 1]) return nums[0];
    
    return min(nums, 0, nums.length - 1);
};

具有重复项的数组

enter image description here

var min = (A, l, h) => {
    if(l >= h) return A[l];
    
    // traverse from left "and right" to avoid duplicates in the end
    while(A[l] == A[l+1]) {
        l++;
    }
    
    let  m = Math.floor((l + h) / 2);
    // as its unique
    if(A[m] > A[h]) {
        // go towards right as last item is small than mid
        return min(A, m + 1, h);
    } else if(A[m] >= A[m - 1]) {
        // go towards left as prev item is smaller than current
        return min(A, l, m - 1);
    } else {
        // right and left is greater than current
        return A[m];
    }
}
/**
 * @param {number[]} nums
 * @return {number}
 */
var findMin = function(nums) {
    // If array is rotated nums.length time or same as it is
    if(nums[0] < nums[nums.length - 1]) return nums[0];
    
    return min(nums, 0, nums.length - 1);
};