LeetCode144--下一个排列、搜索旋转排序数组

本文探讨了两种数组操作的算法实现。第一部分介绍了如何找到一个数组的下一个排列,通过找到数组中相邻的逆序对并反转后续部分以生成更小的排列。第二部分讲解了在搜索旋转排序数组中查找目标值的方法,利用二分查找策略优化搜索过程,确保在有序子序列中定位目标。这两个问题都涉及到数组的高效操作和排序性质的利用。
摘要由CSDN通过智能技术生成

1、下一个排列

题目说的并不是很清楚,这里用白话来解释一下,

找出这个数组排序出的所有数中,刚好比当前数大的那个数

比如当前 nums = [1,2,3]。这个数是123,找出1,2,3这3个数字排序可能的所有数,排序后,比123大的那个数 也就是132

如果当前 nums = [3,2,1]。这就是1,2,3所有排序中最大的那个数,那么就返回1,2,3排序后所有数中最小的那个,也就是1,2,3 -> [1,2,3]

我们这个时候来分析,需要满足什么条件才能得到刚好比当前数大的那个数。

  1. 首先想让使一个数变大,那么我们就需要让数组后面比较大的数来和前面比较小的数进行替换
  2. 要刚好比当前数大需要满足数组前端的小数越靠右越好,而且后端的大数越小越好(当然是需要大于前端的小数情况下)
  3. 这个时候我们就应该从后往前找,找到nums[i+1]>nums[i],这个时候我们找到了这个小数,然后我们也知道[i+1,n)这个范围内的数都是递减的。
  4. 我们需要在[i+1,n)这个范围内找到刚好大于nums[i]的数nums[j];
  5. 然后我们交换i和j位置上的数,交换之后[i+1,n)这个范围内的数都是递减的,我们需要将其进行反转,让其递增,这样才能让数尽量小。
  6. 如果我们根本就没有找i+1和i满足条件,那么直接进入第5步。
class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        //寻找前一位比后一位要小的,此时找到之后[i+1,n]就应该都是降序排列的
        while(i >= 0 && nums[i] >= nums[i+1]){
            i--;
        }
        //如果存在这样的升序对
        if(i>=0){
            int j = nums.length - 1;
            //在[i+1,n]中找到一个既比i大又尽可能小的数。
            while(j >= 0 && nums[i] >= nums[j]){
                j--;
            }
            swap(nums, i, j);
        }
        reverse(nums, i+1);
     }
     private void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
     }
     private void reverse(int[] nums, int start){
         int left = start, right = nums.length - 1;
         while(left < right){
             swap(nums, left, right);
             left++;
             right--;
         }
     }
}

2、搜索旋转排序数组

//整数数组 nums 按升序排列,数组中的值 互不相同 。 
//
// 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= 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 。 
//
// 
//
// 示例 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 
// -10^4 <= nums[i] <= 10^4 
// nums 中的每个值都 独一无二 
// 题目数据保证 nums 在预先未知的某个下标上进行了旋转 
// -10^4 <= target <= 10^4 
// 
//
// 
//
// 进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗? 
// Related Topics 数组 二分查找 

我们还是按照原先的二分查找的方法,只是多了判决条件,需要先判断哪边的序列是有序的,如果有序直接进入普通的二分查找了,如果还是在无序的那一边,那就一直通过移位找到目标所在的有序子序列中。 因为目标如果存在,一定会存在于一个有序的子序列中。

public int search(int[] nums, int target) {
        int len = nums.length;
        int left = 0, right = len - 1;
        //只需要在有序的半段里用首尾两个数组来判断目标值是否在这一区域内
        while(left <= right){
            int mid = (left + right) / 2;
            if(nums[mid] == target){
                return mid;
            //如果右边是有序的
            }else if(nums[mid] < nums[right]){
                //在右边的序列中
                if(nums[mid] < target && target <= nums[right]){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            //如果左边是有序的
            }else{
                //在左边的序列中
                if(target >= nums[left] && nums[mid] > target){
                    right = mid - 1;
                }else{
                    left = mid + 1;
                }
            }
        }
        return -1;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值