【LeetCode热题100】打卡第11天:有效括号&合并两个有序链表

【LeetCode热题100】打开第11天

⛅前言

大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏!

精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。在此专栏中,我们将会涵盖各种类型的算法题目,包括但不限于数组、链表、树、字典树、图、排序、搜索、动态规划等等,并会提供详细的解题思路以及Java代码实现。如果你也想刷题,不断提升自己,就请加入我们吧!QQ群号:827302436。我们共同监督打卡,一起学习,一起进步。

博客主页💖:知识汲取者的博客

LeetCode热题100专栏🚀:LeetCode热题100

Gitee地址📁:知识汲取者 (aghp) - Gitee.com

Github地址📁:Chinafrfq · GitHub

题目来源📢:LeetCode 热题 100 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

PS:作者水平有限,如有错误或描述不当的地方,恳请及时告诉作者,作者将不胜感激

🔒有效括号

原题链接:Loading Question… - 力扣(LeetCode)

在这里插入图片描述

🔑题解

  • 解法一:替换

    /**
     * @author ghp
     * @title 有效的括号
     */
    class Solution {
        public boolean isValid(String s) {
            if (s.length() % 2 == 1) {
                // 字符个数为奇数,一定含有无效括号
                return false;
            }
            // 循环替换所有的括号,直到无法替换为止
            while (true) {
                int len = s.length();
                s = s.replace("()", "");
                s = s.replace("{}", "");
                s = s.replace("[]", "");
                if (s.length() == len) {
                    // 已经将所有可替换的全都替换了,如果此时len为0说明字符串有效
                    return len == 0;
                }
            }
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( n 2 ) O(n^2) O(n2),while循环每一次都替换一对括号,总共需要循环n/2次,每次替换replace方法的时间复杂度是n,所以总的时间复杂度为 O ( n / 2 ∗ n ) O(n/2*n) O(n/2n)
    • 空间复杂度: O ( 1 ) O(1) O(1)

    其中 n n n 为括号的个数

  • 解法二:栈

    这个虽然时间复杂度

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Stack;
    
    /**
     * @author ghp
     * @title 有效的括号
     */
    class Solution {
    
        public static final Map<Character, Character> brackets = new HashMap() {{
            put('(', ')');
            put('[', ']');
            put('{', '}');
        }};
    
        public boolean isValid(String s) {
            if (s.length() % 2 == 1) {
                // 字符个数为奇数,一定含有无效括号
                return false;
            }
            Stack<Character> stack = new Stack<>();
            for (int i = 0; i < s.length(); i++) {
                char ch = s.charAt(i);
                if (brackets.containsKey(ch)) {
                    // 左括号,直接入栈
                    stack.push(ch);
                } else {
                    // 右括号,出栈,并判断是否与当前括号匹配(注意这里要判空,防止NPE)
                    if (stack.isEmpty() || brackets.get(stack.pop()) != ch) {
                        return false;
                    }
                }
            }
            return stack.isEmpty();
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( n ) O(n) O(n)
    • 空间复杂度: O ( n ) O(n) O(n)

    其中 n n n 为字符的个数


🔒合并两个有序链表

在这里插入图片描述

🔑题解

💡思路一:合并到第三方链表上

创建一个新的头节点,然后构成一个新链表,这个链表用来存放链表1和链表2比较留下的最小的结点

🔐【非递归实现】

解题示意图

原始代码

import java.util.*;

class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null || list2 == null) {
        	//只要有一个为空就直接返回另一个
        	return list1 != null ? list1 : list2;
        }
        ListNode head1 = list1;//辅助遍历list1
        ListNode head2 = list2;//辅助遍历list2
        int val;//用来创建新链表的头节点
        //选出链表1和链表链表2较小的头节点作为 新链表的头节点的val
        if(head1.val < head2.val) {
        	val = head1.val;
        	head1 = head1.next;//这里需要后移,因为已经比较过了,后面就不需要再进行比较了
        }else {
        	val = head2.val;
        	head2 = head2.next;
        }
        ListNode newNode = new ListNode(val);//新建一个头节点,他是合并后链表的头节点
        ListNode head = newNode;//指向newNode链表,辅助遍历
        while(head1 != null && head2 != null) {//切记判断不要使用head1.next或者head2.next
        	if(head1.val < head2.val) {
                //说明当前链表1的结点是最小的
        		head.next = head1;//将链表1的结点添加到新链表上
        		head = head.next;//始终保证辅助指针再新链表的尾结点,方便结点的添加
        		head1 = head1.next;
        	}else {
        		head.next = head2;
        		head = head.next;
        		head2 = head2.next;
        	}
        }
        if(head1 != null && head2 == null) {
            //直接将链表1不为空的部分连接到新链表后面
        	head.next = head1;
        }
        if(head1 == null && head2 != null) {
            直接将链表2不为空的部分连接到新链表后面
        	head.next = head2;
        }
        return newNode;
    }
}

期间犯的错去:新建结点直接ListNode newNode = null;导致报空指针异常,原因是为空时,后面用到了head.next = head1|head2,后面还会讲解(当然还有一些睁眼瞎的错误,就不记录了w(゚Д゚)w

代码优化

  • 优化1:时间优化。我们可以直接创建一个结点,然后返回的时候只返回newNode.next结点即可,这样就不需要先进行判断了是用链表1的结点作为头节点,还是链表2的结点作为头节点,能够一定程度降低时间损耗。
  • 优化2:空间优化。同时因为我们是将链表1和链表2进行合并,不再需要再使用链表1和链表2,所以可以直接使用list1和list2进行遍历,而不再借助辅助指针进行比遍历,能一定程度减少内存消耗

优化后的代码

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null || list2 == null) {
        	//只要有一个为空就直接返回另一个
        	return list1 != null ? list1 : list2;
        }
        ListNode newNode = new ListNode(-1);//新建一个空指针,指向合并后链表的头节点
        ListNode head = newNode;//辅助newNode,用新增结点
        while(list1 != null && list2 != null) {
        	if(list1.val < list2.val) {
        		head.next = list1;
        		head = head.next;
        		list1 = list1.next;
        	}else {
        		head.next = list2;
        		head = head.next;
        		list2 = list2.next;
        	}
        }
        if(list1 != null && list2 == null) {
            //直接将链表1不为空的部分连接到新链表后面
        	head.next = list1;
        }
        if(list1 == null && list2 != null) {
            直接将链表2不为空的部分连接到新链表后面
        	head.next = list2;
        }
        return newNode.next;
    }
}
🔐【递归实现】

