31-35 下一个排列 - 最长有效括号 - 搜索旋转排序数组 - 排序数组中查找元素的第一个和最后一个位置 - 搜索插入位置

31. 下一个排列

从右向左找到第一个数字不再递增的位置设为nums[i],然后在nums[i]右边找到一个刚好大于当前位的数字nums[j]交换;
然后将nums[i]右边的数字sort一下;
由于从右向左是递增的,所以可以写一个倒序函数来sort,或者直接用Arrays.sort也行

//有一点单调栈的思想在里面
//我们其实是从右向左找到第一个数字不再递增的位置,然后从右边找到一个刚好大于当前位的数字即可。
public static void nextPermutation(int[] nums) {
    int i = nums.length - 2;
    //从右向左找到第一个数字不再递增的位置
    while (i >= 0 && nums[i] >= nums[i + 1]) {
        i--;
    }
    //如果从右向左一直递增
    if (i < 0) {
        Arrays.sort(nums);
        return;
    }
    int j = nums.length - 1;
    //从右边找到一个刚好大于当前位的数字
    while (j >= 0 && nums[j] <= nums[i]) {
        j--;
    }
    swap(nums, i, j);
    int left = i + 1;
    int right = nums.length - 1;
    while (left < right) {
        swap(nums, left, right);
        left++;
        right--;
    }
    return;
}

public static void swap(int[] nums, int i, int j) {
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

32. 最长有效括号

  • 从左到右扫描字符串,扫描到左括号,就将当前位置入栈。
  • 扫描到右括号,就将栈顶出栈(代表栈顶的左括号匹配到了右括号),这个时候然后分一下两种情况:
    • 栈不空,那么就用当前的位置减去栈顶的存的位置,然后就得到当前合法序列的长度,然后更新一下最长长度。
    • 栈是空的,说明之前没有与之匹配的左括号,那么就将当前的位置入栈
  • 其实多余的左括号和右括号的位置,都会落到栈底,为什么?因为合法的括号都是成对出现的,左括号都会压入的话遇见右括号就弹出了,最终不会出现在栈里面
  • 初始化的时候为了便于计算第一个合法括号(我们需要pop之后的peek),压入-1
public int longestValidParentheses(String s) {
    if (s == null || s.length() == 0) return 0;
    Stack<Integer> stack = new Stack<>();
    //为了方便计算第一个合法括号的子串长度,我们先加一个-1
    stack.push(-1);
    int res = 0;
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        //扫描到左括号,就将当前位置入栈
        if (c == '(') {
            stack.push(i);
        } else {
        //扫描到右括号,先pop
            stack.pop();
            //如果栈空,说明之前没有左括号与之匹配,则将当前位置压栈
            if (stack.isEmpty()) {
                stack.push(i);
            } else {
            //如果栈不是空的,就用i-stack.peek()更新res
                res = Math.max(res, i - stack.peek());
            }
        }
    }
    return res;
}

33. 搜索旋转排序数组

  • 查找一般都是用二分,题目也要求时间复杂度对数级别
  • 用闭区间表示[0,nums.length - 1],所以跳出循环的时候需要left>right(这样才是空集)
  • 若nums[left] <= nums[mid],说明mid前面是有序的:
    • nums[left] <= target < nums[mid]时,在前半部分找,right=mid-1;
    • 否则在后半部分找,left=mid+1;
  • 同理, 若nums[left] > nums[mid],说明mid后面是有序的
    • nums[mid] < target <= nums[right]时,在后半部分找left=mid+1
    • 否则,right=mid-1;
public static int search2(int[] nums, int target) {
    if (nums == null || nums.length == 0) return -1;
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
        int mid = (left + right) >>> 1;
        if (nums[mid] == target) {
            return mid;
        }
        //mid前面都是有序的,nums[left]<=target<nums[mid],则在前半部分找,否则去后半部分找。
        if (nums[left] <= nums[mid]) {
            if (nums[left] <= target && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;//注意这里不能写成left = mid;因为已经判断过了nums[mid] != target
            }
        } else {
            if (target > nums[mid] && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }
    return -1;
}

34. 在排序数组中查找元素的第一个和最后一个位置(经典)

  • 找左边界left的时候,一下几种情况直接返回不存在:
    • 如果target < nums[0],i=0;
    • 如果target > nums[nums.length-1],i = nums.length;
    • 如果nums[0]<target<nums[nums.length-1]但是nums[i] != target
  • 若左边界left存在,那么右边界肯定存在, 且left<=right<=nums.length-1
    • j指向right
public int[] searchRange(int[] nums, int target) {
    if (nums == null || nums.length == 0) return new int[]{-1, -1};
    int i = 0;
    int j = nums.length - 1;
    //找左边界引索
    while (i <= j) {
        int mid = (i + j) >>> 1;
        if (nums[mid] == target) {
            j = mid - 1;
        } else if (nums[mid] < target) {
            i = mid + 1;
        } else if (nums[mid] > target) {
            j = mid - 1;
        }
    }
    //如果target < nums[0],i=0;如果target > nums[nums.length-1],i >= nums.length;
    // 如果nums[0]<target<nums[nums.length-1]且nums[i] != target
    if (i >= nums.length || i >= 0 && nums[i] != target) {
        return new int[]{-1, -1};
    }
    int left = i;
    i = 0;
    j = nums.length - 1;
    //找右边界引索
    while (i <= j) {
        int mid = (i + j) >>> 1;
        if (nums[mid] == target) {
            i = mid + 1;
        } else if (nums[mid] < target) {
            i = mid + 1;
        } else if (nums[mid] > target) {
            j = mid - 1;
        }
    }
    int right = j;
    return new int[]{left, right};
}

35. 搜索插入位置

public int searchInsert(int[] nums, int target) {
    int length=nums.length;
    if(length==0) return 0;
    //插入位置有可能在数组的末尾
    if(target>nums[length-1]){
        return length;
    }
    //如果target比nums[length-1]小,那插入的index只可能在[0,length-1]
    int left=0;
    int right=length-1;
    while (left<right){//意思是left=right时返回任意一个即可
        int mid=(left+right)>>>1;
        if(nums[mid]<target){//不可能
            left=mid+1;
        }else{//有可能
            right=mid;
        }
    }
    return left;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值