Lintcode 39. 恢复旋转排序数组

本文介绍了一种在原地恢复旋转排序数组排序的方法,包括直接循环、分段翻转等策略,通过O(1)的额外空间和O(n)的时间复杂度实现数组排序。

题目描述:

给定一个旋转排序数组,在原地恢复其排序。

什么是旋转数组?

    • 比如,原始数组为[1,2,3,4], 则其旋转数组可以是[1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3]

挑战:使用O(1)的额外空间和O(n)时间复杂度

思路:

一、直接多次循环

二、假设1前面有k个数,找到1的位置后, 将其前面的k个数增加到数组末尾,然后数据统一前移一次,再去除后面的k个位置

三、(也是代码中采用的方法)

三步翻转法:以{4,5,6,7,1,2,3}为例

1.  先找到1的位置.然后翻转{4,5,6,7}得到{7,6,5,4}

2. 翻转{1,2,3}得到{3,2,1}

3. 此时数组为:{7,6,5,4,3,2,1}, 将其翻转即得{1,2,3,4,5,6,7}


代码:

class Solution {
public:
    void recoverRotatedSortedArray(vector<int> &nums) {
        // write your code here
        int pos = 1, len = nums.size();
        for(;(pos < len) && (nums[pos] >= nums[pos-1]); ++pos);//注意'='号
        if(pos == len)// 若已经是原始排序,则返回
            return;
        rotateArray(nums, 0, pos);
        rotateArray(nums, pos, len);
        rotateArray(nums, 0, len);
    }
    void rotateArray(vector<int> &nums, int begin, int end){
        for(int i = begin, j = end - 1; i < j; ++i, --j)
            swap(nums[i], nums[j]);
    }
};


你提出的问题是 **LintCode 159:寻找旋转排序数组中的最小值**,要求用 C 语言编写,但又给出了 C++ 的函数签名: ```cpp int findMin(vector<int> &nums) { } ``` 这在 **纯 C 语言中无法实现**,因为 C 没有 `vector<int>` 和引用 `&`。 --- ## ✅ 正确理解你的需求 你可能是想: - 理解这个问题的算法逻辑; - 用 **C 语言实现核心功能**; - 同时知道如何在 C++ 中提交到 LintCode。 我们分两部分解答: --- ## ✅ 第一部分:C 语言实现(推荐用于学习和本地运行) ### 📌 函数接口(C 风格) ```c int findMin(int* nums, int n) ``` - `nums`: 指向数组首元素的指针 - `n`: 数组长度 ### ✅ 完整 C 代码(二分查找法) ```c #include <stdio.h> /** * 在旋转排序数组中找最小值(无重复元素) * 使用二分查找,时间复杂度 O(log n) */ int findMin(int* nums, int n) { if (n == 0) return 0; if (n == 1) return nums[0]; int left = 0; int right = n - 1; // 如果没有旋转(第一个元素 <= 最后一个),直接返回首元素 if (nums[left] <= nums[right]) { return nums[left]; } while (left <= right) { int mid = left + (right - left) / 2; // 情况1:mid 是最小值 // mid > 0 && nums[mid] < nums[mid-1] 表示下降点 if (mid > 0 && nums[mid] < nums[mid - 1]) { return nums[mid]; } // 情况2:mid 是最大值,则最小值在右边 if (mid < n - 1 && nums[mid] > nums[mid + 1]) { return nums[mid + 1]; } // 决定搜索方向 if (nums[mid] >= nums[0]) { // 中间值大于等于首元素 → mid 在左半段(递增部分),最小值在右半段 left = mid + 1; } else { // 中间值小于首元素 → mid 在右半段(包含最小值的一侧) right = mid - 1; } } return nums[left]; // 实际上不会执行到这里 } // ==================== 测试主函数 ==================== int main() { // 测试用例1: {4,5,6,7,0,1,2} -> min=0 int nums1[] = {4, 5, 6, 7, 0, 1, 2}; int n1 = sizeof(nums1) / sizeof(nums1[0]); printf("Test 1: %d\n", findMin(nums1, n1)); // 输出 0 // 测试用例2: {3,5,8,1,2} -> min=1 int nums2[] = {3, 5, 8, 1, 2}; int n2 = sizeof(nums2) / sizeof(nums2[0]); printf("Test 2: %d\n", findMin(nums2, n2)); // 输出 1 // 测试用例3: {1,2,3,4,5} -> 未旋转 -> min=1 int nums3[] = {1, 2, 3, 4, 5}; int n3 = sizeof(nums3) / sizeof(nums3[0]); printf("Test 3: %d\n", findMin(nums3, n3)); // 输出 1 // 测试用例4: 单个元素 int nums4[] = {1}; int n4 = 1; printf("Test 4: %d\n", findMin(nums4, n4)); // 输出 1 return 0; } ``` --- ### ✅ 编译运行(保存为 `.c` 文件) ```bash gcc -std=c99 find_min.c -o find_min ./find_min ``` 输出: ``` Test 1: 0 Test 2: 1 Test 3: 1 Test 4: 1 ``` --- ## ✅ 第二部分:C++ 版本(用于 LintCode 提交) LintCode 要求的是如下接口: ```cpp int findMin(vector<int> &nums) ``` 这是标准的 C++ 接口。 ### ✅ 可提交的 C++ 代码 ```cpp class Solution { public: /** * @param nums: a rotated sorted array (no duplicates) * @return: the minimum number in the array */ int findMin(vector<int> &nums) { int n = nums.size(); if (n == 0) return 0; if (n == 1) return nums[0]; int left = 0; int right = n - 1; // 如果没有旋转 if (nums[left] <= nums[right]) { return nums[left]; } while (left <= right) { int mid = left + (right - left) / 2; // 找到下降点:mid 是最小值前一个位置 if (mid > 0 && nums[mid] < nums[mid - 1]) { return nums[mid]; } if (mid < n - 1 && nums[mid] > nums[mid + 1]) { return nums[mid + 1]; } if (nums[mid] >= nums[0]) { left = mid + 1; // 最小值在右半部分 } else { right = mid - 1; // 最小值在左半部分 } } return nums[left]; } }; ``` 你可以将此代码直接粘贴到 LintCode 或 LeetCode 的编辑器中提交。 --- ## ✅ 算法原理说明 | 条件 | 含义 | |------|------| | `nums[0] <= nums[n-1]` | 数组旋转,最小值是 `nums[0]` | | `nums[mid] >= nums[0]` | `mid` 在左半递增段,最小值在右边 | | `nums[mid] < nums[0]` | `mid` 在右半段(含最小值),往左找 | 通过比较 `nums[mid]` 与 `nums[0]` 来判断 `mid` 落在哪一段。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值