解题示意图
在这里插入图片描述

代码实现

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
    	if(list1 == null || list2 == null) {
        	//只要有一个为空就直接返回另一个
        	return list1 != null ? list1 : list2;
        }
        ListNode newNode = new ListNode(-1);//新建头节点,用于构建新链表
        ListNode head = newNode;//辅助指针,用于遍历新链表同时新增结点
        if(list2.val>list1.val){
        	head.next = list1;//将链表1的结点挂到新链表上
        	head = head.next;//始终保证head指向新链表的尾结点,方便增加结点
            head.next = Merge(list1.next,list2);//list1已经比完,移动list1指针
        }
        else{
        	head.next = list2;
        	head = head.next;
            head.next = Merge(list1,list2.next);
        }
        return newNode.next;
    }
}

💡思路二 :合并到一个链表上

先确定头节点是选用list1还是list2,然后借助辅助指针,将链表1和链表2串连起来

🔐【非递归实现】

解题示意图

在这里插入图片描述

代码实现

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null || list2 == null) {
        	//只要有一个为空就直接返回另一个
        	return list1 != null ? list1 : list2;
        }
        ListNode newNode = null;//新建一个空指针,指向合并后链表的头节点
        ListNode head = null;//辅助newNode,用新增结点
        while(list1 != null && list2 != null) {
        	if(list1.val < list2.val) {
        		if(newNode == null) {
        			//以head1为头结点
        			newNode = list1;
        			head = newNode;//设置辅助指针,因为头指针是新链表的位置标识,不能动
        			list1 = list1.next;
        			continue;//后面的代码就不需要执行了
        		}
        		head.next = list1;
        		head = head.next;
        		list1 = list1.next;
        	}else {
        		if(newNode == null) {
                    //以head2为头节点
        			newNode = list2;
        			head = newNode;
        			list2 = list2.next;
        			continue;
        		}
        		head.next = list2;
        		head = head.next;
        		list2 = list2.next;
        	}
        }
        if(list1 != null && list2 == null) {
            //直接将链表1不为空的部分连接到新链表后面
        	head.next = list1;
        }
        if(list1 == null && list2 != null) {
            直接将链表2不为空的部分连接到新链表后面
        	head.next = list2;
        }
        return newNode;
    }
}
🔐【递归实现】

代码实现

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null || list2 == null) {
        	return list1 != null ? list1 : list2;//只要有一个为空就直接返回另一个
        }
        //判断选用哪一个作为头节点
        if(list2.val>list1.val){
            //开始递归
            list1.next = Merge(list1.next,list2);
            return list1;
        }
        else{
            list2.next = Merge(list1,list2.next);
            return list2;
        }
    }
}

这个是参考别人的,这段代码太妙了wow~ ⊙o⊙

具体实现思路,就是不断地去寻找两个链表的最小结点,直到为空为止(具体过程我知道,但是不知道为什么描述不出来,可能是还没完全懂吧┭┮﹏┭┮)

递归需要两个必备条件:递归终止条件+递归入口条件。即什么时候结束递归,什么时候开始递归。明白这两点应该就能看懂这段代码了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知识汲取者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值