一、链表

1. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

「链表 headA」的节点数量为 a ,「链表 headB」的节点数量为 b ,「两链表的公共尾部」的节点数量为 c

指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:
a+(b−c)
指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:
b+(a−c)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
      ListNode A = headA;
      ListNode B = headB;
      while(A!=B){ //只要A/B不为空就往下走,走到尾则跳到另一个头
        A = A!= null ? A.next : headB;
        B = B!= null ? B.next : headA;
      }
      //第一次相遇就在公共节点
      return A;
        
    }
}

2. 反转链表

主要是三个指针的移动,先移动cur.next,再移动pre,最后再移动cur。细节:在while里面先创建一个next,为了最后的cur移动。

/**
 * 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 reverseList(ListNode head) {
        ListNode pre = null; //新的头结点的引用
        ListNode cur = head;
        while(cur!=null){
          ListNode next = cur.next;
          //重点是这三个指针的移动
          cur.next = pre;
          //必须要先移动pre,再移动cur,否则会丢失前节点
          pre = cur;
          cur = next;
        }
        return pre;

    }
}
//方法二:递归

3. 回文链表-@官方题解一 

其实很简单。总共就两步:1、把链表每个节点的值复制到数组当中,数组.add(cur.val)。2、双指针分别从最前面和最后面对比是否相同。

/**
 * 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 boolean isPalindrome(ListNode head) {
      List<Integer> vals = new ArrayList<Integer>(); //创建数组
      //将链表值赋值到数组中
      ListNode cur = head;
      while(cur!=null){
        vals.add(cur.val);
        cur = cur.next;
      }
       //双指针判断回文
       int front  = 0;
       int back = vals.size()-1;
       while(front < back){
        if(!vals.get(front).equals(vals.get(back))){
          return false;
        }
        front ++;
        back --;
       }
       return true;

    }
}

4. 环形链表(判断链表有无环)-官方题解方法二

思路也很简单。首先特判:要是头结点为空,或头结点.next为空,也就是只有一个节点,都不可能成环。总体思路:快慢指针,慢移动一个,快移动两个,如果 fast==null 或 fast.next == null,说明无环 return false,若快慢相遇说明有环。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
 //官方题解方法二
public class Solution {
    public boolean hasCycle(ListNode head) {
      //特判
      if(head == null || head.next == null){
        return false;
      }
      ListNode slow = head;
      ListNode fast = head.next;
      while(slow != fast){
        if(fast == null || fast.next == null){
          return false;
        }
        slow = slow.next;
        fast = fast.next.next;
      }
      return true;
        
    }
}

5. 环形链表Ⅱ-官方题解:方法二

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

其实也是定义快慢指针,只是在上一题基础上,当快慢相遇的时候,重新定义一个指针从头结点跟慢指针一起移动,相遇的节点就是环入口。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
      if(head == null || head.next == null ){
        return null;
      }
      ListNode slow = head;
      ListNode fast = head;
      while(fast != null && fast.next != null){
        slow = slow.next;
        fast = fast.next.next;
        if(slow == fast){
          ListNode ptr = head;
          while(ptr != slow){
            ptr = ptr.next;
            slow = slow.next;
          }
          return ptr;
        }
      }
      return null;        
    }
}

6. 合并两个有序链表-@腐烂的橘子-递归

主要理解递归思想,逐个比较链表数值,小的那个list.next = 新的list继续merge

/**
 * 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 list1, ListNode list2) {
      if(list1==null){
        return list2;
      }else if(list2==null){
        return list1;
      }else if(list1.val<list2.val){
        list1.next = mergeTwoLists(list1.next,list2);
        return list1;
      }else{
        list2.next = mergeTwoLists(list2.next,list1);
        return list2;
      }
        
    }
}

7. 两数相加-@画手大鹏

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

技巧:当题目需要返回头结点的时候,可以创建一个虚拟结点指向头结点。下一题也是

8. 删除链表的倒数第N个结点-@画手大鹏

思路:1、fast先向前移动n个节点。2、slow和fast同步移动直到fast在最后一个。3、删除slow的下一个节点。

  • 删除后返回 pre.next,为什么不直接返回 head 呢,因为 head 有可能是被删掉的点
    /**
     * 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 removeNthFromEnd(ListNode head, int n) {
            ListNode pre = new ListNode(0);
            pre.next = head;
            ListNode fast = pre, slow = pre;
            while(n>0){ //完成while后此时slow和fast相差n
              fast = fast.next;
              n--;
            }
            //fast和slow一起同步移动直到fast是最后一个
            while(fast.next != null){
              fast = fast.next;
              slow = slow.next;
            }
            //此时slow的下一个节点就是要删除的节点
            slow.next = slow.next.next;
            return pre.next;
        }
    }

    9. 两两交换链表中的节点-@画手大鹏

  • 递归思想,创建节点newHead = head.next,然后先移动head.next = swapPairs(newHead)。再移动newHead.next = head。顺序不能错,因为第一步要依赖newHead.next.

    /**
     * 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 swapPairs(ListNode head) {
          if(head == null || head.next == null){
            return head;
          }
          ListNode newHead = head.next;
          head.next = swapPairs(newHead.next);
          newHead.next = head;
          return newHead;
            
        }
    }

    10. 随机链表的复制-@Krahets方法一:哈希表

  • 这里的random没用的,就是看成多了一个指针而已。

  • 哈希表key是原节点,value是对应的复制节点。

  • 第一次遍历复制cur.val,第二次遍历定义next指针和random指针。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution {
    public Node copyRandomList(Node head) {
      //特判
      if(head == null) return null;
      Node cur = head;
      //key是原节点,value是复制节点
      Map<Node,Node> map = new HashMap<>();
      //两次遍历,第一次构造节点值,第二次构造指针指向
      while(cur!=null){
        map.put(cur,new Node(cur.val));
        cur = cur.next;
      }
      cur = head;//第一次遍历完之后重新指向头结点
      while(cur != null){
        map.get(cur).next = map.get(cur.next);
        map.get(cur).random = map.get(cur.random);
        cur = cur.next;
      }
      //返回新链表的头结点
      return map.get(head);
        
    }
}

11. LRU缓存-@官方题解

二、二叉树

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 List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res  = new ArrayList<>();
        inorder(root,res);
        return res; 
    }
    public void inorder(TreeNode root,List<Integer> res){
      if(root == null){
        return;
      }
      inorder(root.left,res);
      res.add(root.val);
      inorder(root.right,res);
    }
}

2. 二叉树的最大深度-@官方题解法一:DFS递归

如果我们知道了左子树和右子树的最大深度 l 和 r,那么该二叉树的最大深度即为

max(l,r)+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 int maxDepth(TreeNode root) {
      if(root == null){
        return 0;
      }else{
        int leftHeight = maxDepth(root.left);
        int rightHeight = maxDepth(root.right);
        return Math.max(leftHeight,rightHeight)+1;
      }

        
    }
}

3. 翻转二叉树-@官方题解-递归

对比递归终止条件。

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;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/invert-binary-tree/solutions/415160/fan-zhuan-er-cha-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4. 对称二叉树-@Krahets-DFS递归

重点是recur函数:

1、递归终止的true(左右子树均null)和false条件(左右其中一个为空 || 左右节点值不相同)

2、再判断recur(左的左,右的右)|| (左的右,右的左)

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return root == null || recur(root.left, root.right);
    }
    boolean recur(TreeNode L, TreeNode R) {
        if (L == null && R == null) return true;
        if (L == null || R == null || L.val != R.val) return false;
        return recur(L.left, R.right) && recur(L.right, R.left);
    }
}

作者:Krahets
链接:https://leetcode.cn/problems/symmetric-tree/solutions/2361627/101-dui-cheng-er-cha-shu-fen-zhi-qing-xi-8oba/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5. 二叉树的直径-官方题解评论区-@捷少

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。

两节点之间路径的 长度 由它们之间边数表示。

class Solution {
    int maxd=0;
    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);
        return maxd;
    }
    public int depth(TreeNode node){
        if(node==null){
            return 0;
        }
        int Left = depth(node.left);
        int Right = depth(node.right);
//将每个节点最大直径(左子树深度+右子树深度)当前最大值比较并取大者
        maxd=Math.max(Left+Right,maxd);
        return Math.max(Left,Right)+1;//返回节点深度
    }
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