Leetcode刷题系列(三)Rotated Array相关

  此部分关于在旋转数组中搜索的题目较多,因此需要扩展上一篇中二分查找算法的基础框架。补充:这种在array中进行搜索的题目都最好进行边界检查。

Find Minimum in Rotated Sorted Array I

 
  将一个有序的数组在中间某个地方进行旋转可以看成如图所示。此题为寻找数组中的最小值,即为图中红色圆圈部分。

  《Leetcode刷题系列(三)Rotated Array相关》

  题目可以复用二分查找的基本框架,关键在与target的选择,我们需设定target = nums[nums.length – 1],这样题目可转化为:寻找第一个比目标数小的位置。

Find Minimum in Rotated Sorted Array II

  此题目与Find Minimum in Rotated Sorted Array I的不同之处仅在于数组中存在重复元素(duplicates),如此也可使用上道题目的解题思路和二分查找法的基础框架,需要注意的代码部分:

while (start + 1 < end) {
        int mid = start + (end - start) / 2;
        if (nums[mid] == nums[end]) {
            end--;
        } else if (nums[mid] < nums[end]) {
            end = mid;            
        } else {
            start = mid;
        }
    }

  上述代码的”<=”可合并到一起,因为按上一篇blog所讲,此代码框架为偏向左边的,即偏向前,不用担心会被”卡住”。

  但此道题目需要注意的并不是可以用二分查找来做,而是当数组中存在重复元素时,算法效率并不是O(lgn),而是O(n),极端的例子为在”1111111…”存在一个”0”。此时使用一个for循环进行顺序查找即可解决,并具有相同的时间复杂度。

Search in Rotated Sorted Array I

  题目为搜索旋转数组中的目标数的位置,若不存在则返回-1。可如图所示,目标数为红色圆圈,题目的难点在于当与目标数进行比较时,大于目标数的部分有两段(蓝色),所以我们并不知道nums[mid]是在target的左边还是右边,这样就无法移动start和end了。

  《Leetcode刷题系列(三)Rotated Array相关》

  我们可以分两种情况来看,如图所示,绿色部分和蓝色部分。此时,我们使用A[start] 与 A[mid]进行比较即可区分开是在哪一部分,然后通过比较A[start]、target、A[end]和A[mid]的位置即可进行移动操作了。

  《Leetcode刷题系列(三)Rotated Array相关》

代码:

while (start + 1 < end) {
        mid = start + (end - start) / 2;
        if (A[mid] == target) {
            return mid;
        }
        if (A[start] < A[mid]) {
            // 绿色部分
            if (A[start] <= target && target <= A[mid]) {
                end = mid;
            } else {
                start = mid;
            }
        } else {
            // 蓝色部分
            if (A[mid] <= target && target <= A[end]) {
                start = mid;
            } else {
                end = mid;
            }
        }
    } 

Search in Rotated Sorted Array II

  这道题与Search in Rotated Sorted Array I的区别也是仅在于数组中存在重复元素,因此与Find Minimum in Rotated Sorted Array II极为类似,时间复杂度为O(n),使用一个顺序查找即可解决,这里不再赘述。

Recover Rotated Sorted Array

  最后一道题为恢复旋转数组,与搜索无关。举个例子:67812345,我们可以先找到8的位置然后将6到8进行逆转,再将8后面的1到5进行逆转,如此可得到:87654321,最后进行一次所有元素的逆转,即可得到12345678,恢复完毕。代码:

private void reverse(ArrayList<Integer> nums, int start, int end) {
    for (int i = start, j = end; i < j; i++, j--) {
        int temp = nums.get(i);
        nums.set(i, nums.get(j));
        nums.set(j, temp);
    }
}

public void recoverRotatedSortedArray(ArrayList<Integer> nums) {
    for (int index = 0; index < nums.size() - 1; index++) {
        if (nums.get(index) > nums.get(index + 1)) {
            reverse(nums, 0, index);
            reverse(nums, index + 1, nums.size() - 1);
            reverse(nums, 0, nums.size() - 1);
            return;
        }
    }
}

Rotate String

  此题目放在这是因为它与Recover Rotated Sorted Array解题思路很相似,根据偏移量将字符串进行旋转。代码为:

public char[] rotateString(char[] A, int offset) {
    if (A == null || A.length == 0) {
        return A;
    }

    offset = offset % A.length;
    reverse(A, 0, A.length - offset - 1);
    reverse(A, A.length - offset, A.length - 1);
    reverse(A, 0, A.length - 1);
    return A;
}

private void reverse(char[] A, int start, int end) {
    for (int i = start, j = end; i < j; i++, j--) {
        char temp = A[i];
        A[i] = A[j];
        A[j] = temp;
    }
}
点赞