1. 定义
二叉树是一种树形数据结构,由节点组成,每个节点最多有两个子节点,分别为左子节点和右子节点。二叉树具有以下性质:
每个节点最多有两个子节点,称为左子节点和右子节点。
- 左子节点和右子节点可以为空,即一个节点没有子节点或只有一个子节点。
- 二叉树的子树也是二叉树,即每个子节点都可以看作是根节点,构成一个新的二叉树。
- 二叉树可以是空树,即不包含任何节点的情况。
- 二叉树的节点之间不存在环,即不能通过任意路径从一个节点回到自身。
2. 遍历
二叉树的遍历主要有三种方式,前序遍历、中序遍历和后序遍历。
- 前序遍历是根据【根-左-右】的顺序进行遍历,即首先访问根节点,然后访问左子树,最后访问右子树。
- 中序遍历是根据【左-根-右】的顺序进行遍历,即首先访问左子树,然后访问根节点,最后访问右子树。
- 后序遍历是根据【左-右-根】的顺序进行遍历,首先访问左子树,然后访问右子树,最后访问根节点。
3. 节点结构定义
构建二叉树,首先要构造二叉树的节点,以下是二叉树节点的定义:
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;}
}
在上述代码中,我们定义了一个名为TreeNode的类,包含了节点的值(val)、左子节点(left)、右子节点(right)三个成员变量。同时,我们提供了三个构造方法,分别用于创建空节点、只含值的节点和含值、左右子节点的节点。
4. 通过遍历结果生成二叉树
前提无重复值
1. 已知前中序遍历生成二叉树 力扣105
- 常用递归写法
将问题拆分为一个个子问题,即将每个二叉树结点当作一个子问题来求解。
public TreeNode buildTree(int[] preorder, int[] inorder) {int n = preorder.length;if(n == 0){return null;}int leftSize = indexOf(inorder,preorder[0]);int[] pre1 = Arrays.copyOfRange(preorder,1,1+leftSize);int[] pre2 = Arrays.copyOfRange(preorder,1+leftSize,n);int[] in1 = Arrays.copyOfRange(inorder,0,leftSize);int[] in2 = Arrays.copyOfRange(inorder,leftSize+1,n);TreeNode left = buildTree(pre1,in1);TreeNode right = buildTree(pre2,in2);return new TreeNode(preorder[0],left,right);
}
public int indexOf(int[] inorder,int x){for(int i = 0;i < inorder.length;i++){if(inorder[i] == x){return i;}}return -1;
}
时间复杂度:O(n^2),空间复杂度: O(n^2)
2. 在上述方法需要遍历整个集合求出根结点的下标,时间复杂度为O(n),可以使用哈希表来进一步简化这一过程:
public TreeNode buildTree(int[] preorder,int[] inorder){int n = preorder.length;Map<Integer,Integer> index = new HashMap<Integer,Integer>();for(int i = 0;i < n;i++){index.put(inorder[i],i);}return dfs(preorder,0,n,inorder,0,n,index);}public TreeNode dfs(int[] preorder,int preL,int preR,int[] inorder,int inL,int inR,Map<Integer,Integer> index){if(preL == preR){return null;}int leftSize = index.get(preorder[preL]) - inL;TreeNode left = dfs(preorder,preL + 1,preL + 1 + leftSize,inorder,inL,inL+leftSize,index);TreeNode right = dfs(preorder,preL + 1 + leftSize,preR,inorder,inL+leftSize + 1,inR,index);return new TreeNode(preorder[preL],left,right);}
时间复杂度:O(n),空间复杂度:O(n)
2. 已知中后序遍历生成二叉树 力扣106
- 常用递归写法
首先要确定左子树的长度,然后根据中后序遍历的特点进行节点划分。
public TreeNode buildTree(int[] inorder, int[] postorder) {int n = inorder.length;if(n == 0){return null;}int leftSize = indexOf(inorder,postorder[n - 1]);int[] inL = Arrays.copyOfRange(inorder,0,leftSize);int[] inR = Arrays.copyOfRange(inorder,leftSize + 1,n);int[] postL = Arrays.copyOfRange(postorder,0,leftSize);int[] postR = Arrays.copyOfRange(postorder,leftSize,n - 1);TreeNode left =buildTree(inL,postL);TreeNode right = buildTree(inR,postR);return new TreeNode(postorder[n-1],left,right);}public int indexOf(int[] arr,int x){int n = arr.length;for(int i =0;i < n;i++){if(arr[i] == x){return i;}}return -1;}
时间复杂度:O(n^2),空间复杂度: O(n^2)
3. 使用哈希表来进一步简化:
public TreeNode buildTree(int[] inorder, int[] postorder) {int n = inorder.length;HashMap<Integer,Integer> index = new HashMap<Integer,Integer>();for(int i = 0;i < n;i++){index.put(inorder[i],i);}return dfs(inorder,0,n-1,postorder,0,n-1,index);}public TreeNode dfs(int[] inorder,int inL,int inR,int[] postorder,int postL,int postR,HashMap<Integer,Integer> index){if(inL > inR){return null;}if(inL == inR){return new TreeNode(inorder[inL]);}int leftSize = index.get(postorder[postR]) - inL;TreeNode left = dfs(inorder,inL,inL + leftSize - 1,postorder,postL,postL + leftSize - 1,index);TreeNode right = dfs(inorder,inL + leftSize + 1,inR,postorder,postL + leftSize,postR - 1,index);return new TreeNode(postorder[postR],left,right);}
时间复杂度:O(n),空间复杂度:O(n)
2. 已知前后序遍历生成二叉树 力扣889
- 常用递归写法
public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {int n = preorder.length;if(n == 0){return null;}if(n == 1){return new TreeNode(preorder[0]);}int leftSize = indexOf(postorder,preorder[1]) + 1;int[] preL = Arrays.copyOfRange(preorder,1,leftSize + 1);int[] preR = Arrays.copyOfRange(preorder,leftSize + 1,n);int[] postL = Arrays.copyOfRange(postorder,0,leftSize);int[] postR = Arrays.copyOfRange(postorder,leftSize,n-1);TreeNode left = constructFromPrePost(preL,postL);TreeNode right = constructFromPrePost(preR,postR);return new TreeNode(preorder[0],left,right);}public int indexOf(int[] arr,int x){int n = arr.length;for(int i =0; i< n;i++){if(arr[i] == x){return i;}}return -1;}
时间复杂度:O(n^2),空间复杂度: O(n^2)
2. 使用哈希表来进一步简化:
public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {int n = preorder.length;HashMap<Integer,Integer> index = new HashMap<Integer,Integer>();for(int i =0;i < n;i++){index.put(postorder[i],i);}return dfs(preorder,0,n-1,postorder,0,n-1,index);}public TreeNode dfs(int[] preorder,int preL,int preR,int[] postorder,int postL,int postR,HashMap<Integer,Integer> index){if(preL > preR)return null;if(preL == preR)return new TreeNode(preorder[preL]);int leftSize = index.get(preorder[preL + 1]) - postL + 1;TreeNode left = dfs(preorder,preL + 1,preL + leftSize,postorder,postL,postL + leftSize - 1,index);TreeNode right = dfs(preorder,preL + leftSize + 1,preR,postorder,postL + leftSize,postR - 1,index);return new TreeNode(preorder[preL],left,right);}
时间复杂度:O(n),空间复杂度:O(n)
5. 总结
通过前中、前后、中后遍历结果生成二叉树关键点在于确定左子树和右子树的边缘范围。