LeetCode日记——【数据结构】树专题(遍历,BST,Tire)
预备知识:BFS算法(广度优先搜索)题1-题2
题1:一棵树每层节点的平均数(Average of Levels in Binary Tree)
LeetCode题号:637
难度:Easy
链接:https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/
题目描述:
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public List<Double> averageOfLevels(TreeNode root) { 12 List <Double> ret = new ArrayList<>(); 13 if(root==null) return ret; 14 Queue <TreeNode> queue = new LinkedList<>(); 15 queue.add(root); 16 while (!queue.isEmpty()) { 17 //当前队列中的元素个数 18 int cnt = queue.size(); 19 //当前队列元素之和 20 double sum = 0; 21 //遍历当前层的所有节点 22 for (int i = 0; i < cnt; i++) { 23 TreeNode node = queue.poll(); 24 sum += node.val; 25 //把当前节点的孩子节点加入队列 26 if (node.left != null) queue.add(node.left); 27 if (node.right != null) queue.add(node.right); 28 } 29 ret.add(sum / cnt); 30 } 31 return ret; 32 } 33 }
分析:
使用 BFS (广度优先搜索)进行层次遍历。通过一个队列来存放一层的元素。i用于指向当前层的所有节点。每遍历到当前层的节点,就把这个节点弹出,并把值加到sum中,然后把它的所有孩子节点加到队列里。每一组for循环计算一层数据。然后重新计算队列长度(下一层的节点个数),再进入下一组for循环。
题2:得到左下角的节点(Find Bottom Left Tree Value)
LeetCode题号:513
难度:Easy
链接:https://leetcode-cn.com/problems/find-bottom-left-tree-value/description/
题目描述:
给定一个二叉树,在树的最后一行找到最左边的值。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int findBottomLeftValue(TreeNode root) { 12 Queue <TreeNode> queue = new LinkedList<>(); 13 queue.add(root); 14 while(!queue.isEmpty()){ 15 root=queue.poll(); 16 if(root.right!=null) queue.add(root.right); 17 if(root.left!=null) queue.add(root.left); 18 } 19 return root.val; 20 } 21 }
分析:
使用 BFS (广度优先搜索)进行层次遍历。创建一个队列来存放TreeNode。首先放入root节点。如果该队列不为空,则将最先放进去的节点设置为root并弹出,并且在队列中加入它的两个孩子节点。
先放右孩子节点,再放左孩子节点,这样就可以保证左边的节点永远在最后遍历,最后得到的就是最下面一层最左边的节点。
预备知识:DFS算法(深度优先搜索)题3-题5
前序遍历:根左右
1.首先输出该树的根节点。
2.永远先查看当前节点有无左子节点,如有,就输出其左子节点。
3.如果当前节点无左子节点,查看其有无右子节点,如有,就输出其右子节点。
4.如果当前节点为叶子节点,则返回查看其父节点,然后重新从步骤2开始检查。输出过的元素将不再输出。
一句话概括:先输出根,然后有左输出左,没有左就输出右,都没有返回父节点。
中序遍历:左根右
1.从根节点开始
2.若当前节点存在左子节点?有,就将当前节点转向其左子节点,重新判断2。无?转3。
3.若当前节点无左子节点或左边的节点已经全部输出,就输出自己。然后判断有无右子节点?有,然后将当前节点转向其右子节点,然后重新判断2。无,转4.
4.如果当前节点为叶子节点或左右节点均已输出,就将当前节点返回其父节点,然后重新从2开始。
一句话概括:左边所有子孙节点都输出了,就输出自己,然后看右子节点
后序遍历:左右根
1.从根节点开始
2.有无左子节点(左边节点都已经输出过了也算没有)?有,当前节点转向左子节点。无,转3
3.无左子节点,有右子节点吗(右边节点都已经输出过了也算没有)?有,当前节点转向右子节点。无,转4
4.左右都无,输出自己。当前节点返回其父节点,重新从2开始。
一句话概括:有左边的就往左边跑,左边没有就看右边,两边都没有了就输出自己,返回父节点。
题3:二叉树的前序遍历(Binary Tree Preorder Traversal)
LeetCode题号:144
难度:Medium
链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/description/
题目描述:
给定一个二叉树,返回它的 前序 遍历。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public List<Integer> preorderTraversal(TreeNode root) { 12 List <Integer> list = new ArrayList<>(); 13 Stack <TreeNode> stack = new Stack<>(); 14 stack.push(root); 15 while(!stack.isEmpty()){ 16 TreeNode node = stack.pop(); 17 if(node==null) continue; 18 list.add(node.val); 19 stack.push(node.right); 20 stack.push(node.left); 21 } 22 return list; 23 } 24 }
分析:
首先将根节点push入栈。
在循环体中,我们首先将栈顶元素pop出来存起来作为当前节点,将其值输出。然后先push它的右节点,再push它的左节点入栈,再进行下一轮循环。这样就保证先输出它的左节点。
题4:二叉树的后序遍历(Binary Tree Preorder Traversal)
LeetCode题号:144
难度:Hard
链接:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/description/
题目描述:
给定一个二叉树,返回它的 后序 遍历。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public List<Integer> postorderTraversal(TreeNode root) { 12 List<Integer> list = new ArrayList<>(); 13 Stack<TreeNode> stack = new Stack<>(); 14 stack.add(root); 15 while(!stack.isEmpty()){ 16 TreeNode node = stack.pop(); 17 if (node == null) continue; 18 list.add(node.val); 19 stack.push(node.left); 20 stack.push(node.right); 21 } 22 Collections.reverse(list); 23 return list; 24 } 25 }
分析:
前序遍历为:根左右,将其代码中左右push位置交换,变为根右左。然后最后倒序一下,即为左右根,就完成了后序遍历。
题5:二叉树的中序遍历(Binary Tree Inorder Traversal )
LeetCode题号:94
难度:Medium
链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/description/
题目描述:
给定一个二叉树,返回它的 中序 遍历。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 //左根右 11 class Solution { 12 public List<Integer> inorderTraversal(TreeNode root) { 13 List<Integer> list = new ArrayList<>(); 14 if (root == null) return list; 15 Stack <TreeNode> stack = new Stack<>(); 16 TreeNode curr = root; 17 while(curr!=null||!stack.isEmpty()){ 18 while(curr!=null){ 19 stack.push(curr); 20 curr=curr.left; 21 } 22 TreeNode node = stack.pop(); 23 list.add(node.val); 24 curr = node.right; 25 } 26 return list; 27 } 28 }
分析:
定义一个当前节点curr。
如果当前节点不为null,就将其push入栈,然后将curr移动到他的左子节点。再重新判断是否为null。(先左)
当前节点已经为null,则将栈顶元素push出来(null的父节点)输出,(再根)然后转向null的父节点的右子节点。(最后右)
预备知识:BST(二叉查找树) 题6-题15
BST是满足如下3个条件的二叉树:
1. 节点的左子树包含的节点的值小于该节点的值
2. 节点的右子树包含的节点的值大于等于该节点的值
3. 节点的左子树和右子树都是BST
BST的初始构建可以利用插入操作完成。BST最常使用的操作是查找和遍历,还有删除操作但相对较少使用。
一个重要的常识:用中序遍历来遍历二叉查找树,得到的数字是升序的(做题十分有用!)
题6:修剪二叉搜索树(Trim a Binary Search Tree)
LeetCode题号:699
难度:Easy
链接:https://leetcode-cn.com/problems/trim-a-binary-search-tree/description/
题目描述:
给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public TreeNode trimBST(TreeNode root, int L, int R) { 12 if(root==null) return null; 13 //若当前节点的值已超出最大边界,则其右边的所有节点都超出最大边界,抛弃全部右边节点 14 if (root.val > R) return trimBST(root.left, L, R); 15 //若当前节点的值已小于最小边界,则其左边边的所有节点都小于最小边界,抛弃全部左边节点 16 if (root.val < L) return trimBST(root.right, L, R); 17 //分别对左右子节点为根的子树进行修剪 18 root.left = trimBST(root.left, L, R); 19 root.right = trimBST(root.right, L, R); 20 return root; 21 } 22 }
分析:
当root的值位于L
和R
之间,则递归修剪其左右子树,返回root。
当root的值小于L
,则其左子树的值都小于L
,抛弃左子树,返回修剪过的右子树。
当root的值大于R
,则其右子树的值都大于R
,抛弃右子树,返回修剪过的左子树。
题7:寻找二叉查找树的第 k 个元素(Kth Smallest Element in a BST)
LeetCode题号:230
难度:Medium
链接:https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst/description/
题目描述:
给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明:你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
输出: 1
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode() {} 8 * TreeNode(int val) { this.val = val; } 9 * TreeNode(int val, TreeNode left, TreeNode right) { 10 * this.val = val; 11 * this.left = left; 12 * this.right = right; 13 * } 14 * } 15 */ 16 class Solution { 17 public int kthSmallest(TreeNode root, int k) { 18 int leftcount = count(root.left); 19 if(leftcount==k-1) return root.val; 20 if(leftcount>k-1) return kthSmallest(root.left,k); 21 return kthSmallest(root.right,k - leftcount - 1); 22 } 23 private int count(TreeNode root){ 24 if(root==null) return 0; 25 return 1+count(root.left)+count(root.right); 26 } 27 }
分析:
先写一个count()方法用来计算已当前节点为root的树的节点数。
在主方法中,我们定义一个leftcount变量来计算该树左子树的节点数。然后分3种情况讨论:
1.因为左子树所有节点的值都小于root节点,所以如果leftcount==k-1,说明root就是第k小的那个节点。
2.如果leftcount>k-1,说明第k小的节点在左子树中,递归返回左子树的第k小的节点。
3.如果leftcount<k-1,说明第k小的节点在右子树中,递归返回右子树的第k-leftcount-1小的节点。
题8:把二叉查找树每个节点的值都加上比它大的节点的值(Convert BST to Greater Tree)
LeetCode题号:538
难度:Easy
链接:https://leetcode-cn.com/problems/convert-bst-to-greater-tree/description/
题目描述:
给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
例如:
输入: 原始二叉搜索树:
5
/ \
2 13
输出: 转换为累加树:
18
/ \
20 13
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 private int sum = 0; 12 public TreeNode convertBST(TreeNode root) { 13 if(root==null) return null; 14 //把当前节点的右子树变为累加树 15 convertBST(root.right); 16 //更新root值 17 sum+= root.val; 18 root.val=sum; 19 //把当前节点的左子树变为累加树 20 convertBST(root.left); 21 return root; 22 } 23 }
分析:
首先我们判断当前访问的节点是否存在,如果存在就递归右子树,递归回来的时候更新总和和当前点的值,然后递归左子树。如果我们分别正确地递归 root.right 和 root.left ,那么我们就能正确地用大于某个节点的值去更新此节点,然后才遍历比它小的值。
以上为官方分析,其实这道题我不会。
题9:二叉查找树的最近公共祖先(Lowest Common Ancestor of a Binary Search Tree)
LeetCode题号:235
难度:Easy
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/
题目描述:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 12 //说明两个节点都在root节点的左子树中 13 if (root.val > p.val && root.val > q.val) { 14 //到左子树中去递归寻找 15 return lowestCommonAncestor(root.left, p, q); 16 } 17 //说明两个节点都在root节点的右子树中 18 if (root.val < p.val && root.val < q.val) { 19 //到右子树中去递归寻找 20 return lowestCommonAncestor(root.right, p, q); 21 } 22 //两个节点一左一右,那么最近公共祖先就是只能是深度最小的root 23 return root; 24 } 25 }
分析:
分3种情况讨论:
1.如果这两个节点的值均小于根节点的值,说明两个节点都在根节点的左边,那么我们将root.left作为当前root,递归寻找最深的祖先
2.如果这两个节点的值均大于根节点的值,说明两个节点都在根节点的右边,那么我们将root.right作为当前root,递归寻找最深的祖先
3.不符合上面两种情况,说明两个节点一左一右,那么返回root即可。
题10:二叉树的最近公共祖先(Lowest Common Ancestor of a Binary Tree)
LeetCode题号:236
难度:Medium
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/description/
题目描述:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 12 if(root==null||root==p||root==q) return root; 13 TreeNode left = lowestCommonAncestor(root.left,p,q); 14 TreeNode right = lowestCommonAncestor(root.right,p,q); 15 if(left==null && right==null) return null; 16 if(left == null) return right; 17 if(right == null) return left; 18 return root; 19 } 20 }
分析:
终止条件:当越过叶节点,或当 root等于 p或q,则直接返回 root;
递归体:
开启递归左子节点,返回值记为 left;
开启递归右子节点,返回值记为 right;
根据 left 和 right ,可展开为四种情况;
1.当 left和 right同时为空 :说明 root的左 / 右子树中都不包含 p,q,返回 null;
2.当 left和 right同时不为空 :说明 p, q分列在 root 的 异侧 ,因此 root 为最近公共祖先,返回 root ;
3.当 left为空 ,right不为空 :p,q 都不在 rootroot 的左子树中,直接返回 right。具体可分为两种情况:
p,q 其中一个在 root的 右子树 中,此时 right指向 p(假设为 p );
p,q 两节点都在 root的 右子树 中,此时的 right指向 最近公共祖先节点 ;
4.当 left不为空 ,right为空 :与情况3同理;
题11:从有序数组中构造二叉查找树(Convert Sorted Array to Binary Search Tree)
LeetCode题号:108
难度:Easy
链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/description/
题目描述:
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public TreeNode sortedArrayToBST(int[] nums) { 12 return ToBST(nums,0,nums.length-1); 13 } 14 private TreeNode ToBST(int[] nums,int left,int right){ 15 if(left>right) return null; 16 int mid = (left+right)/2; 17 TreeNode root = new TreeNode(nums[mid]); 18 root.left=ToBST(nums,left,mid-1); 19 root.right=ToBST(nums,mid+1,right); 20 return root; 21 } 22 }
分析:
将两个指针分别放在有序数组的开头与末尾。将中间索引的数作为根节点,然后左节点为左边一半的中心,右节点为右边一半的中心,以此类推。
题12:根据有序链表构造平衡的二叉查找树(Convert Sorted List to Binary Search Tree)
LeetCode题号:109
难度:Medium
链接:https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree/description/
题目描述:
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
代码:
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 /** 10 * Definition for a binary tree node. 11 * public class TreeNode { 12 * int val; 13 * TreeNode left; 14 * TreeNode right; 15 * TreeNode(int x) { val = x; } 16 * } 17 */ 18 class Solution { 19 public TreeNode sortedListToBST(ListNode head) { 20 if(head==null) return null; 21 if(head.next==null) return new TreeNode(head.val); 22 ListNode premid = preMid(head); 23 ListNode mid = premid.next; 24 premid.next=null; 25 TreeNode root = new TreeNode(mid.val); 26 //对当前前一半的链表进行递归 27 root.left = sortedListToBST(head); 28 //对当前后一半的链表进行递归 29 root.right=sortedListToBST(mid.next); 30 return root; 31 } 32 private ListNode preMid(ListNode head){ 33 ListNode pre = head; 34 ListNode slow = head; 35 ListNode fast = head.next; 36 while(fast!=null && fast.next!=null){ 37 pre=slow; 38 slow=slow.next; 39 fast=fast.next.next; 40 } 41 return pre; 42 } 43 }
分析:
先写一个preMid()方法,用了三个指针。fast指针每次走2步,slow指针每次走一步。pre指针用来指示slow指针的上一步。当fast指针走到链表末尾(偶数节点)或末尾的前一个节点(奇数节点)时,返回pre节点,相当于返回了链表中部的前一半部分的倒数第2个节点。
然后我们来写主方法。先写边界情况。然后我们以pre节点为前一半链表的尾部,mid为后一半链表的头部将原链表断开。然后我们以mid节点的值创建一个root树节点。这样就完成了一次操作。然后,我们创建root.left,并对当前的前一半链表进行递归操作。创建root.right,并对当前的后一半链表进行递归操作。最后,返回树的根节点root。
题13:在二叉查找树中寻找两个节点,使它们的和为一个给定值(Two Sum IV - Input is a BST)
LeetCode题号:653
难度:Easy
链接:https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst/description/
题目描述:
给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
案例 1:
输入:
5
/ \
3 6
/ \ \
2 4 7
Target = 9
输出: True
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public boolean findTarget(TreeNode root, int k) { 12 List <Integer> nums = new ArrayList<>(); 13 inOrder(root,nums); 14 int left = 0; 15 int right = nums.size()-1; 16 while(left<right){ 17 int sum = nums.get(left)+nums.get(right); 18 if(sum<k) left++; 19 if(sum>k) right--; 20 if(sum==k) return true; 21 } 22 return false; 23 } 24 //中序遍历 25 private void inOrder(TreeNode root, List<Integer> nums){ 26 if(root==null) return; 27 inOrder(root.left,nums); 28 nums.add(root.val); 29 inOrder(root.right,nums); 30 } 31 }
分析:
先用中序遍历把树上每个节点的数字存放到一个List中。
然后对这个List用双指针遍历来判断是否存在两数之和等于k(之前做过)。
题14:在二叉查找树中查找两个节点之差的最小绝对值(Minimum Absolute Difference in BST)
LeetCode题号:530
难度:Easy
链接:https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/description/
题目描述:
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 private TreeNode preNode = null; 12 private int minDiff = Integer.MAX_VALUE; 13 public int getMinimumDifference(TreeNode root) { 14 inOrder(root); 15 return minDiff; 16 } 17 private void inOrder(TreeNode node){ 18 if(node==null) return; 19 inOrder(node.left); 20 //不断更新最小绝对值minDiff 21 if (preNode != null) minDiff = Math.min(minDiff, node.val - preNode.val); 22 preNode = node; 23 inOrder(node.right); 24 } 25 }
分析:
在中序遍历的过程中,在遍历右节点之前,保存好上次的当前节点。在遍历了左节点后,将当前节点与上一个当前节点作差,并更新最小差值minDiff。
注意:初始化minDiff时,要将其初值设置为一个非常大的数,MAX.VALUE。否则,万一最小差值比设定的初值还要大,那么midDiff就恒为初值了。
题15:寻找二叉查找树中出现次数最多的值(Find Mode in Binary Search Tree)
LeetCode题号:501
难度:Easy
链接:https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/description/
题目描述:
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 //当前计数 12 private int currCnt = 1; 13 //最大计数 14 private int maxCnt = 1; 15 private TreeNode preNode = null; 16 public int[] findMode(TreeNode root) { 17 List<Integer> maxCntNums = new ArrayList<>(); 18 inOrder(root,maxCntNums); 19 //把maxCntNums中的数放到list中去(题目返回格式要求) 20 int[] list = new int[maxCntNums.size()]; 21 int idx=0; 22 for(int num:maxCntNums){ 23 list[idx++]= num; 24 } 25 return list; 26 } 27 //中序遍历,并将当前出现次数最多的数存于nums 28 private void inOrder(TreeNode node,List<Integer> nums){ 29 if(node==null) return; 30 inOrder(node.left,nums); 31 //若当前值与上一个值相同,则该数计数currCnt加1 32 if(preNode!=null){ 33 if(preNode.val==node.val) currCnt++; 34 else currCnt=1; 35 } 36 //maxCnt存放当前出现的最多的数字的出现次数 37 if(currCnt>maxCnt){ 38 maxCnt=currCnt; 39 nums.clear(); 40 nums.add(node.val); 41 }else if(currCnt==maxCnt){ 42 nums.add(node.val); 43 } 44 preNode = node; 45 inOrder(node.right,nums); 46 } 47 }
分析:
总体思路是:定义一个maxCnt变量,存放频率最高的数字出现的次数。currCnt表示遍历时当前数字的计数值。在中序遍历时,由currCnt来不断更新maxCnt,并将对应的数字存入ArrayList。完成遍历时,我们将会得到频率最高的数字与他出现的次数maxCnt。在主方法中,我们把ArrayList转化为普通数组并返回。
把ArrayList转化为普通数组:不要再用(int i=0;i<n;i++)了,直接用foreach循环,更加简便。索引自增的语句也应该放在赋值语句中,这样更加简洁。
预备知识:字典树(Tire)
又称单词查找树,Trie树,是一种树形结构。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
题16:实现一个 Trie(Implement Trie (Prefix Tree))
LeetCode题号:208
难度:Medium
链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree/description/
题目描述:
实现一个 Trie (前缀树),包含 insert
, search
, 和 startsWith
这三个操作。
代码:
1 class Trie { 2 //定义节点 3 private class Node{ 4 //每一个节点都拥有一个容量为26,名为childs的数组,数据类型为Node 5 Node[] childs = new Node[26]; 6 //每个Node都有isLeaf属性,用来标记其是否是叶子节点 7 boolean isLeaf; 8 } 9 private Node root = new Node(); 10 11 /** Initialize your data structure here. */ 12 public Trie() {} 13 14 /** Inserts a word into the trie. */ 15 public void insert(String word) { 16 insert(word,root); 17 } 18 private void insert(String word,Node node) { 19 if(node==null) return; 20 //字符串为空,则当前节点为叶子节点 21 if(word.length()==0){ 22 node.isLeaf=true; 23 return; 24 } 25 int index = indexForChar(word.charAt(0)); 26 //给当前节点附带的数组里面创建新Node 27 if(node.childs[index]==null){ 28 node.childs[index] = new Node(); 29 } 30 //舍弃word的第一个字符,递归操作 31 insert(word.substring(1), node.childs[index]); 32 } 33 34 /** Returns if the word is in the trie. */ 35 public boolean search(String word) { 36 return search(word,root); 37 } 38 private boolean search(String word,Node node) { 39 if(node==null) return false; 40 if(word.length()==0) return node.isLeaf; 41 int index = indexForChar(word.charAt(0)); 42 return search(word.substring(1),node.childs[index]); 43 } 44 45 /** Returns if there is any word in the trie that starts with the given prefix. */ 46 public boolean startsWith(String prefix) { 47 return startsWith(prefix,root); 48 } 49 private boolean startsWith(String prefix,Node node) { 50 if (node == null) return false; 51 if (prefix.length() == 0) return true; 52 int index = indexForChar(prefix.charAt(0)); 53 return startsWith(prefix.substring(1), node.childs[index]); 54 } 55 private int indexForChar(char c) { 56 return c - 'a'; 57 } 58 } 59 60 /** 61 * Your Trie object will be instantiated and called as such: 62 * Trie obj = new Trie(); 63 * obj.insert(word); 64 * boolean param_2 = obj.search(word); 65 * boolean param_3 = obj.startsWith(prefix); 66 */
分析:
我没看懂,谁来救救我!
题17:键值映射(Map Sum Pairs)
LeetCode题号:677
难度:Medium
链接:https://leetcode-cn.com/problems/map-sum-pairs/description/
题目描述:
实现一个 MapSum 类里的两个方法,insert 和 sum。
对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。
对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。
代码:
1 class MapSum { 2 private class Node { 3 Node[] child = new Node[26]; 4 int value; 5 } 6 private Node root = new Node(); 7 public MapSum() {} 8 public void insert(String key, int val) { 9 insert(key, root, val); 10 } 11 12 private void insert(String key, Node node, int val) { 13 if (node == null) return; 14 if (key.length() == 0) { 15 node.value = val; 16 return; 17 } 18 int index = indexForChar(key.charAt(0)); 19 if (node.child[index] == null) { 20 node.child[index] = new Node(); 21 } 22 insert(key.substring(1), node.child[index], val); 23 } 24 25 public int sum(String prefix) { 26 return sum(prefix, root); 27 } 28 29 private int sum(String prefix, Node node) { 30 if (node == null) return 0; 31 if (prefix.length() != 0) { 32 int index = indexForChar(prefix.charAt(0)); 33 return sum(prefix.substring(1), node.child[index]); 34 } 35 int sum = node.value; 36 for (Node child : node.child) { 37 sum += sum(prefix, child); 38 } 39 return sum; 40 } 41 42 private int indexForChar(char c) { 43 return c - 'a'; 44 } 45 } 46 47 /** 48 * Your MapSum object will be instantiated and called as such: 49 * MapSum obj = new MapSum(); 50 * obj.insert(key,val); 51 * int param_2 = obj.sum(prefix); 52 */
分析:
我没看懂,谁来救救我!
完结撒花~