【每日三题】2022-3-28 LeetCode 22-24

22.括号生成

思路:一个包含n组括号的有效字符串,一定可以分为"(" + L + ")" + R,其中L和R为包含“0~n-1"对括号的有效字符串,因此考虑递归。这里为什么不将其简单的分为两部分L + R呢,是因为L和R都可能呢为0个括号的有效字符串,而递归需要缩小状态空间。若不每次分出一个括号,则L或R任一为空字符串(即包含0个括号的有效字符串)时,会陷入死循环。L+R中总共包含n-1个括号,每次手动添加一个括号。遍历各种情况。此时可采用缓存来保存之前的结果,因为之后会调用用到。

代码:

private ArrayList[] cache = new ArrayList[9];

public List<String> generateParenthesis(int n) {
    if (cache[n] != null) {
        return cache[n];
    }
    ArrayList<String> result = new ArrayList<>();
    if (n == 0) {
        result.add("");
    } else {
        for (int i = 0; i < n; i++) {
            List<String> left = generateParenthesis(i);
            List<String> right = generateParenthesis(n - 1 - i);
            for (String l : left) {
                for (String r : right) {
                    result.add("(" + l + ")" + r);
                }
            }
        }
    }
    cache[n] = result;
    return result;
}

时间复杂度:O( 4 n n \frac{4^n}{\sqrt{n}} n 4n),这个复杂度取决于整体结果的状态空间大小,涉及到数学推导,有兴趣可查看卡特兰数

空间复杂度:O(n)

23.合并K个升序链表

思路:针对K个链表设立K个指针,每个指针始终指向每个链表当前尚未合并的最小元素。然后将这k个结点中最小的那个合并到结果中,同时将对应指针在对应的链表中推进一格。若推进到队尾,则不再推进。此时需要比较k个结点的大小,比较时间为O(K)。为优化效率,可使用优先队列(底层为小顶堆)来使得比较的时间复杂度优化为O(logK)。同时设立标志位记录已经被全部合并的链表数,当所有链表合并完成后,返回结果。(查看题解后,发现可以略去这个标志位,因为优先队列中元素的个数总是等同于当前未合并完成的链表数量)

代码:

private static class Entity {
    int index;
    ListNode value;

    public Entity(int index, ListNode value) {
        this.index = index;
        this.value = value;
    }
}

public ListNode mergeKLists(ListNode[] lists) {
    if (lists.length == 0) {
        return null;
    }
    ListNode head = new ListNode(-1), ptr = head;
    PriorityQueue<Entity> pq = new PriorityQueue<>(Comparator.comparingInt(o -> o.value.val));
    int emptyList = 0;
    for (int i = 0; i < lists.length; i++) {
        if (lists[i] == null) {
            emptyList ++;
        } else {
            pq.add(new Entity(i, lists[i]));
        }
    }
    while (emptyList < lists.length) {
        Entity entity = pq.poll();
        ptr.next = entity.value;
        ptr = ptr.next;
        if (entity.value.next == null) {
            emptyList ++;
        } else {
            entity.value = entity.value.next;
            pq.add(entity);
        }
    }
    return head.next;
}

时间复杂度:O(nlogk),此处n为所有链表中的结点数量和,因为优先队列中最多有k个元素,因此插入与弹出的时间复杂度都为O(logk),所有结点都要插入和弹出一次。

空间复杂度:O(k)

24.两两交换链表中的结点

思路:此时不允许修改结点的值,即需要在原链表中进行交换。每次交换需要链表中有至少两个结点还未完成交换,称第一个结点为first,第二个结点为second。交换过程中还需要涉及到first的前一个结点,因此采用三个指针指向这三个结点。交换时注意结点中next指针的更改顺序,避免丢失信息。交换完成后,更新指针并进行下一次交换,直到少于两个结点还未完成交换时,整个链表的交换完成。

代码:

public ListNode swapPairs(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode newHead = new ListNode(-1, head), ptr = newHead;
    while (ptr.next != null && ptr.next.next != null) {
        ListNode first = ptr.next, second = first.next;
        first.next = second.next;
        second.next = first;
        ptr.next = second;
        ptr = first;
    }
    return newHead.next;
}

时间复杂度:O(n)

空间复杂度:O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值