目录

1. 什么是二叉搜索树

1.1 二叉搜索树结构

1.2 二叉搜索树特性应用

2. 二叉搜索树基础实现

2.1 BST类型与构造函数

2.2 插入操作

2.2.1 思路分析

2.2.2 递归实现

2.2.3 非递归实现

2.3 查找操作

2.3.1 递归实现

2.3.2 非递归实现

3. 验证二叉搜索树

3.1 递归法

3.1.1 自顶向下实现

3.2 迭代法

3.3 中序遍历法

3.4 拓展:二叉树遍历的非递归实现

3.4.1 方法总论

3.4.2 中序遍历

3.4.3 先序遍历

3.4.4 后续遍历

4. 集合(Set)

4.1 集合简介

4.2 集合的实现

4.2.1 Java中的实现

4.2.2 基于BST的实现

5. 检索(Index)

5.1 检索简介

5.2 检索的实现

5.2.1 Java中的实现

5.2.2 使用哈希表实现检索

5.2.3 基于BST的实现


1. 什么是二叉搜索树

1.1 二叉搜索树结构

满足以下条件的二叉树,称为二叉搜索树(Binary Search Tree,BST)

1. 对于每个节点,他的左子树的所有节点都比他小

2. 对于每个节点,他的右子树的所有节点都比他大

说明1:以上是严格的说法,不允许BST中有重复节点,但是实际算法中可以允许重复

说明2:二叉搜索树也是递归的数据结构,他的左右子树也是二叉搜索树

说明3:上述二叉搜索树的特性换一种说法,

① 对于每个节点,左子树中的最大值小于该节点

② 对于每个节点,右子树中的最小值大于该节点

1.2 二叉搜索树特性应用

1. 对BST进行中序遍历,得到的是一个非降序的绪列

在数学上可以证明,如果一颗二叉树的中序遍历是非降序的,那么这颗二叉树一定是二叉搜索树

利用该性质,可以验证一颗二叉树是否是二叉搜索树

2. BST有插入操作

普通的二叉树没有插入操作的概念,而BST提供了插入数据的规则

3. 高效的插入与查找

BST插入和查找操作的时间复杂度均为O(h),其中h为BST的高度

① 在最好情况下,树的高度为log2n,也就是时间复杂度为O(logn)

② 在最坏情况下,树的高度为n,也就是时间复杂度为O(n)

2. 二叉搜索树基础实现

2.1 BST类型与构造函数

// BST节点类型class TreeNode {public int val;public TreeNode left;public TreeNode right;public TreeNode(int value) {val = value;}}// BST类型class BST {private TreeNode root;public BST() {// 该构造函数其实可以省略root = null;}}

2.2 插入操作

2.2.1 思路分析

1. BST插入操作要确保插入后仍然是BST,因此插入操作的核心是查找正确的插入位置

2. BST的插入操作有很多实现方式,我们选择不修改已有节点的方式,此时插入的位置一定是叶子节点的下方

更正:其实插入的位置不一定是叶子节点的下方,也可能是没有左子树或右子树的节点下方,比如在示例BST中插入值为15的节点

说明:本题为LeetCode第701题

https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/

2.2.2 递归实现

// root:要插入的BST root节点,进入每层子树时,就是这个子树的root// val:要插入的数据值// 返回值:插入节点后的BST root节点// 每层子树返回的就是插入节点后的子树rootprivate TreeNode insertRecursiveHelper(TreeNode root, int val) {if (root == null) {return new TreeNode(val);}// 其实只有被插入节点的左右子树引用会被修改,其余节点不会被修改if (val < root.val) {root.left = insertRecursiveHelper(root.left, val);} else {root.right = insertRecursiveHelper(root.right, val);}return root;}public void insertRecursive(int val) {root = insertRecursiveHelper(root, val);}

算法说明:

1. 递归的过程要进行到null节点,且到达的null节点就是要插入节点的正确位置

