treeNodes) {
+ for (TreeNode it : treeNodes) {
+ if (treeNode.getId().equals(it.getParentId())) {
+ if (treeNode.getChildren() == null) {
+ treeNode.setChildren(new ArrayList<>());
+ }
+ treeNode.getChildren().add(findChildren(it, treeNodes));
+ }
+ }
+
+ return treeNode;
+ }
+}
+```
+
+
+
+### 回溯模板
+
+
+
+### DFS模板
+
+
+
+### BFS模板
+
+
+
+## 查找算法
+
+### 顺序查找
+
+就是一个一个依次查找。
+
+
+
+### 二分查找
+
+二分查找又叫折半查找,从有序列表的初始候选区`li[0:n]`开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。如果待查值小于候选区中间值,则只需比较中间值左边的元素,减半查找范围。依次类推依次减半。
+
+- 二分查找的前提:**列表有序**
+- 二分查找的有点:**查找速度快**
+- 二分查找的时间复杂度为:**O(logn)**
+
+![二分查找](images/Algorithm/二分查找.gif)
+
+JAVA代码如下:
+
+```java
+/**
+ * 执行递归二分查找,返回第一次出现该值的位置
+ *
+ * @param array 已排序的数组
+ * @param start 开始位置,如:0
+ * @param end 结束位置,如:array.length-1
+ * @param findValue 需要找的值
+ * @return 值在数组中的位置,从0开始。找不到返回-1
+ */
+public static int searchRecursive(int[] array, int start, int end, int findValue) {
+ // 如果数组为空,直接返回-1,即查找失败
+ if (array == null) {
+ return -1;
+ }
+
+ if (start <= end) {
+ // 中间位置
+ int middle = (start + end) / 1;
+ // 中值
+ int middleValue = array[middle];
+ if (findValue == middleValue) {
+ // 等于中值直接返回
+ return middle;
+ } else if (findValue < middleValue) {
+ // 小于中值时在中值前面找
+ return searchRecursive(array, start, middle - 1, findValue);
+ } else {
+ // 大于中值在中值后面找
+ return searchRecursive(array, middle + 1, end, findValue);
+ }
+ } else {
+ // 返回-1,即查找失败
+ return -1;
+ }
+}
+
+/**
+ * 循环二分查找,返回第一次出现该值的位置
+ *
+ * @param array 已排序的数组
+ * @param findValue 需要找的值
+ * @return 值在数组中的位置,从0开始。找不到返回-1
+ */
+public static int searchLoop(int[] array, int findValue) {
+ // 如果数组为空,直接返回-1,即查找失败
+ if (array == null) {
+ return -1;
+ }
+
+ // 起始位置
+ int start = 0;
+ // 结束位置
+ int end = array.length - 1;
+ while (start <= end) {
+ // 中间位置
+ int middle = (start + end) / 2;
+ // 中值
+ int middleValue = array[middle];
+ if (findValue == middleValue) {
+ // 等于中值直接返回
+ return middle;
+ } else if (findValue < middleValue) {
+ // 小于中值时在中值前面找
+ end = middle - 1;
+ } else {
+ // 大于中值在中值后面找
+ start = middle + 1;
+ }
+ }
+
+ // 返回-1,即查找失败
+ return -1;
+}
+```
+
+
+
+### 插值查找
+
+插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。将折半查找中的求mid索引的公式,low表示左边索引left,high表示右边索引right,key就是前面我们讲的findVal。
+
+![二分查找mid](images/Algorithm/二分查找mid.png) **改为** ![插值查找mid](images/Algorithm/插值查找mid.png)
+
+**注意事项**
+
+- 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快
+- 关键字分布不均匀的情况下,该方法不一定比折半查找要好
+
+```java
+/**
+ * 插值查找
+ *
+ * @param arr 已排序的数组
+ * @param left 开始位置,如:0
+ * @param right 结束位置,如:array.length-1
+ * @param findValue
+ * @return
+ */
+public static int insertValueSearch(int[] arr, int left, int right, int findValue) {
+ //注意:findVal < arr[0] 和 findVal > arr[arr.length - 1] 必须需要, 否则我们得到的 mid 可能越界
+ if (left > right || findValue < arr[0] || findValue > arr[arr.length - 1]) {
+ return -1;
+ }
+
+ // 求出mid, 自适应
+ int mid = left + (right - left) * (findValue - arr[left]) / (arr[right] - arr[left]);
+ int midValue = arr[mid];
+ if (findValue > midValue) {
+ // 向右递归
+ return insertValueSearch(arr, mid + 1, right, findValue);
+ } else if (findValue < midValue) {
+ // 向左递归
+ return insertValueSearch(arr, left, mid - 1, findValue);
+ } else {
+ return mid;
+ }
+}
+```
+
+
+
+### 斐波那契查找
+
+黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618。由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。这是一个神奇的数字,会带来意向不大的效果。斐波那契数列{1, 1,2, 3, 5, 8, 13,21, 34, 55 }发现斐波那契数列的两个相邻数的比例,无限接近黄金分割值0.618。
+斐波那契查找原理与前两种相似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1(F代表斐波那契数列),如下图所示:
+
+![斐波那契查找](images/Algorithm/斐波那契查找.png)
+
+JAVA代码如下:
+
+```java
+/**
+ * 因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列
+ *
+ * 非递归方法得到一个斐波那契数列
+ *
+ * @return
+ */
+private static int[] getFibonacci() {
+ int[] fibonacci = new int[20];
+ fibonacci[0] = 1;
+ fibonacci[1] = 1;
+ for (int i = 2; i < fibonacci.length; i++) {
+ fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2];
+ }
+ return fibonacci;
+}
+
+/**
+ * 编写斐波那契查找算法
+ *
+ * 使用非递归的方式编写算法
+ *
+ * @param arr 数组
+ * @param findValue 我们需要查找的关键码(值)
+ * @return 返回对应的下标,如果没有-1
+ */
+public static int fibonacciSearch(int[] arr, int findValue) {
+ int low = 0;
+ int high = arr.length - 1;
+ int k = 0;// 表示斐波那契分割数值的下标
+ int mid = 0;// 存放mid值
+ int[] fibonacci = getFibonacci();// 获取到斐波那契数列
+ // 获取到斐波那契分割数值的下标
+ while (high > fibonacci[k] - 1) {
+ k++;
+ }
+
+ // 因为 fibonacci[k] 值可能大于 arr 的 长度,因此我们需要使用Arrays类,构造一个新的数组
+ int[] temp = Arrays.copyOf(arr, fibonacci[k]);
+ // 实际上需求使用arr数组最后的数填充 temp
+ for (int i = high + 1; i < temp.length; i++) {
+ temp[i] = arr[high];
+ }
+
+ // 使用while来循环处理,找到我们的数 findValue
+ while (low <= high) {
+ mid = low + fibonacci[k] - 1;
+ if (findValue < temp[mid]) {
+ high = mid - 1;
+ k--;
+ } else if (findValue > temp[mid]) {
+ low = mid + 1;
+ k++;
+ } else {
+ return Math.min(mid, high);
+ }
+ }
+
+ return -1;
+}
+```
+
+
+
+## 搜素算法
+
+### 深度优先搜索(DFS)
+
+深度优先搜索(Depth-First Search / DFS)是一种**优先遍历子节点**而不是回溯的算法。
+
+![深度优先搜索](images/Algorithm/深度优先搜索.jpg)
+
+**DFS解决的是连通性的问题**。即给定两个点,一个是起始点,一个是终点,判断是不是有一条路径能从起点连接到终点。起点和终点,也可以指的是某种起始状态和最终的状态。问题的要求并不在乎路径是长还是短,只在乎有还是没有。
+
+
+
+**代码案例**
+
+```java
+/**
+ * Depth-First Search(DFS)
+ *
+ * 从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止。然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点为止。
+ *
+ * 数据结构:栈
+ * 父节点入栈,父节点出栈,先右子节点入栈,后左子节点入栈。递归遍历全部节点即可
+ *
+ * @author lry
+ */
+public class DepthFirstSearch {
+
+ /**
+ * 树节点
+ *
+ * @param
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class TreeNode {
+ private V value;
+ private List> childList;
+
+ // 二叉树节点支持如下
+
+ public TreeNode getLeft() {
+ if (childList == null || childList.isEmpty()) {
+ return null;
+ }
+
+ return childList.get(0);
+
+ }
+
+ public TreeNode getRight() {
+ if (childList == null || childList.isEmpty()) {
+ return null;
+ }
+
+ return childList.get(1);
+ }
+ }
+
+ /**
+ * 模型:
+ * .......A
+ * ...../ \
+ * ....B C
+ * .../ \ / \
+ * ..D E F G
+ * ./ \ / \
+ * H I J K
+ */
+ public static void main(String[] args) {
+ TreeNode treeNodeA = new TreeNode<>("A", new ArrayList<>());
+ TreeNode treeNodeB = new TreeNode<>("B", new ArrayList<>());
+ TreeNode treeNodeC = new TreeNode<>("C", new ArrayList<>());
+ TreeNode treeNodeD = new TreeNode<>("D", new ArrayList<>());
+ TreeNode treeNodeE = new TreeNode<>("E", new ArrayList<>());
+ TreeNode treeNodeF = new TreeNode<>("F", new ArrayList<>());
+ TreeNode treeNodeG = new TreeNode<>("G", new ArrayList<>());
+ TreeNode treeNodeH = new TreeNode<>("H", new ArrayList<>());
+ TreeNode treeNodeI = new TreeNode<>("I", new ArrayList<>());
+ TreeNode treeNodeJ = new TreeNode<>("J", new ArrayList<>());
+ TreeNode treeNodeK = new TreeNode<>("K", new ArrayList<>());
+ // A->B,C
+ treeNodeA.getChildList().add(treeNodeB);
+ treeNodeA.getChildList().add(treeNodeC);
+ // B->D,E
+ treeNodeB.getChildList().add(treeNodeD);
+ treeNodeB.getChildList().add(treeNodeE);
+ // C->F,G
+ treeNodeC.getChildList().add(treeNodeF);
+ treeNodeC.getChildList().add(treeNodeG);
+ // D->H,I
+ treeNodeD.getChildList().add(treeNodeH);
+ treeNodeD.getChildList().add(treeNodeI);
+ // G->J,K
+ treeNodeG.getChildList().add(treeNodeJ);
+ treeNodeG.getChildList().add(treeNodeK);
+
+ System.out.println("非递归方式");
+ dfsNotRecursive(treeNodeA);
+ System.out.println();
+ System.out.println("前续遍历");
+ dfsPreOrderTraversal(treeNodeA, 0);
+ System.out.println();
+ System.out.println("后续遍历");
+ dfsPostOrderTraversal(treeNodeA, 0);
+ System.out.println();
+ System.out.println("中续遍历");
+ dfsInOrderTraversal(treeNodeA, 0);
+ }
+
+ /**
+ * 非递归方式
+ *
+ * @param tree
+ * @param
+ */
+ public static void dfsNotRecursive(TreeNode tree) {
+ if (tree != null) {
+ // 次数之所以用 Map 只是为了保存节点的深度,如果没有这个需求可以改为 Stack>
+ Stack