zoukankan      html  css  js  c++  java
  • 第11题:旋转数组的最小数字

    题目描述

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

    考点:二分查找法

    如果从头到尾遍历,时间复杂度是O(N),用二分查找法的时间复杂度是O(logn)。

    旋转数组分为两个部分,第一个部分比第二个部分的数字大,而最小的数字是这两个部分的分界线。

    思路:

    1.如果长度为空,则返回0:

    int size= rotateArray.size();

    2.设置三个指针:

    left指向第一个元素,right指向最后一个元素,mid指向中间元素。

    当left和right间距为1时,最小元素是right,将mid指向right,最终返回mid.

    left=0;right = size- 1;mid =0;

    由于可能该旋转数组已经是顺序数字,第一个元素可能是最小的,所以mid初始值设置为0,此时返回第一个元素。

    3.循环查找:

    按照规则,left一定大于等于right,循环条件:array[left]>=array[right]

    如果左右指针相差为1,right-left==1,则mid=right,break循环。

    否则,mid为新范围中间的指针,防止栈溢出,mid= left+ (right-left)/2,

    如果有特殊情况,[1 0 1 1 1] [1 1 1 0 1],这里mid left right 的值一样,无法找到最小值,则需要顺序遍历。

    如果mid指的数在数组第一个部分,arr[mid]应该大于等于arr[left],这时最小值在后部分,所以将left指向mid. left=mid.

    否则,mid指的数在数组的第二部分,最小值在mid到left之间,所以范围缩小,right=mid.

    移动后的left永远在第一部分,right永远在第二部分,最终left指向第一部分的最后一个元素,right指向第二部分的第一个元素。这就是循环结束条件,right-left ==1,break。最小的元素就是right指向的元素。

    4.顺序遍历:

    int ordermid(vector<int> &arr,int left ,int right)

    {

    int  result = arr[left];

    for(int i = left+1;i <right;i++)

    {

    if(arr[i]<result)

    result=arr[i];

    }

    return result;//返回result 是最小的值。 

    }

    C++ 二分查找:

    class Solution {
    public:
        int minNumberInRotateArray(vector<int> rotateArray) {
            int size = rotateArray.size();
            if(size == 0) return 0;//如果长度为0,返回空
            
            int left =0;
            int mid = left;//如果已经是排好序的,返回最左
            int right = size-1;
            
            while(rotateArray[left]>=rotateArray[right])//包含了等于的情况
            {
                 if(right-left==1)
                {
                    mid = right;
                    break;
                }
                
                mid=left+(right-left)/2;//防止栈溢出
                
                if(rotateArray[left]==rotateArray[right]&&rotateArray[left]==rotateArray[mid])//特殊情况需要顺序遍历
                return ordermid(rotateArray,left,right);
                
                if(rotateArray[left]<=rotateArray[mid])//左 小于或等于 中 就移动左指针,说明最小值在后面的数组中,缩小范围
                    left = mid;
                else //左 大于 中,说明最小值在第一个数组中,移动右指针
                    right = mid;
            }
            return rotateArray[mid];
        }
        
    private:
        int ordermid(vector<int> &array,int left,int right)//特殊情况顺序遍历
        {
            int result = array[left];
            for(int i = left + 1;i<right;i++)
            {
                if(result>array[i])
                    result=array[i];
            }
            return result;
        }
    };
    int main()
    {
    	Solution s;
    	 //vector<int> num = {0,1,2,3,4,5};
    	//vector<int> num = {4,5,6,7,1,2,3};
    	//vector<int> num = { 2,2,2,2,1,2 };
    	 vector<int> num = {1 };
    	int result = s.minNumberInRotateArray(num);
    	// 输出
    	cout << result << endl; 
    	system("pause");
    
    	return 0;
    }

    使用STL: 

    class Solution {
    public:
        int minNumberInRotateArray(vector<int> rotateArray) {
           sort(rotateArray.begin(),rotateArray.end());
             
            return rotateArray[0];
             
        }
    };
    // 面试题11:旋转数组的最小数字
    // 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
    // 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组
    // {3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
    
    #include <cstdio>
    #include <exception>
    
    int MinInOrder(int* numbers, int index1, int index2);
    
    int Min(int* numbers, int length)
    {
        if(numbers == nullptr || length <= 0)
            throw new std::exception("Invalid parameters");
     
        int index1 = 0;
        int index2 = length - 1;
        int indexMid = index1;
        while(numbers[index1] >= numbers[index2])
        {
            // 如果index1和index2指向相邻的两个数,
            // 则index1指向第一个递增子数组的最后一个数字,
            // index2指向第二个子数组的第一个数字,也就是数组中的最小数字
            if(index2 - index1 == 1)
            {
                indexMid = index2;
                break;
            }
     
            // 如果下标为index1、index2和indexMid指向的三个数字相等,
            // 则只能顺序查找
            indexMid = (index1 + index2) / 2;
            if(numbers[index1] == numbers[index2] && numbers[indexMid] == numbers[index1])
                return MinInOrder(numbers, index1, index2);
    
            // 缩小查找范围
            if(numbers[indexMid] >= numbers[index1])
                index1 = indexMid;
            else if(numbers[indexMid] <= numbers[index2])
                index2 = indexMid;
        }
     
        return numbers[indexMid];
    }
    
    int MinInOrder(int* numbers, int index1, int index2)
    {
        int result = numbers[index1];
        for(int i = index1 + 1; i <= index2; ++i)
        {
            if(result > numbers[i])
                result = numbers[i];
        }
    
        return result;
    }
    
    // ====================测试代码====================
    void Test(int* numbers, int length, int expected)
    {
        int result = 0;
        try
        {
            result = Min(numbers, length);
    
            for(int i = 0; i < length; ++i)
                printf("%d ", numbers[i]);
    
            if(result == expected)
                printf("	passed
    ");
            else
                printf("	failed
    ");
        }
        catch (...)
        {
            if(numbers == nullptr)
                printf("Test passed.
    ");
            else
                printf("Test failed.
    ");
        }
    }
    
    int main(int argc, char* argv[])
    {
        // 典型输入,单调升序的数组的一个旋转
        int array1[] = { 3, 4, 5, 1, 2 };
        Test(array1, sizeof(array1) / sizeof(int), 1);
    
        // 有重复数字,并且重复的数字刚好的最小的数字
        int array2[] = { 3, 4, 5, 1, 1, 2 };
        Test(array2, sizeof(array2) / sizeof(int), 1);
    
        // 有重复数字,但重复的数字不是第一个数字和最后一个数字
        int array3[] = { 3, 4, 5, 1, 2, 2 };
        Test(array3, sizeof(array3) / sizeof(int), 1);
    
        // 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
        int array4[] = { 1, 0, 1, 1, 1 };
        Test(array4, sizeof(array4) / sizeof(int), 0);
    
        // 单调升序数组,旋转0个元素,也就是单调升序数组本身
        int array5[] = { 1, 2, 3, 4, 5 };
        Test(array5, sizeof(array5) / sizeof(int), 1);
    
        // 数组中只有一个数字
        int array6[] = { 2 };
        Test(array6, sizeof(array6) / sizeof(int), 2);
    
        // 输入nullptr
        Test(nullptr, 0, 0);
    
        return 0;
    }
    
  • 相关阅读:
    hdu 5646 DZY Loves Partition
    bzoj 1001 狼抓兔子 平面图最小割
    poj 1815 Friendship 最小割 拆点 输出字典序
    spoj 1693 Coconuts 最小割 二者取其一式
    hdu 5643 King's Game 约瑟夫环变形
    约瑟夫环问题
    hdu 5642 King's Order
    CodeForces 631C Report
    1039: C语言程序设计教程(第三版)课后习题9.4
    1043: C语言程序设计教程(第三版)课后习题10.1
  • 原文地址:https://www.cnblogs.com/lightmare/p/10428846.html
Copyright © 2011-2022 走看看