【问题标题】:Find smallest number in Sorted Rotatable Array在已排序的可旋转数组中查找最小的数字
【发布时间】:2011-12-16 10:27:08
【问题描述】:

我在一次采访中遇到了这个问题。请帮助我获得解决方案。

问题是:

您已经对可旋转数组进行了排序,即。 e.数组包含已排序的元素并且可以循环旋转,例如如果数组中的元素是 [5,6,10,19,20,29],那么第一次旋转数组变为 [29,5,6,10,19 ,20],第二次变成 [20,29,5,6,10,19] 等等。

所以你需要找到数组中任意一点的最小元素。不会向您提供数组旋转的次数。只需给出旋转的数组元素并找出其中最小的元素。在这种情况下,输出应该是 5。

【问题讨论】:

  • 如果没有其他要求,直接线性搜索即可;)
  • 但这不会在时间复杂度前沿上中奖

标签: algorithm


【解决方案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])

方法二:

当您将已排序、旋转的数组分成两半 (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]

【讨论】:

  • 更准确地说,如果有旋转点,答案是 arr[i+1],否则是 a[1]。
  • 你会在二分搜索中做哪种控制?
  • 如果数组根本不旋转,我认为你不能避免查看所有元素,特别是如果所有元素都相等。
  • @CoffeeonMars:类似于我在方法 2 中的内容。
  • D'oh,排序不是问题,只有相等的元素。
【解决方案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;
    }

}

【讨论】:

    【解决方案3】:

    在已排序的旋转数组中查找最小的数: 使用二分搜索概念

    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))
    

    【讨论】:

      【解决方案4】:

      我的代码如下,算法为 cmets。甚至适用于重复的元素。

      //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];
      }
      

      【讨论】:

        【解决方案5】:

        这可以在 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] 这样的数组,仅检查第一个、最后一个和中间元素是不可能的(因为它们都相等) 最小元素位于数组的哪一半。

        我用 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;
        }
        

        【讨论】:

          【解决方案6】:

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

          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
          }
          

          【讨论】:

            【解决方案7】:

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

            处理已排序/未排序的升序//降序用例..

            缺点是它仍然会执行 log n 步以在没有旋转的完美排序集中找到最小值。

            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);
                }
            }
            

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

            【讨论】:

              【解决方案8】:
                  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);
                          }
                      }
                  }
              

              【讨论】:

                【解决方案9】:

                以下算法需要 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];
                }
                

                【讨论】:

                  【解决方案10】:

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

                  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);
                      }
                  }
                  

                  【讨论】:

                    【解决方案11】:

                    问题:在已排序的旋转数组中找到最小值。 解决方案 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];
                            }
                    
                        }
                    }
                    

                    【讨论】:

                      【解决方案12】:
                      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)
                      

                      first if 语句检查偶数数组是否被旋转。在这种情况下,第一个元素是 min 。如果列表的长度为 1,则该元素为最小值。 否则如果中间元素小于最后一个元素,则继续在后半部分查找,否则在前半部分查找元素

                      【讨论】:

                        【解决方案13】:
                        //java program to find minimum element in a sorted array rotated 
                        //about any pivot in O(log n) in worst case and O(1) in best case
                        
                        class ArrayRotateMinimum {
                            public static void main(String str[]) {
                        
                            // initialize with your sorted rotated array here
                        
                            int array[] = { 9, 1, 2, 3, 4, 5, 6, 7, 8, };
                            System.out.println("Minimum element is: " + minimumElement(array));
                            }
                        
                        static int minimumElement(int array[]) {
                        
                            // variables to keep track of low and high indices
                        
                            int low, mid, high;
                        
                            // initializing variables with appropriate values
                        
                            low = 0;
                            high = array.length - 1;
                            while (low < high) {
                        
                                // mid is always defined to be the average of low and high
                        
                                mid = (low + high) / 2;
                                if (array[low] > array[mid]) {
                        
                                    // for eg if array is of the form [9,1,2,4,5],
                                    // then shift high to mid to reduce array size by half
                                    // while keeping minimum element between low and high
                        
                                    high = mid;
                                } else if (array[mid] > array[high]) {
                        
                                    // this condition deals with the end case when the final two
                                    // elements in the array are of the form [9,1] during the
                                    // last iteration of the while loop
                        
                                    if (low == mid) {
                                        return array[high];
                                    }
                        
                                    // for eg if array is of the form [4,5,9,1,2],
                                    // then shift low to mid to reduce array size by half
                                    // while keeping minimum element between low and high
                        
                                    low = mid;
                                } else {
                        
                                    // the array has not been rotated at all
                                    // hence the first element happens to be the smallest element
                        
                                    return array[low];
                                }
                            }
                        
                            //return first element in case array size is just 1
                            return array[0];
                        
                            }
                        }
                        

                        【讨论】:

                          【解决方案14】:

                          这是我使用递归的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])
                          

                          【讨论】:

                            【解决方案15】:

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

                            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");
                            
                                }
                            

                            【讨论】:

                              【解决方案16】:

                              使用递归二分搜索方法解决有重复和不重复的数组。

                              唯一数组

                              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);
                              };
                              

                              重复数组

                              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);
                              };
                              

                              【讨论】:

                                猜你喜欢
                                • 2020-11-28
                                • 1970-01-01
                                • 2019-03-31
                                • 2021-01-29
                                • 1970-01-01
                                • 1970-01-01
                                • 2021-04-14
                                • 2018-05-14
                                • 2012-09-08
                                相关资源
                                最近更新 更多