算法题解(Leetcode 21、22、23、31、32:合并两个有序链表、括号生成、合并K个升序链表、下一个排列、最长有效括号 )

21. 合并两个有序链表 - 简单 - 9/12

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

在这里插入图片描述

在这里插入图片描述


解析:

迭代:遍历两个链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode h = new ListNode(0); //合并后的链表
        ListNode ans = h;   //待返回的链表
        while(l1 != null && l2 != null){
            if(l1.val < l2.val){ //如果l1结点的值 小于 l2结点的值
                h.next = l1; //链表指向 较小的l1;
                h = h.next;  //移动到下一个结点
                l1 = l1.next;  //l1也移动到下一个结点
            }else{
                h.next = l2;
                h = h.next;
                l2 = l2.next;
            }
        }
        if(l1 == null){ //如果l1为空了,就把 h指向l2
            h.next = l2;
        }
        if(l2 == null){ //如果l2为空了,就把 h指向l1
            h.next = l1;
        }
        return ans.next;
    }
}

在这里插入图片描述
时间复杂度:O(m + n)。

空间复杂度:O(1)。

22. 括号生成 - 中等 - 9/13

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

有效括号组合需满足:左括号必须以正确的顺序闭合。

在这里插入图片描述


深度优先遍历:

在这里插入图片描述
画图以后,可以分析出的结论:

  • 当前左右括号都有大于 00 个可以使用的时候,才产生分支;
  • 产生左分支的时候,只看当前是否还有左括号可以使用;
  • 产生右分支的时候,还受到左分支的限制,右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以产生分支;
  • 在左边和右边剩余的括号数都等于 00 的时候结算。
class Solution {
    public List<String> generateParenthesis(int n) {
        //保存结果
        List<String> res = new ArrayList<>();
        //n为0,直接返回
        if(n == 0){
            return res;
        }
        //递归调用
        dfs("",n,n,res);
        //返回结果
        return res;
    }

    /**
     * @param str 当前递归得到的结果
     * @param left   左括号还有几个可以使用
     * @param right  右括号还有几个可以使用
     * @param res    结果集
     */
    private void dfs(String str,int left,int right,List<String> res) {
        //递归终止条件 
        if(left == 0 && right == 0){
        	//在递归终止的时候,直接把它添加到结果集即可
            res.add(str);
            return;
        }
        //剪枝
        if(left>right){
            return;
        }
        //如果左括号还剩有,就使用左括号后 -1,再递归调用
        if(left>0){
            dfs(str+"(",left-1,right,res);
        }
        //如果右括号还剩有,就使用右括号后 -1,再递归调用
        if(right > 0){
            dfs(str+")",left,right-1,res);
        }
    }
}

在这里插入图片描述

23. 合并K个升序链表 - 困难 - 9/14

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

在这里插入图片描述
在这里插入图片描述


解法一:暴力破解,遍历所有结点,存入到一个数组中,将数组排序后转为链表返回。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        //新建一个数组,用来存储所有值
        List<Integer> list = new ArrayList<>();
        //遍历所有结点,存入list
        for(ListNode node : lists){
            while(node != null){
                list.add(node.val); //添加到list中
                node = node.next;   //下一个结点
            }
        }
        //排序
        Collections.sort(list);
        //将集合转为链表
        ListNode head = new ListNode(0);
        ListNode h = head;
        for(int i : list){
            ListNode newNode = new ListNode(i); //新结点
            h.next = newNode; //拼接新结点
            h = h.next; //指向下一个结点
        }
        h.next = null; //最后一个结点为空
        return head.next; //返回head
    }
}

在这里插入图片描述

31. 下一个排列 - 中等 - 9/15

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

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

在这里插入图片描述在这里插入图片描述


解析:从右向左找到第一个数字不再递增的位置,然后从右边找到一个刚好大于当前位的数字,再将这两个数字调换位置,最后把剩下的反转处理即可。

class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length-2;
        //找到第一个不再递增的下标
        while(i>=0 && nums[i] >= nums[i+1]){
            i--;
        }
        //如果i小于0,则说明没有,就反转整个数组
        if(i<0){
            reverse(nums,0);
            return;
        }
        //找到刚好大于nums[i]大的下标
        int j = nums.length - 1;
        while(j>=0 && nums[j] <= nums[i]){
            j--;
        }
        //交换
        swap(nums,i,j);
        //将剩下的反转
        reverse(nums,i+1);

    }

    //交换方法
    private void swap(int[] nums,int i, int j){
        int temp = nums[j];
        nums[j] = nums[i];
        nums[i] = temp;
    }

    //反转方法
    private void reverse(int[] nums, int start){
        int i = start;
        int j = nums.length - 1;
        while(i<j){
            swap(nums,i,j);
            i++;
            j--;
        }
    }
}

在这里插入图片描述
时间复杂度:最坏的情况就是遍历完所有位,O(n),倒置也是 O(n),所以总体依旧是 O(n)。

空间复杂度:O(1)。

32. 最长有效括号 - 困难 - 9/16

给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

在这里插入图片描述

在这里插入图片描述


解析:

从左到右扫描字符串,栈顶保存当前扫描的时候,合法序列前的一个位置位置下标是多少,啥意思嘞?

我们扫描到左括号,就将当前位置入栈。

扫描到右括号,就将栈顶出栈(代表栈顶的左括号匹配到了右括号),然后分两种情况。

  • 栈不空,那么就用当前的位置减去栈顶的存的位置,然后就得到当前合法序列的长度,然后更新一下最长长度。

  • 栈是空的,说明之前没有与之匹配的左括号,那么就将当前的位置入栈。

看下图示,更好的理解一下。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


class Solution {
    public int longestValidParentheses(String s) {
        int maxlen = 0; //有效字串的长度
        Stack<Integer> stack = new Stack<>(); //创建栈
        stack.push(-1); //初始化栈
        for(int i=0; i<s.length(); i++){
            if(s.charAt(i) == '('){  //如果是左括号,就添加进栈
                stack.push(i);
            }
            else{ //如果当前是右括号
                stack.pop(); //出栈
                if(stack.isEmpty()){ //出栈之后如果栈为空,则添加最后一个为被匹配的右括号进去
                    stack.push(i);
                }else{
                    //返回
                    maxlen = Math.max(maxlen,i-stack.peek());
                }
            }
        }
        return maxlen;
    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值