力扣31.下一个排列、力扣33.搜索旋转排序数组

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1]

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]

  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]

  • arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

提示:

  • 1 <= nums.length <= 100

  • 0 <= nums[i] <= 100


思路:以1,2,3,4,5,6为例,排列一次为123456,123465,123546,...654321.

从后往前遍历,如果是倒序(下降方向),下一个序列就是reverse,如果不是完全下降的序列,找到前一个比后一个小的情况,即最后一个升序,如12385764的下一个序列是12386457,选比5次大的数字6来替换该位置,即交换5和6,剩下的就按升序排列,变成457,如下图所示。

因为要找下一个最小的序列,就考虑动最后一个升序序列的左端换成次大的数组。

代码如下:

void nextPermutation(vector<int>& nums) {
        //从后往前查找第一个相邻升序的元素对[i,j]
        int end = nums.size() - 1;
        int j = end;
        while(j>0){
            if(nums[j-1] < nums[j]){
                break;
            }
            j--;
        }
        if(j<=0){
            //说明是降序的,结果就是逆序的
            reverse(nums.begin(),nums.end());
        }else{
            int i = j - 1; 
            int k = end;
            //从后往前找第一个大于nums[i]的值nums[k],交换nums[i]和nums[k]
            for(;k>=j;k--){
                if(nums[i]<nums[k]){
                    break;
                }
            }
            int t = nums[k];
            nums[k] = nums[i];
            nums[i] = t;
            //逆序[j,end]
            reverse(nums.begin() + j,nums.end());
        }
        
    }

力扣33.搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1

提示:

  • 1 <= nums.length <= 5000

  • -104 <= nums[i] <= 104

  • nums 中的每个值都 独一无二

  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转

  • -104 <= target <= 104


思路:一个有序数组经旋转后,被分为了两个有序数组,基于二分法去查找有序数组中的某个目标元素。需要判断更新target大致在哪个范围。如果中间位置上的值比左值大说明在升序数组中,就去判断target是否在左边数组的范围内,如果在就更新right为mid-1,如果不在就说明在第二个有序数组中。

代码:

int search(vector<int>& nums, int target) {
        int left = 0,right = nums.size() - 1;
        while(left<=right){
            int mid = left + (right - left)/2;
            if(nums[mid]==target){
                return mid;
            }
            //判断是否在左边的升序序列
            if(nums[left]<=nums[mid]){
                if(target>=nums[left] && target<nums[mid]){
                    right = mid - 1;
                }else{
                    left = mid + 1;
                }
            }
            else{
                if(target>nums[mid] && target<=nums[right]){
                    left = mid + 1;
                }
                else{
                    right = mid - 1;
                }
            }
        }
        return -1;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值