2. 当递归到null节点时,说明已经达到正确位置,此时返回新增节点的引用给上层,此时这就是新增的一层子树的root

2.2.3 非递归实现

// root:要插入的BST root节点// val:要插入的数据值// 返回值:插入节点后的BST root节点private TreeNode insertLoopHelper(TreeNode root, int val) {// 如果当前为空树,则直接返回新增节点引用if (root == null) {return new TreeNode(val);}TreeNode cur = root;while (cur != null) {if (val < cur.val) {if (cur.left != null) {cur = cur.left;} else {cur.left = new TreeNode(val);break;}} else {if (cur.right != null) {cur = cur.right;} else {cur.right = new TreeNode(val);break;}}}return root;}public void insertLoop(int val) {root = insertLoopHelper(root, val);}

2.3 查找操作

2.3.1 递归实现

private boolean containRecursiveHelper(TreeNode root, int val) {// 递归出口1// 递归到null节点,说明没有目标数据if (root == null) {return false;}// 递归出口2// 找到目标,同样终止递归if (root.val == val) {return true;}if (val < root.val) {return containRecursiveHelper(root.left, val);} else {return containRecursiveHelper(root.right, val);}}public boolean containRecursive(int val) {return containRecursiveHelper(root, val);}

2.3.2 非递归实现

public boolean containLoopHelper(TreeNode root, int val) {TreeNode cur = root;while (cur != null) {if (cur.val == val) {break;} else if (val < cur.val) {cur = cur.left;} else {cur = cur.right;}}if (cur != null) {return true;} else {return false;}}public boolean containLoop(int val) {return containLoopHelper(root, val);}

3. 验证二叉搜索树

说明:LeetCode第98题

https://leetcode-cn.com/problems/validate-binary-search-tree/

3.1 递归法

3.1.1 自顶向下实现

算法思路:带着上界和下界遍历BST,如果当前层满足上下界要求,则修改上界或下界递归地判断其左子树和右子树;如果当前层不满足上下界要求,则直接返回false

// lower & upper参数为当前层节点root要满足的界限private static boolean isValidBst2Helper(TreeNode root, long lower, long upper) {// 递归出口1// 递归到达null节点,返回trueif (root == null) {return true;}// 递归出口2// 如果当前层不满足界限要求,向上层返回false// 增加等号判断,是因为我们假设BST中没有重复节点if (root.val <= lower || root.val >= upper) {return false;}// 当前层节点val是其左子树的上界boolean leftResult = isValidBst2Helper(root.left, lower, root.val);// 当前层节点val是其右子树的下界boolean rightResult = isValidBst2Helper(root.right, root.val, upper);// 这里其实也可以加速,当左子树判断失败时,就可以返回false// 不需要再向右子树递归了// 如果左右子树中有一层不满足界限要求,则返回falseif (!leftResult || !rightResult) {return false;} else {return true;}}public static boolean isValidBst2(TreeNode root) {// 初始界限要覆盖TreeNode.val的值域return isValidBst2Helper(root, Long.MIN_VALUE, Long.MAX_VALUE);}

说明1:由于TreeNode.val为int类型,在初始调用时设置的上下界必须覆盖int类型的值域,所以使用了Long类型的最大 & 最小值

更进一步的做法,可以把这里的上下界数值改为指向上下界节点的引用,这样在初始调用时,上下界引用均设置为null

说明2:此处节点的判断顺序类似先序遍历

说明3:向每个节点设置的上下界如下图所示,图中也标识了节点访问顺序

说明4:最后的返回式可以改为如下形式,以提升效率,尽早结束递归

// 这里利用了&&运算符的短路特性,只要左子树判断为false,则直接返回falsereturn isValidBst2Helper(root.left, lower, root.val) &&isValidBst2Helper(root.right, root.val, upper);

3.1.2 自底向上实现

说明:该方法为九章基础班课程中的解法

算法思路:该方法先判断子树是否为BST,同时返回子树的最大值和最小值,然后在本层判断root.val是否在(左子树最大值,右子树最小值)区间中

tips:个人觉得该解法没有LeetCode中自顶向下的解法好

// 结果类型class Result {public boolean isBst; // 子树是否为BSTpublic long minValue; // 子树的最大值public long maxValue; // 子树的最大值public Result(boolean isBst, long minValue, long maxValue) {this.isBst = isBst;this.minValue = minValue;this.maxValue = maxValue;}}private static Result isValidBst1Helper(TreeNode root) {if (root == null) {// 已经递归到null节点,向上层节点返回结果return new Result(true, Long.MAX_VALUE, Long.MIN_VALUE);}Result leftResult = isValidBst1Helper(root.left);Result rightResult = isValidBst1Helper(root.right);// 当前层的左右子树已经不是BSTif (!leftResult.isBst || !rightResult.isBst) {// 因为当前层的左子树或右子树已经不是BST// 返回的子树最小 & 最大值已无意义return new Result(false, -1, -1);}// 当前层不满足BST要求// 增加等号同样是因为假设BST中没有重复节点if (root.val <= leftResult.maxValue || root.val >= rightResult.minValue) {return new Result(false, -1 ,-1);}// 当前层满足BST要求,向上层返回true,及本层的最小值 & 最大值return new Result(true, Math.min(root.val, leftResult.minValue),Math.max(root.val, rightResult.maxValue));}public static boolean isValidBst1(TreeNode root) {Result result = isValidBst1Helper(root);return result.isBst;}

说明1:root == null时的返回值是本解法的技巧所在

递归到null节点时,向上层返回的子树最大值为-∞,子树最小值为+∞,这样做的目的是为了确保在其上一层的判断总能满足BST要求

因为判断的标准是root.val大于左子树的最大值(-∞)且小于右子树的最小值(+无穷)

为了配合此处的返回值,当本层满足BST要求时,返回的本层最小值 & 最大值要和root.val比较,否则会继续向上层返回±∞

说明2:此处节点的判断顺序类似后序遍历

3.2 迭代法

算法思路:在自顶向下的递归法实现中,是通过函数参数向下一层子树传递上下界信息,此处可以使用队列保存上下界信息进行比较

这个算法其实有点儿广度优先层序遍历的意思

// 保存节点private static LinkedList<TreeNode> elem = new LinkedList<TreeNode>();// 保存对应节点需要满足的下界private static LinkedList<Integer> lowers = new LinkedList<Integer>();// 保存对应节点需要满足的上界private static LinkedList<Integer> uppers = new LinkedList<Integer>();private static void enqueue(TreeNode node, Integer lower, Integer upper) {elem.add(node);lowers.add(lower);uppers.add(upper);}public static boolean isValidBst3(TreeNode root) {TreeNode node = null;Integer lower = null;Integer upper = null;Integer val = null;enqueue(root, lower, upper);// elem为空,说明所有节点已经比较完毕,且没有出错while (!elem.isEmpty()) {node = elem.poll();lower = lowers.poll();upper = uppers.poll();if (node == null) {continue;}val = node.val;if (lower != null && val <= lower) {return false;}if (upper != null && val >= upper) {return false;}enqueue(node.left, lower, val);enqueue(node.right, val, upper);}return true;}

说明1:为什么是队列操作

LeetCode视频题解中,说创建栈来存储节点和上下界信息,但是LinkedList的add操作是在表尾加入元素;poll操作是取出表头元素,这明显是队列操作

而且参考二叉树的层序遍历,这里也应该是队列结构

说明2:该算法的可改进之处,就是在子树为null时不再enqueue

if (node.left != null) {enqueue(node.left, lower, val);}if (node.right != null) {enqueue(node.right, val, upper);}

3.3 中序遍历法

算法思路:对二叉树进行中序遍历,如果得到的绪列是升序的,就说明这是一颗二叉搜索树。一种简单的方法是将中序遍历的结果写入ArrayList,之后遍历ArrayList判断是否是升序

private static ArrayList<Integer> list = new ArrayList<Integer>();// 中序遍历,并将遍历结果写入ArrayListprivate static void inorder(TreeNode root) {if (root == null) {return;}inorder(root.left);// 访问root节点list.add(root.val);inorder(root.right);}public static boolean isValidBst4(TreeNode root) {inorder(root);// 判断ArrayList中的数据是否为升序int i = 0;for (i = 0; i < list.size() - 1; ++i) {if (list.get(i) >= list.get(i + 1)) {break;}}if (i < list.size() - 1) {return false;} else {return true;}}

这里的可改进之处是可以不保留整个中序遍历数组,可以在中序遍历过程中实时检查当前节点的值是否大于前一个中序遍历节点的值,这样就可以将空间复杂度降低到O(h),也就是中序遍历函数栈的空间复杂度

private static Integer preVal = null;private static boolean isBst = true;public static void inorderCheck(TreeNode root) {if (root == null) {return;}// 递归调用只是实现问题的分解// 实际的节点访问发生在中序遍历处inorderCheck(root.left);// 访问root节点,在此处判断与上一节点的关系if (preVal != null && root.val <= preVal) {isBst = false;// 因为本层的检查已经失败// 所以这里可以调用return返回,不再递归}preVal = root.val;inorderCheck(root.right);}public static boolean isValidBst6(TreeNode root) {inorderCheck(root);return isBst;}

进一步的改进,还可以使用栈来模拟递归中序遍历时的函数栈情况,在改进之前我们可以分析一下中序遍历时函数栈的使用情况

此处我们补全BST中的null节点,同时标出中序遍历的访问顺序。之所以标记出null节点,是因为在递归调用中,其实会递归到null节点,并作为递归出口

根据上面的函数栈状态变化,很容易使用自定义的栈将上述的递归调用改为非递归实现,这里的核心是,在中序遍历中的访问时机是左子树递归调用返回时

该算法步骤要点总结如下,

1. 向左子树递归到null节点,此时说明左子树递归调用即将返回,到达访问时机

2. 此时出栈的节点,就是该访问的节点

3. 访问转向右子树

public static boolean isValidBst5(TreeNode root) {Stack<TreeNode> stack = new Stack<TreeNode>();Integer inorder = null;// 如果栈为空且root为null,只有2种可能// 1. 初始调用的root为空树// 2. BST的所有节点均已遍历完毕且均符合条件while (!stack.isEmpty() || root != null) {// 向左子树递归,直到null节点while (root != null) {stack.push(root);root = root.left;}// 中序访问节点// 与递归调用相比,相当于已被访问的节点在访问时就出栈root = stack.pop();if (inorder != null && root.val <= inorder) {return false;}// 记录当前中序访问节点inorder = root.val;// 向右子树递归root = root.right;}return true;}

说明:可见该算法对栈的使用,与递归调用的函数栈使用并不相同,只是实现了相同的访问效果

也就是说,这里是找到了中序访问的规律

3.4 拓展:二叉树遍历的非递归实现

说明:按实现难度组织

3.4.1 方法总论

1. 每个节点都是一个根节点

2. 需要借助根节点转向访问右子树

3. 节点访问之后才能出栈

3.4.2 中序遍历

1. 递归实现

public static void inorderRecursive(TreeNode root) {if (root == null) {return;}inorderRecursive(root.left);System.out.print(root.val + " ");inorderRecursive(root.right);}

2. 非递归实现

public static void inorderLoop(TreeNode root) {Stack<TreeNode> stack = new Stack<TreeNode>();while (!stack.isEmpty() || root != null) {while (root != null) {stack.push(root);root = root.left;}// 出循环时,root为null// 这表示已经遍历到了null节点(虚拟存在的节点)root = stack.pop();System.out.print(root.val + " ");// 转向右子树root = root.right;}}

3.4.3 先序遍历

1. 递归实现

public static void preorderRecursive(TreeNode root) {if (root == null) {return;}System.out.print(root.val + " ");preorderRecursive(root.left);preorderRecursive(root.right);}

2. 非递归实现

public static void preorderLoop(TreeNode root) {Stack<TreeNode> stack = new Stack<TreeNode>();while (!stack.isEmpty() || root != null) {while (root != null) {System.out.print(root.val + " ");stack.push(root);root = root.left;}root = stack.pop();root = root.right;}}

3.4.4 后续遍历

1. 递归实现

public static void postorderRecursive(TreeNode root) {if (root == null) {return;}postorderRecursive(root.left);postorderRecursive(root.right);System.out.print(root.val + " ");}

2. 非递归实现

后续遍历非递归实现的难点,在于只有从右子树返回时,才能访问节点,并将该节点出栈

public static void postorderLoop(TreeNode root) {Stack<TreeNode> stack = new Stack<TreeNode>();TreeNode lastVisit = null;while (!stack.isEmpty() || root != null) {while (root != null) {stack.push(root);root = root.left;}root = stack.pop();// 访问时机:// 1. 从左子树返回,且节点没有右子树// 2. 从右子树返回if (root.right == null || lastVisit == root.right) {System.out.print(root.val + " ");lastVisit = root;// 将root设置为null非常重要// 因为此时已经访问过该节点,// 也就是该节点的左右子树均已访问完成// 此时不应该再从该节点向下层递归// 将root设置为null后,可以触发节点的出栈root = null;} else {// 从左子树返回,且有右子树// 将根节点重新入栈,并转向右子树stack.push(root);root = root.right;}}}

参考资料:

二叉树前序、中序、后序遍历非递归写法的透彻解析

二叉树前序、中序、后序遍历非递归写法的透彻解析

还可以参考LeetCode题解

中序遍历:

https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/er-cha-shu-de-zhong-xu-bian-li-by-leetcode-solutio/

先序遍历:

https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/er-cha-shu-de-qian-xu-bian-li-by-leetcode-solution/

后续遍历:

https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/er-cha-shu-de-hou-xu-bian-li-by-leetcode-solution/

4. 集合(Set

4.1 集合简介

1. 集合存储没有重复元素的数据

2. Set有2个方法,

① add(element)

增加一个元素

② contains(element)

判断集合内是否包含这个元素

4.2 集合的实现

4.2.1 Java中的实现

Java中有一个Set接口,即Interface Set<E>,基于该接口,实现了2种Set类型,

1. TreeSet

基于Tree实现的Set,一般使用红黑树

2. HashSet

基于哈希表(Hash Table)实现的Set

4.2.2 基于BST的实现

使用本节介绍的BST也可以实现Set,其中,

1. add(element)

对应BST的Insert操作

2. contains(element)

对应BST的Search操作

class BstSet {private BST bst;public BstSet() {bst = new BST();}public void add(int val) {bst.insertRecursive(val);}public boolean contains(int val) {boolean contain = bst.containRecursive(val);return contain;}}

5. 检索(Index

5.1 检索简介

1. 检索存储的是键值对(key-value pairs)

2. 检索也称作映射(map)或字典(dictionary)

3. 检索有3个方法,

① put(key, value)

设置key对应的value

② get(key)

读取key对应的value

③ containsKey(key)

判断对应的key是否在检索中

说明:集合可以理解为value到value的映射,而检索是key到value的映射

5.2 检索的实现

5.2.1 Java中的实现

Java中有一个Map接口,即Interface Map<K, V>,基于该接口,实现了2种Map类型,

1. TreeMap

基于Tree实现的Map,使用红黑树

2. HashMap & HashTable

基于哈希表(Hash Table)实现的Map

说明1:根据JDK 11文档,在不要求线程安全的场景中,建议使用HashMap替换HashTable;在要求线程安全的高并发场景中,建议使用ConcurrentHashMap

说明2:HashMap使用示例

在LeetCode题解中经常会使用到HashMap类型,下面给出简单示例

import java.util.HashMap;import java.util.Map;Map<Integer, Character> hashmap = new HashMap<Integer, Character>();hashmap.put(10, 'a');hashmap.put(20, 'b');System.out.println(hashmap.get(10));System.out.println(hashmap.get(30)); // return nullSystem.out.println(hashmap.containsKey(20));System.out.println(hashmap.containsKey(30));

5.2.2 使用哈希表实现检索

5.2.2.1 开散列法

开散列法(open hashing)也称为拉链法(separate chaining),实现方法如下,

1. 开辟一个数组

2. 数组中的每个元素是一个链表的头节点引用

3. 初始状态数组每个元素对应的链表为空链表

4. put(key, value)时,创建一个ListNode(包含key & value),之后根据hash函数计算key对应的index下标,并将这个ListNode插入index下标对应的链表中

下图中,假设hash函数为h(key) = key % 5

如果发生哈希冲突,则将数据按序插入链表

5. get(key)时,根据hash函数计算key对应的index下标,然后遍历该下标对应的链表

5.2.2.2 闭散列法

闭散列法(closed hashing)也称为开放地址法(open addressing),实现方法如下,

1. 开辟一个数组,每个位置只存放一个Node(包含key & value)

2. put(key, value)时,根据hash函数计算key对应的index下标,并将Node存入该位置

如果发生哈希冲突,则顺序往后查找,直到找到一个空位置可以存入Node

3. get(key)时,根据hash函数计算key对应的index下标,然后从该位置开始往后遍历查找

5.2.3 基于BST的实现

使用BST实现检索时,需要对TreeNode进行扩展。原来只存储val一个值,现在要存储key-val键值对,之后根据key形成BST,这样就可以根据key设置 & 查找val

说明:此处的实现不涉及key值相同的处理

class TreeNode {public int key;public int val;public TreeNode left;public TreeNode right;public TreeNode(int key, int val) {this.key = key;this.val = val;left = right = null;}}class BST {private TreeNode root;public BST() {root = null;}private TreeNode insertRecursiveHelper(TreeNode root, int key, int val) {if (root == null) {return new TreeNode(key, val);}if (key < root.key) {root.left = insertRecursiveHelper(root.left, key, val);} else {root.right = insertRecursiveHelper(root.right, key, val);}return root;}public void insertRecursive(int key, int val) {root = insertRecursiveHelper(root, key, val);}private TreeNode containRecursiveHelper(TreeNode root, int key) {if (root == null) {return null;}if (root.key == key) {return root;}if (key < root.key) {return containRecursiveHelper(root.left, key);} else {return containRecursiveHelper(root.right, key);}}public TreeNode containRecursive(int key) {return containRecursiveHelper(root, key);}}class BSTreeMap {private BST bst;public BSTreeMap() {bst = new BST();}public void put(int key, int val) {bst.insertRecursive(key, val);}public boolean contains(int key) {TreeNode contain = bst.containRecursive(key);if (contain == null) {return false;} else {return true;}}public Integer get(int key) {TreeNode contain = bst.containRecursive(key);if (contain == null) {return null;} else {return contain.val;}}}

九章基础算法04:二叉搜索树与哈希表相关推荐

  1. 《数据结构与算法之二叉搜索树(Java实现)》

    说在前头:本人为大二在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,能力有限,文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正.若 ...

  2. 看动画学算法之:二叉搜索树BST

    文章目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 看动画学算法之:二叉搜索树BST 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的 ...

  3. 算法篇 - 二叉搜索树

    前言 在前端的工作当中,二叉搜索树不怎么常见,虽然没有快排.冒泡.去重.二分.希尔等算法常见,但是它的作用,在某些特定的场景下,是非常重要的. 目前es6的使用场景比较多,所以我准备能用es6的地方就 ...

  4. 九章基础算法03:树和递归

    目录 1. 树的概念 1.1 树的结构 1.2 二叉树结构 2. 树的遍历 2.1 概述 2.2 先序遍历(preorder traversal) 2.3 中序遍历(inorder traversal ...

  5. 【每日一算法】二叉搜索树结点最小距离

    微信改版,加星标不迷路! 每日一算法-二叉搜索树节点最小距离 作者:阿广 阅读目录 1 题目 2 解析 1 题目 给定一个二叉搜索树的根结点 root, 返回树中任意两节点的差的最小值. 示例: 输入 ...

  6. 数据结构与算法之二叉搜索树

    与链表不同,树是一种非线性的数据结构.树中最常用的是二叉树,二叉树限制了子树的数量,也就是每个结点的子树至多2个,并且这两个子树是有顺序的.而二叉搜索树(二叉查找树,二叉排序树)是指根节点的关键字大于 ...

  7. leetcode算法题--二叉搜索树迭代器

    题目链接:https://leetcode-cn.com/problems/binary-search-tree-iterator/ 1.排序 将树元素保存到数组中,再对其排序. class BSTI ...

  8. 【数据结构与算法】二叉搜索树V2.0的Java实现

    更新说明 在二叉搜索树V1.0的编程实现中,我们实现了BST的查找.插入,左右儿子删除的功能,但写的确实很一般,这里就Update一下. 功能介绍 void insert(x) → Insert x ...

  9. 数据结构与算法-平衡二叉搜索树

    平衡二叉搜索树 1.自平衡的二叉搜索树 2.平衡 (1)空树平衡 (2)非空树平衡 左右子树平衡 左右子树高度差绝对值 <= 1 3.平衡因子 左右子树的高度差的衡量值 -1 0 1 (一)平衡 ...

最新文章

  1. GCC编译选项--创建与使用库
  2. 网络工程师计算机类吗,机房网络工程师 | 网络工程专业的你知道吗?
  3. pyinstaller 安装使用方法
  4. python decode unicode encode
  5. LuoGu P2002 消息扩散
  6. React开发(139):react中onref
  7. c语言 有趣的代码,分享一段有趣的小代码
  8. 容器编排技术 -- Kubernetes入门概述
  9. 数字信号处理实验(三):离散时间傅里叶变换
  10. 利用Postman测试智慧交通系统接口
  11. centos中多台主机免密登录_centos免密码使用密钥登录
  12. go-mysql数据-查询--输入数据--实战2
  13. 再也不学AJAX了!(三)跨域获取资源 ② - JSONP CORS
  14. 一、瞰景Smart3D软件介绍
  15. mac 10.8 montion lion 山狮系统上 安装 rails 和 oci8 连 oracle 数据库
  16. 操作系统——进程调度
  17. java将邮件保存到本地文件夹_JavaMail 邮件文件夹管理
  18. ZYNQ7010 CAN的官方例程改为XCANPS_MODE_NORMAL模式,程序没跑通
  19. android下存储设备的使用
  20. 20170628总结

热门文章

  1. python 旋转矩阵_【每日算法Day 93】不用额外空间,你会旋转一个矩阵吗?
  2. dbeaver默认值怎么设置_电脑没声音怎么办,峰哥教你如何解决
  3. 贝叶斯数据分析_科研进阶项目 | 剑桥大学 | 心理学、社会学、生物医学:统计数据分析(6.13开课)...
  4. java的获取声音振幅_录音获取声音振幅波形显示
  5. mysql ( )=,(mysql)
  6. 中的枚举属性函数_Java 枚举:有效应用
  7. Java中正则表达式替换字符串
  8. mysql 显示各列的数据类型命令_mysql中查看库中某个表的所有列和对应的字段类型...
  9. python 随机数抽奖系统_python实现的简单抽奖系统实例
  10. mysql ---- 约束