链表中环的入口节点(中等)
可以使用快慢指针法或者遍历一遍通过哈希表判断是否出现过重复。
下面先实现快慢指针法:
快慢指针相遇后,慢指针和头指针分别向前移动,直至慢指针和头指针相遇,返回慢指针即入口节点
package org.meituan.leetcode;
/**
* @projectName: codebetter
* @package: org.meituan.leetcode
* @className: DetectCycle
* @author: fangjiayueyuan
* @description: TODO
* @date: 2023/5/4 下午10:10
* @version: 1.0
*/
public class DetectCycle {
public static void main(String[] args) {
ListNode listNode1 = new ListNode(3);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(0);
ListNode listNode4 = new ListNode(-4);
// ListNode listNode5 = new ListNode(2);
listNode1.next=listNode2;
listNode2.next=listNode3;
listNode3.next=listNode4;
listNode4.next=listNode1;
ListNode output = new SolutionDetectCycle().detectCycle(listNode1);
System.out.println(output.val);
}
}
class SolutionDetectCycle {
public ListNode detectCycle(ListNode head) {
if(head== null) return null;
ListNode fastNode = head;
ListNode slowNode = head;
ListNode startNode = head;
while(fastNode!=null){
if(fastNode.next != null && fastNode.next.next!=null){
fastNode = fastNode.next.next;
}else{
return null;
}
if(slowNode.next != null){
slowNode = slowNode.next;
}else{
return null;
}
if(fastNode==slowNode){
break;
}
}
while(startNode!=slowNode){
startNode = startNode.next;
slowNode = slowNode.next;
}
// if(fastNode==null)return null;
// if(fastNode==slowNode){
// return slowNode.next;
// }
return slowNode;
}
}
反转链表
采用循环和递归两种方式进行解题
循环
- 循环的重点在于保存当前节点的下一个节点,为了防止多次使用
.next
把问题搞复杂,最好每个涉及到的节点都使用一个变量来维护,更加清晰。每次反转之后,pre节点的指针也需要向后移动。 - 还有一处需要注意,
if(head == null || head.next == null)
要把head == null
写在前面,判断的时候会按照顺序来判断
package org.meituan.leetcode;
import java.util.List;
/**
* @projectName: codebetter
* @package: org.meituan.leetcode
* @className: ReverseList
* @author: fangjiayueyuan
* @description: TODO
* @date: 2023/5/4 下午3:54
* @version: 1.0
*/
public class ReverseList {
public static void main(String[] args) {
ListNode listNode1 = new ListNode(4);
ListNode listNode2 = new ListNode(5);
ListNode listNode3 = new ListNode(1);
ListNode listNode4 = new ListNode(9);
listNode1.next=listNode2;
listNode2.next=listNode3;
listNode3.next=listNode4;
ListNode output = new SolutionReverseList().reverseList(listNode1);
while(output != null){
System.out.println(output.val);
output= output.next;
}
}
}
class SolutionReverseList {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode node = head;
ListNode nodePre = null;
ListNode nodeNext = null;
ListNode nodeLast = null;
while(node != null){
if(node.next == null){
nodeLast = node;
}
nodeNext = node.next; //实现反转之前,先保存好下一个节点,防止链表断掉
node.next = nodePre; // 实现反转
nodePre = node;
node = nodeNext;
}
return nodeLast;
}
}
递归
递归的思考思路:
1、找出递归公式,有点类似于数学中的归纳法,所有的递归问题都可以用递推公式来表示。有了这个递推公式,我们就可以很轻松地将它改为递归代码;递归只能考虑当前层和下一层的关系,不能继续往下深入,继续深入就容易陷入思想的怪圈;
2、找到终止条件。
人脑几乎无法把递和归一步步想清楚(不是只有我想不清楚),一步步做递归是计算机擅长的事情,我们只需要把迭代条件和终止条件想明白告诉计算机即可!(参考)
关于递归,这两个视频也很有用:
1)https://www.bilibili.com/video/BV1UD4y1Y769/
2)https://www.bilibili.com/video/BV18M411z7bb/
回归到题目本身,递归公式F(node) = F(node.next)+{递归之后的操作},假设一个链表为1->2->3,如果2<-3已经反转完成了F函数,下一步是需要把node.next的next指向node实现反转即1<-2,并让node.next指向null,否则链表将会成环即1->2且1<-2。
实现代码如下:
package org.meituan.leetcode;
import java.util.List;
/**
* @projectName: codebetter
* @package: org.meituan.leetcode
* @className: ReverseList
* @author: fangjiayueyuan
* @description: TODO
* @date: 2023/5/4 下午3:54
* @version: 1.0
*/
public class ReverseList {
public static void main(String[] args) {
ListNode listNode1 = new ListNode(4);
ListNode listNode2 = new ListNode(5);
ListNode listNode3 = new ListNode(1);
ListNode listNode4 = new ListNode(9);
listNode1.next=listNode2;
listNode2.next=listNode3;
listNode3.next=listNode4;
ListNode output = new SolutionReverseList().reverseList(listNode1);
while(output != null){
System.out.println(output.val);
output= output.next;
}
}
}
class SolutionReverseList {
public ListNode reverseList(ListNode head) {
if(head.next == null || head==null){
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
合并两个排序的链表
分别采用循环、递归两种方法进行解题
循环
1、双指针:分别指向两个链表的节点
2、构造头指针:要构造两个头指针,一个标识新的链表的头节点的位置,一个新的链表中每个节点的位置。(不构造两个节点,想破头也没想出来解法。)
package org.meituan.leetcode;
/**
* @projectName: codebetter
* @package: org.meituan.leetcode
* @className: MergeTwoLists
* @author: fangjiayueyuan
* @description: TODO
* @date: 2023/5/4 下午5:18
* @version: 1.0
*/
public class MergeTwoLists {
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(3);
ListNode listNode3 = new ListNode(5);
ListNode listNode4 = new ListNode(7);
listNode1.next=listNode2;
listNode2.next=listNode3;
listNode3.next=listNode4;
ListNode listNode5 = new ListNode(1);
ListNode listNode6 = new ListNode(3);
ListNode listNode7 = new ListNode(5);
ListNode listNode8 = new ListNode(7);
listNode5.next=listNode6;
listNode6.next=listNode7;
listNode7.next=listNode8;
ListNode output = new SolutionMergeTwoLists().mergeTwoLists(listNode1,listNode5);
while(output != null){
System.out.println(output.val);
output= output.next;
}
}
}
class SolutionMergeTwoLists {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null)return l2;
if(l2 == null) return l1;
ListNode head = new ListNode(0);
ListNode cur = head;
while(l1 != null && l2 != null){
if(l1.val<l2.val){
cur.next = l1;
l1 = l1.next;
}else{
cur.next=l2;
l2 = l2.next;
}
cur = cur.next;
}
if(l1!=null){
cur.next=l1;
}
else{
cur.next=l2;
}
return head.next;
}
}
递归
1、递推公式:
F(n)为合并两个链表,F(n)=初始状态+F(n-1),F(n-1)即拆解子问题,因为题目要求是按照递增的顺序,由于两条链表的头节点哪个更小是不确定的,由于初始状态不同,F(n-1)也不同。
l1更小时,F(n)为l1+F(n-1),F(n-1)即l1.next = mergeTwoLists(l1.next,l2);
否则:F(n)为l2+F(n-1),F(n-1)即l2.next = mergeTwoLists(l1,l2.next);
2、终止条件:终止条件一般为边界条件,即链表为空。链表为空,则合并后返回另一个链表头结点即可。
代码如下:
package org.meituan.leetcode;
/**
* @projectName: codebetter
* @package: org.meituan.leetcode
* @className: MergeTwoLists
* @author: fangjiayueyuan
* @description: TODO
* @date: 2023/5/4 下午5:18
* @version: 1.0
*/
public class MergeTwoLists {
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(3);
ListNode listNode3 = new ListNode(5);
ListNode listNode4 = new ListNode(7);
listNode1.next=listNode2;
listNode2.next=listNode3;
listNode3.next=listNode4;
ListNode listNode5 = new ListNode(1);
ListNode listNode6 = new ListNode(3);
ListNode listNode7 = new ListNode(5);
ListNode listNode8 = new ListNode(7);
listNode5.next=listNode6;
listNode6.next=listNode7;
listNode7.next=listNode8;
ListNode output = new SolutionMergeTwoLists().mergeTwoLists(listNode1,listNode5);
while(output != null){
System.out.println(output.val);
output= output.next;
}
}
}
class SolutionMergeTwoLists {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null)return l2;
if(l2 == null) return l1;
if(min(l1,l2)==l1){
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}else {
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
ListNode min(ListNode l1, ListNode l2){
// 返回两个节点中节点值较小的节点
if(l1.val<l2.val){
return l1;
}else{
return l2;
}
}
}
树的子结构(中等)
1、
前置题目推荐:相同的树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSameTree(TreeNode A, TreeNode B) {
if(A==null && B==null){
return true; // 如果两条树相同则一定相同
}
if(A==null || B==null){
return false; // 如果两条树只有一条为空则一定不同
}
return A.val==B.val && isSameTree(A.left,B.left) && isSameTree(A.right,B.right);
}
}
前置题目2:另一棵树的子树
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root==null && subRoot==null){
return true;
}
if(root==null && subRoot!=null){
return false;
}
if(root!=null && subRoot==null){
return true;
}
if(isSame(root,subRoot)){
return true;
}
return isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);
}
public boolean isSame(TreeNode root1,TreeNode root2){
if(root1==null && root2==null){
return true;
}
if(root1==null || root2==null){
return false;
}
return root1.val==root2.val && isSame(root1.left,root2.left) && isSame(root1.right,root2.right);
}
}
二叉树的镜像
采用递归的思路来解二叉树的问题
1、递归公式:
寻找子问题,把二叉树镜像就是分别把二叉树的左孩子镜像、二叉树的右孩子镜像;
子问题解决后,还需要把根节点的右孩子指向左孩子镜像,根节点的左孩子指向右孩子镜像即可。
2、终止条件:二叉树为空,返回空;二叉树只有根节点,返回根节点。
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root==null)return null;
TreeNode left = mirrorTree(root.left);
TreeNode right = mirrorTree(root.right);
root.left = right;
root.right = left;
return root;
}
}