大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏!
精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。在此专栏中,我们将会涵盖各种类型的算法题目,包括但不限于数组、链表、树、字典树、图、排序、搜索、动态规划等等,并会提供详细的解题思路以及Java代码实现。如果你也想刷题,不断提升自己,就请加入我们吧!QQ群号:827302436。我们共同监督打卡,一起学习,一起进步。
博客主页:知识汲取者的博客
LeetCode热题100专栏:LeetCode热题100
Gitee地址:知识汲取者 (aghp) - Gitee.com
题目来源:LeetCode 热题 100 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台
PS:作者水平有限,如有错误或描述不当的地方,恳请及时告诉作者,作者将不胜感激
原题链接:226.翻转二叉树
解法一:递归
刷LeetCode热题100也有一个月了,现在有那么一丢丢的感觉了,遇到树相关的问题,绝大多数递归就对了。
这题同样使用递归,递归遍历左右子树一下就决解了,不愧是简单题,思路也很简单:先递归到最后一层,然后自底向上交换当前节点左右节点,这样就能够实现翻转二叉树了
public class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}
}
复杂度分析:
其中 n n n 为树中节点的个数, h h h是树的高度
解法二:迭代
迭代本质和递归是一样的,只是将隐士的栈变成了显示的栈,和上面解法是完全等价的。
不是很懂的可以区看这篇文章:【LeetCode热题100】打卡第27天:二叉树的前序、中序、后序遍历,这篇文章对迭代遍历树有较为详细的讲解
public class Solution {
public TreeNode invertTree(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root;
while (!stack.isEmpty() || cur != null) {
// 遍历当前节点的左子树
while (cur != null) {
stack.push(cur);
// 将指针指向左子节点
cur = cur.left;
}
TreeNode node = stack.pop();
// 交换左右子节点
TreeNode left = node.left;
TreeNode right = node.right;
node.left = right;
node.right = left;
// 将指针指向右子节点
cur = right;
}
return root;
}
}
复杂度分析:
其中 n n n 为树中节点的个数, h h h是树的高度
解法三:利用遍历交换左右子节点
在遍历的同时,可以交换左右节点,至于二叉树的遍历方式就有很多种了,常见的比如:中序、后序、前序、层序,遍历方式又可以分为:递归、迭代,这么算起来,解法三就有 8 种不同的实现方式了,这里我就只列举一种,其它方式,大家可以自己参考这我上一个解法中提到的那篇文章进行实现,这里我就使用层序遍历实现一下,关于层序遍历详细可以参考我的这篇文章:【LeetCode热题100】打卡第29天:二叉树的层序遍历,层序遍历的核心实现思路就是 BFS
PS:其实解法一和解法二本质上就是采用中序遍历的思想:先左后中再右
public class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
// 防止后面出现NPE
return null;
}
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
TreeNode left = node.left;
TreeNode right = node.right;
node.left = right;
node.right = left;
if (left != null) {
queue.offer(left);
}
if (right != null) {
queue.offer(right);
}
}
return root;
}
}
原题链接:234.回文链表
解法一:暴力
最直接的方法,遍历所有的节点,利用StringBuilder进行拼接,然后判断反转后的内容是否一致,如果一致则说明当前链表是一个回文串,否则不是,这里有一个比较坑的点,就是StringBuilder没有重写Object的toString方法,所以如果调用equals方法,比较的地址,所以这里我们需要将StringBuilder转成String(String重写了Object的toString方法),然后再调用equals方法进行比较
public class Solution {
public boolean isPalindrome(ListNode head) {
StringBuilder sb = new StringBuilder();
while (head != null) {
sb.append(head.val);
head = head.next;
}
String str1 = new StringBuilder(sb).reverse().toString();
String str2 = sb.toString();
return str1.equals(str2);
}
}
复杂度分析:
其中 n n n 为链表中元素的个数
解法二:快慢指针
在写链表相关题目时,已经好几次遇到快慢指针了,从最开始的“我靠,还有这种方式“、到后来的“额原来还可以这样写”,再到现在的”不就是快慢指针吗,这不有手就行“(我还没到这一种心态,我还在第二种心态o((>ω< ))o)
算法实现的主要核心思路:快指针走到末尾,慢指针刚好到中间。其中慢指针将前半部分反转。然后比较
关于反转链表的部分我们可以参考这一题:【LeetCode热题100】打卡第37天:反转链表
关于快慢指针的部分我们可以参考这一题:【LeetCode热题100】打卡第34天:排序链表
/**
* @author ghp
* @title
*/
public class Solution {
public boolean isPalindrome(ListNode head) {
if (head.next == null) {
// 链表只有一个节点,直接返回,防止后面出现NPE
return true;
}
ListNode fast = head, slow = head, pre = null;
// 定位中间节点,同时反转链表的前半段
while (fast != null && fast.next != null) {
// fast多走一步
fast = fast.next.next;
// 反转前半段链表
ListNode next = slow.next;
slow.next = pre;
pre = slow;
slow = next;
}
if (fast != null) {
// 当前链表具有偶数个节点,还需要将slow往后移动一位
slow = slow.next;
}
// 逐个比较 反转后的前半段链表 和 未反转的后半段链表 的节点的val是否相等
while (pre != null && slow != null) {
if (pre.val != slow.val) {
// 有一个节点的val不相等,说明当前链表不是回文链表
return false;
}
pre = pre.next;
slow = slow.next;
}
// 前后两截链表的val都相等,则说明当前链表是回文链表
return true;
}
}
复杂度分析:
其中 n n n 为链表中元素的个数