Algorithm 在具有重复元素的已排序和旋转数组中查找最小数

Algorithm 在具有重复元素的已排序和旋转数组中查找最小数,algorithm,binary-search,Algorithm,Binary Search,我已经研究这个问题好几天了,但仍然找不到解决它的方法。我的解决方案无法解决某些边缘情况。 问题: 给定一个数组并按升序排序,将数组按k个元素旋转,找到数组的最小数目(原始未旋转数组的第一个元素)的索引。例如: 1.给出{3,4,1,3,3},返回2。 2.给出{3,3,3,3},返回0。 3.给出{1,1,4,1,1,1},返回3。 在没有重复项的情况下,可以使用二进制搜索在O(logn)时间内解决此问题,使用重复项可以使用修改的二进制搜索,最坏情况下的时间复杂度为O(n)。 我的代码: pub

我已经研究这个问题好几天了,但仍然找不到解决它的方法。我的解决方案无法解决某些边缘情况。
问题:
给定一个数组并按升序排序,将数组按k个元素旋转,找到数组的最小数目(原始未旋转数组的第一个元素)的索引。例如:
1.给出{3,4,1,3,3},返回2。
2.给出{3,3,3,3},返回0。
3.给出{1,1,4,1,1,1},返回3。
在没有重复项的情况下,可以使用二进制搜索在O(logn)时间内解决此问题,使用重复项可以使用修改的二进制搜索,最坏情况下的时间复杂度为O(n)。
我的代码:

public int FindPivot(int[] array)
{
    var i = 0;
    var j = array.Length - 1;
    while (i < j)
    {
        var mid = i + (j - i) / 2 + 1;
        if (array[mid] < array[array.Length - 1])
        {
            j = mid - 1;
        }
        else if (array[mid] > array[array.Length - 1])
        {
            i = mid;
        }
        else
        {
            if (array[mid] == array[j])
            {
                j--;
            }
            if (array[mid] == array[i])
            {
                i++;
            }
        }
    }
    return i+1;
}
public int FindPivot(int[]数组)
{
var i=0;
var j=数组。长度-1;
而(iarray[array.Length-1])
{
i=中等;
}
其他的
{
if(数组[mid]==数组[j])
{
j--;
}
if(数组[mid]==数组[i])
{
i++;
}
}
}
返回i+1;
}

如果输入为{3,3,1,3,3,3,3,3},则不起作用,它返回3,而正确答案为2。因为在最后一步,我指向索引2,而j从索引3移动到索引2,它得到了正确的元素,但i+1使结果出错。我在这里缺少什么?

我已经修改了您的代码,如下所示,它似乎适用于所有情况

我想不出任何好的方法来处理所有的情况,因为您的原始代码有点混淆了没有重复元素的算法(分成两个子数组)和有重复元素时的双指针算法的概念

我想说的问题是,移动两个指针的
else
案例并没有涵盖所有案例,就像您有可能进入
else
块,使用
array[I]


因此,我只是用newbie的方法修改了它:添加两个变量来跟踪我们找到的最小元素和最小索引。每当指针移动时更新它,以覆盖所有可能的情况。在末尾返回索引。您不能像return
i+1
那样执行操作,因为它不会处理
k=0
的大小写,这根本不是旋转(
{1,2,3,4}

修改后的代码是用C语言编写的,我从您的示例代码中猜到了


PS:虽然平均而言,这比
O(N)
要快,但如果数据被部分排序而没有重复的元素,最糟糕的情况仍然是
O(N)
。如果我是你,我会做一个简单的迭代,找到第一个最小元素

同样,如果存在重复元素,
O(N)
是您可以达到的最佳效果


使用系统;
公开课考试
{
公共静态int FindPivot(int[]数组)
{
var i=0;
var j=数组。长度-1;

var ans=1我已经修改了您的代码,如下所示,它似乎适用于所有情况

我想不出任何好的方法来处理所有的情况,因为您的原始代码有点混淆了没有重复元素的算法(分成两个子数组)和有重复元素时的双指针算法的概念

我想说的问题是,移动两个指针的
else
案例并没有涵盖所有案例,就像您有可能进入
else
块,使用
array[I]


因此,我只是用新手的方法修改了它:添加两个变量来跟踪我们找到的最小元素和最小索引。每当指针移动时更新它,以覆盖所有可能的情况。在末尾返回索引。你不能像返回
I+1
那样做,因为它不会处理
k=0
的大小写,这不是ro一点也没有(
{1,2,3,4}

修改后的代码是用C语言编写的,我从您的示例代码中猜到了


PS:虽然平均而言,这比
O(N)
要快,但如果数据被部分排序而没有重复的元素,最糟糕的情况仍然是
O(N)
,就像你提到的那样。所以如果我是你,我只需要做一个简单的迭代,然后找到第一个最小元素

同样,如果存在重复元素,
O(N)
是您可以达到的最佳效果


使用系统;
公开课考试
{
公共静态int FindPivot(int[]数组)
{
var i=0;
var j=数组。长度-1;

var ans=1基于@shole的答案,我稍微修改了代码,以涵盖{1,1,1,1,3,1,1,1,1,1}等情况

    public int FindPivot(int[] nums)
    {
        var i = 0;
        var j = nums.Length - 1;
        var ans = int.MaxValue;
        var idx = int.MaxValue;

        while (i < j)
        {
            var mid = i + (j - i) / 2 + 1;

            if (nums[mid] < nums[nums.Length - 1])
            {                   
                if (nums[mid] < ans || (nums[mid] == ans && mid < idx)) { ans = nums[mid]; idx = mid; }
                j = mid - 1;
            }
            else if (nums[mid] > nums[nums.Length - 1])
            {
                i = mid;
            }
            else
            {
                if (nums[j] < ans || (nums[j] == ans && j < idx)) { ans = nums[j]; idx = j; }
                if (nums[mid] == nums[j])
                {
                    j--;
                }
                if (nums[mid] == nums[i])
                {
                    i++;
                }
            }
        }
        // Deal with cases like {1,1,1,1,1}
        if (nums[i] == nums[nums.Length - 1] && nums[i] == nums[0] && i == j)
        {
            return 0;
        }
        if (nums[j] < ans || (nums[j] == ans && j < idx)) { ans = nums[j]; idx = j; }
        return idx;
    }
public int FindPivot(int[]nums)
{
var i=0;
var j=单位长度-1;
var ans=int.MaxValue;
var idx=int.MaxValue;
而(inums[nums.Length-1])
{
i=中等;
}
其他的
{
如果(nums[j]