Page 41 of 61

LeetCode刷题【876】链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

 

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

 

提示:

  • 给定链表的结点数介于 1 和 100 之间。
Related Topics
  • 链表
  • 双指针

  • 👍 388
  • 👎 0
  • 题解

    直观第一反应就是双指针,一个直接往末尾跑,另一个每当往末尾跑的指针跑了两下的时候,往后跑一下。不过也可以最简单的,从头跑到尾知道有多长了之后再取中间那个,取中间那个可以再从头跑一遍,也可以把跑过的记录存起来记录是第几个,然后直接取对应的中间那个。

    先双指针:

    class Solution {
    
        int curIndex = 1;
    
        public ListNode middleNode(ListNode head) {
            ListNode mid = head;
            ListNode cur = head;
            while (cur.next != null){
                cur = cur.next;
                curIndex = curIndex==2?1:curIndex+1;
                if (curIndex==2){
                    mid = mid.next;
                }
            }
            return mid;
        }
    }

    也可以不用curIndex变量记录。直接在while循环里尝试访问cur.next.next

    class Solution {
    
        public ListNode middleNode(ListNode head) {
            ListNode mid = head;
            ListNode cur = head;
            while (cur.next != null){
                cur = cur.next;
                if (cur.next !=null){
                    cur =cur.next;
                }
                mid = mid.next;
            }
            return mid;
        }
    }

    再写个List集合的

    class Solution {
    
        public ListNode middleNode(ListNode head) {
            if (head==null)return null;
            List<ListNode> list = new ArrayList<>();
            list.add(head);
            while (head.next != null){
                head = head.next;
                list.add(head);
            }
            return list.get(list.size()/2);
        }
    }

    跑了一下看看好像List集合的比双指针的占用内存还小点?还是服务器问题?

    LeetCode刷题【1022】从根到叶的二进制数之和

    给出一棵二叉树,其上每个结点的值都是 0 或 1 。每一条从根到叶的路径都代表一个从最高有效位开始的二进制数。例如,如果路径为 0 -> 1 -> 1 -> 0 -> 1,那么它表示二进制数 01101,也就是 13 。

    对树上的每一片叶子,我们都要找出从根到该叶子的路径所表示的数字。

    返回这些数字之和。题目数据保证答案是一个 32 位 整数。

     

    示例 1:

    LeetCode刷题【1022】从根到叶的二进制数之和
    每个结点的值都是 0 或 1
    输入:root = [1,0,1,0,1,0,1]
    输出:22
    解释:(100) + (101) + (110) + (111) = 4 + 5 + 6 + 7 = 22
    

    示例 2:

    输入:root = [0]
    输出:0
    

    示例 3:

    输入:root = [1]
    输出:1
    

    示例 4:

    输入:root = [1,1]
    输出:3
    

     

    提示:

    • 树中的结点数介于 11000 之间。
    • Node.val01
    Related Topics
  • 深度优先搜索
  • 二叉树

  • 👍 121
  • 👎 0
  • 题解

    二叉树、二进制。好说,顺便复习下上次的LeetCode刷题【190】 颠倒二进制位,具体的二进制相关操作符和逻辑:

    与&:0&0=0 0&1=0 1&0=0 1&1=1
    或|:0|0=0 0|1=1 1|0=1 1|1=1
    异或^:0^0=0 0^1=1 1^0=1 1^1=0
    取反~:~1=0 ~0=1
    左移<<:左边的二进制位丢弃,右边补0
    右移>>:正数左补0,负数左补1,右边丢弃
    无符号左移<<<:左边的二进制位丢弃,右边补0
    无符号右移>>>:忽略符号位,空位都以0补齐

    深搜往下,每下一层,左移(<<)一位,此时末位为0,然后把当前节点的值放在末位,也就是或(|)或者是异或(^)操作都可以。

    然后就是判断什么是叶子节点了,这个也简单,只要是没有子节点了的都是叶子节点,也就是左右子节点都为空。遇到叶子节点的时候,把这个时候得到的值加到结果上即可。

    class Solution {
        int res = 0;
        public int sumRootToLeaf(TreeNode root) {
            dfs(0,root);
            return res;
        }
    
        public void dfs(int num, TreeNode root){
            if(root == null){
                return;
            }
            num = num << 1 | root.val;
            if (root.left == null && root.right == null){
                res += num;
            }
            dfs(num, root.left);
            dfs(num, root.right);
        }
    }

    左移一位也可以改为直接乘以2

    LeetCode刷题【881】救生艇

    第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit

    每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit

    返回载到每一个人所需的最小船数。(保证每个人都能被船载)。

     

    示例 1:

    输入:people = [1,2], limit = 3
    输出:1
    解释:1 艘船载 (1, 2)
    

    示例 2:

    输入:people = [3,2,2,1], limit = 3
    输出:3
    解释:3 艘船分别载 (1, 2), (2) 和 (3)
    

    示例 3:

    输入:people = [3,5,3,4], limit = 5
    输出:4
    解释:4 艘船分别载 (3), (3), (4), (5)

    提示:

    • 1 <= people.length <= 50000
    • 1 <= people[i] <= limit <= 30000
    Related Topics
  • 贪心
  • 数组
  • 双指针
  • 排序

  • 👍 160
  • 👎 0
  • 题解

    8月26每日一题

    class Solution {
        public int numRescueBoats(int[] people, int limit) {
            Arrays.sort(people);
            //limit 必然大于 people的最大值,不然就有人走不了
            //所以从最重的人开始上船。每次尝试看看能否带上当前还没上船的最轻的人,如果不能则自己走,
            // 等下一个稍微轻一点的人再来尝试带走这个轻一点的人
            int  count = 0;
            int left = 0;
            int right = people.length-1;
            while (left < right){
                if (people[right]+people[left] <= limit){
                    left++;
                }
                right--;
                count++;
            }
            //最后只剩下一个人了
            if (left==right){
                count++;
            }
            return count;
        }
    }

    直接用了Arrays.sort(people),本题应该重点还是是双指针,排序这块省去

    LeetCode刷题【1110】删点成林

    给出二叉树的根节点 root,树上每个节点都有一个不同的值。

    如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。

    返回森林中的每棵树。你可以按任意顺序组织答案。

     

    示例:

    输入:root = [1,2,3,4,5,6,7], to_delete = [3,5]
    输出:[[1,2,null,4],[6],[7]]
    

     

    提示:

    • 树中的节点数最大为 1000
    • 每个节点都有一个介于 1 到 1000 之间的值,且各不相同。
    • to_delete.length <= 1000
    • to_delete 包含一些从 1 到 1000、各不相同的值。
    Related Topics
  • 深度优先搜索
  • 二叉树

  • 👍 134
  • 👎 0
  • 题解

    二叉树搜索删除对应节点,看到题目第一反应应该是:因为提供的删除集合是个数组集合,所以先把to_delete转换成hashset,转换成hash查找提供的删除节点

    然后就是遍历各个节点找到要删除的节点。删除的节点需要把左右子树放入返回集合中,但是需要判断左右子树的根节点是不是需要删除的节点,如果是则不放入结果结合。

    另外还有一个要点,遍历最后需要把删除的节点删除掉

    class Solution {
    
        Set<Integer> deleteSet = new HashSet<>();
        List<TreeNode> list = new ArrayList<>();
        public List<TreeNode> delNodes(TreeNode root, int[] toDelete) {
            for (int i : toDelete) {
                deleteSet.add(i);
            }
            if (!ifDelete(root.val )){
                list.add(root);
            }
            collectDfs(root);
            return list;
        }
    
    
        public void collectDfs(TreeNode root){
            if (root == null){
                return;
            }
            TreeNode left = root.left;
            TreeNode right = root.right;
            if (ifDelete(root.val)){
                root = null;
                if (left !=null && !ifDelete(left.val)){
                    list.add(left);
                }
                if (right !=null && !ifDelete(right.val)){
                    list.add(right);
                }
            }
            collectDfs(left);
            collectDfs(right);
            if (root==null)return;
            if (root.left != null && ifDelete(root.left.val)){
                root.left = null;
            }
            if (root.right != null && ifDelete(root.right.val)){
                root.right = null;
            }
        }
    
    
        public boolean ifDelete(int val){
            return deleteSet.contains(val);
        }
    }

    其实本来一开始并不是这么写的,本来写了两次dfs,第一次会把删除的节点的值标记成-1,第二次dfs的时候根据节点值为-1来判断,但是实际跑出来效果速度不行。还是就整合起来一次dfs搞定

    LeetCode刷题【965】单值二叉树

    如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。

    只有给定的树是单值二叉树时,才返回 true;否则返回 false

     

    示例 1:

    输入:[1,1,1,1,1,null,1]
    输出:true
    

    示例 2:

    输入:[2,2,2,5,2]
    输出:false
    

     

    提示:

    1. 给定树的节点数范围是 [1, 100]
    2. 每个节点的值都是整数,范围为 [0, 99] 。
    Related Topics
  • 深度优先搜索
  • 广度优先搜索
  • 二叉树

  • 👍 86
  • 👎 0
  • 题解

    今天是继续练习二叉树相关的题目,深化学习掌握

    本题是直观的二叉树遍历要求的题目,可以深搜也可以广搜,深搜每递进一层都需要开启一层额外的栈空间,最终和树的高度是相关的,广搜需要新建额外的队列保存下一层需要遍历的结果集。个人还是偏向深搜来解题一点。

    多加个notSame值判断当前判断的情况,可以提前中断,如果有不同了,不用遍历完全树,后续其他节点也都不用再遍历了。当然如果是广搜的要实现提前中断就很容易了。直接终止while循环退出即可。

    class Solution {
        boolean notSame = false;
        public boolean isUnivalTree(TreeNode root) {
            return isSameTree(root);
        }
    
        public boolean isSameTree(TreeNode root){
            if (notSame){
                return false;
            }
            if (root==null){
                return true;
            }
            if (root.left != null && root.left.val != root.val){
                notSame = true;
                return false;
            }
            if (root.right != null && root.right.val != root.val){
                notSame = true;
                return false;
            }
            if (isSameTree(root.left) && isSameTree(root.right)){
                return true;
            }
            notSame = true;
            return false;
        }
    
    }

    LeetCode刷题【797】所有可能的路径

    给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序

    二维数组的第 i 个数组中的单元都表示有向图中 i 号节点所能到达的下一些节点,空就是没有下一个结点了。

    译者注:有向图是有方向的,即规定了 a→b 你就不能从 b→a 。

     

    示例 1:

    输入:graph = [[1,2],[3],[3],[]]
    输出:[[0,1,3],[0,2,3]]
    解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
    

    示例 2:

    输入:graph = [[4,3,1],[3,2,4],[3],[4],[]]
    输出:[[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]
    

    示例 3:

    输入:graph = [[1],[]]
    输出:[[0,1]]
    

    示例 4:

    输入:graph = [[1,2,3],[2],[3],[]]
    输出:[[0,1,2,3],[0,2,3],[0,3]]
    

    示例 5:

    输入:graph = [[1,3],[2],[3],[]]
    输出:[[0,1,2,3],[0,3]]
    

     

    提示:

    • n == graph.length
    • 2 <= n <= 15
    • 0 <= graph[i][j] < n
    • graph[i][j] != i(即,不存在自环)
    • graph[i] 中的所有元素 互不相同
    • 保证输入为 有向无环图(DAG)
    Related Topics
  • 深度优先搜索
  • 广度优先搜索
  • 回溯

  • 👍 161
  • 👎 0
  • 题解

    8月25日每日一题

    有向,无环这两个输入参数的特性使得这题的难度直线下降,不用考虑访问到已访问过的节点,不要考虑进入环的可能性

    直接就根据入参重新构建一个反向的关系图,如,

    原本入参告诉了从0节点可以前往4,3,1节点,而新的图上0的位置记录的是可以到达0节点的上一个节点有哪些,0节点是初始出发节点,显然没有,不如换个终点4节点。

    可知,能到达4节点的上一个节点可能为【0,1,3】,0节点为初始节点,可以结束,1节点的上一个节点可能为【0】结束,3节点的上一个节点可能为【0,2】,2节点的上一个节点可能为【1】,1节点的上一个节点可能为【0】结束。

    class Solution {
    
        boolean[][] pathMap;
        List<List<Integer>> list;
        public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
            int target = graph.length-1;
            list = new ArrayList<>();
            createMap(graph);
            List<Integer> ls = new ArrayList<>();
            ls.add(target);
            dfs(ls,target);
            return list;
        }
    
        public void dfs(List<Integer> visitList, int node){
            if (node == 0){
                list.add(new ArrayList<>(visitList));
                return;
            }
            if (pathMap[node].length > 0){
                for (int i = 0; i < pathMap[node].length; i++) {
                    if (!pathMap[node][i])continue;
                    visitList.add(0,i);
                    dfs(visitList,i);
                    visitList.remove(0);
                }
            }
        }
    
    
        public void createMap(int[][] graph){
            pathMap = new boolean[graph.length][graph.length];
            for (int i = 0; i < graph.length; i++) {
                for (int j = 0; j < graph[i].length; j++) {
                    pathMap[graph[i][j]][i] = true;
                }
            }
        }
    }

    后续

    从上面的例子可以看到第一开始从4节点找到1节点,1节点的上一个节点可能为【0】结束,而2节点后来又找到了1节点,这边可以优化下,额外记录下能到达0节点的所有点的可能路径,假设一个完整的能到达0路径是【8,6,5,1,0】

    那么需要记录下 8经过【6,5,1,0】,6经过【5,1,0】,5经过【1,0】能到达0节点。

    这样在后面再次絮叨6、5、1这些节点的时候无需再次尝试计算能否到达0节点

    补充,大意了,直接从0节点开始搜索也是可以的,之所以想要反向搜索是想要从基础数据的层面来规避最后进入死胡同的情况,比如这样的情况

    【【1,2】,【】,【】】最终结果应当是到达2节点,但是0节点也可以到达1节点,1节点最后却不能达到2节点,构建反向的路径的时候可以直接规避掉从0节点开始搜索进入1节点的情况

    LeetCode刷题【700】二叉搜索树中的搜索

    给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

    例如,

    给定二叉搜索树:
    
            4
           / \
          2   7
         / \
        1   3
    
    和值: 2
    

    你应该返回如下子树:

          2     
         / \   
        1   3
    

    在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL

    Related Topics
  • 二叉搜索树
  • 二叉树

  • 👍 146
  • 👎 0
  • 题解

    直白简单,搜索树特性。左小又大,递归先写个,

    
    //leetcode submit region begin(Prohibit modification and deletion)
    /**
     * 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 TreeNode searchBST(TreeNode root, int val) {
            if (root==null)return null;
            if (root.val == val)return root;
            if (root.val > val){
                return searchBST(root.left,val);
            }
            return searchBST(root.right,val);
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    结果

    解答成功:
    执行耗时:0 ms,击败了100.00% 的Java用户
    内存消耗:39.6 MB,击败了5.26% 的Java用户

    内存消耗应该是来自于递归栈空间消耗。再试着不递归写个吧

    class Solution {
        public TreeNode searchBST(TreeNode root, int val) {
            while (!(null==root || root.val==val)){
                root = root.val > val ? root.left : root.right;
            }
            return root;
        }
    }
    解答成功:
    执行耗时:0 ms,击败了100.00% 的Java用户
    内存消耗:39.2 MB,击败了62.94% 的Java用户

    勉强还行吧….