一. 数据结构-二叉树
1. 创建一个二叉树的结点类:
代码实现
package tree;
/**
* @Author: qianwen
* @Date: 2019/4/1
* @Description:创建一个二叉树的结点类
*/
public class TreeNode {
int val;
TreeNode left = null;
TreeNode right = null;
TreeNode(int x){
val = x;
}
}
2. 剑指offer面试题
(1)根据前序和中序遍历结果重构二叉树
涉及知识点:
前序遍历二叉树:
-
遍历顺序:根节点—》左子树—》右子树
中序遍历二叉树: -
遍历顺序:左子树—》根节点—》右子树
题目:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
代码实现:package tree; import java.util.Arrays; /** * @Author: qianwen * @Date: 2019/4/1 * @Description:根据前序和中序遍历结果重构二叉树 */ public class ReConstructBinaryTree { //@param: int [] pre 前序遍历的序列,int [] in 中序遍历的序列 public TreeNode reConstructBinaryTree(int [] pre,int [] in) { //算法思路:数组pre的第一个元素是根节点,从in数组中找到根节点,根节点左边是左子树,根节点右边是右子树,通过递归可继续 // 得到左右子树的根节点 //异常情景: if (pre == null || in == null){ return null; } if (pre.length == 0 || in.length == 0){ return null; } if (pre.length != in.length){ return null; } //正常情景: TreeNode root = new TreeNode(pre[0]); //遍历数组in,查找与根节点值相同的元素 for (int i = 0; i < in.length; i++){ if (pre[0] == in[i]){ //取左子树进行递归 root.left = reConstructBinaryTree( Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i)); //取右子树进行递归 root.right = reConstructBinaryTree( Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length)); } } return root; } public static void main(String[] args) { ReConstructBinaryTree rcb = new ReConstructBinaryTree(); TreeNode result = rcb.reConstructBinaryTree(new int[]{1,2,4,7,3,5,6,8}, new int[]{4,7,2,1,5,3,8,6}); System.out.println("根节点的值是:" + result.val); System.out.println("左子树的根节点值是:" + result.left.val); System.out.println("右子树的根节点值是:" + result.right.val); } }
(2)层序遍历二叉树
涉及知识点:
层序遍历二叉树:-
遍历顺序:从根节点开始,每层按从左到右的顺序遍历二叉树
题目:
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
代码实现:
public class PrintFromTopToBottom {
public ArrayList printFromTopToBottom(TreeNode root) {
//异常case:
if (root == null)
return null;//算法思路:利用LinkedList类可“尾进头出”的特点,层序遍历二叉树 //正常case: //用来存储遍历的二叉树结点的值 ArrayList<Integer> arrayList = new ArrayList<>(); //可实现队列、栈,支持按索引查找元素 LinkedList<TreeNode> queue = new LinkedList<>(); //将根节点塞进队列中 queue.add(root); while (!queue.isEmpty()) { //获取并删除队列的第一个元素,**java中没有封装专门实现队列的类?** TreeNode node = queue.poll(); //将队列中第一个结点的值塞到集合中 arrayList.add(node.val); //若左子树非空,将左子树的根节点塞进队列中 if (node.left != null) { queue.addLast(node.left); } //若右子树非空,将右子树的根节点塞进队列中 if (node.right != null) { queue.addLast(node.right); } } return arrayList;
}
//测试脚本
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);PrintFromTopToBottom pr = new PrintFromTopToBottom(); ArrayList result = pr.printFromTopToBottom(root); for (Object i : result){ System.out.println(i); }
}
}
-
补充Java语言基础知识点:
Queue接口
- 实现了队列的数据结构
- 接口提供的方法:
插入元素:add() 将指定元素插入队列的尾部
offer() 将指定元素插入队列的尾部,更适合有容量限制的队列
获取元素:element() 获取队列头部的元素,但是不删除该元素
peek() 获取队列头部的元素,但是不删除该元素。当队列为空时,返回null
remove() 获取队列头部的元素,并删除该元素
poll() 获取队列头部的元素,并删除该元素。当队列为空时,返回null
Deque接口
- 是Queue接口的子接口
- 实现了“双端队列”的数据结构
- 接口提供的方法
插入元素:addFirst()
addLast()
offerFirst()
offerLast()
获取元素:getFirst() 获取并不删除队列的第一个元素
getLast() 获取并不删除队列的最后一个元素
peekFirst() 获取并不删除队列的第一个元素,若双端队列为空,则返回null
peekLast() 获取并不删除队列的最后一个元素,若双端队列为空,则返回null
pollFirst()
pollLast()
removeFirst()
removeLast()
栈方法: pop()
push()
Deque的实现类:
4. ArrayDeque:可实现栈和队列,底层是一个数组,长度默认是16---->可以代替Stack
5. LinkedList:可实现栈和队列,还实现了List接口,底层是一个链表
(3)后序遍历二叉搜索/排序树
涉及知识点:
后序遍历二叉树:
1. 遍历顺序:左子树—》右子树—》根节点
二叉搜索树:
它或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
代码实现:
public class VerifySquenceOfBST {
public boolean verifySquenceOfBST(int[] sequence) {
if (sequence == null || sequence.length == 0)
return false;
int rstart = 0;
int length = sequence.length;
//数组的最后一个元素,是根节点
//从数组的第一个元素查找,第一个大于根节点的元素
for (int i = 0; i <= length - 2; i++) {
if (sequence[i] < sequence[length - 1])
rstart++;
}
//rstart == 0 ==>说明没有左子树
if (rstart == 0) {
verifySquenceOfBST(Arrays.copyOfRange(sequence,0,length-1));
}else {
//restart > 0 ==>说明有左子树
//如果从第一个大于根节点的元素开始,到根节点前面一位的元素,值都大于根节点,则进行下一步;否则直接false;
for (int i = rstart; i <= length - 2; i++) {
if (sequence[i] <= sequence[length - 1]) {
return false;
}
}
//开始遍历左子树
verifySquenceOfBST(Arrays.copyOfRange(sequence,0,rstart));
//开始遍历右子树
verifySquenceOfBST(Arrays.copyOfRange(sequence,rstart,length - 1));
}
return true;
}
public static void main(String[] args) {
VerifySquenceOfBST v = new VerifySquenceOfBST();
System.out.println(v.verifySquenceOfBST(new int[]{4,5,2,3}));
}
}
//算法思路:https://blog.csdn.net/xidiancoder/article/details/60956436
(4)输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
题目:
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
代码实现:
public class HasSubtree {
public boolean hasSubtree(TreeNode root1,TreeNode root2) {
//此处root == null是为了判断第二个二叉树一开始是不是空树
if (root1 == null && root2 == null)
return false;
//若root1和root2均不为空,1.比较树1和树2是否相同;2.比较树1的左子树和树2是否相同;3.比较树1的右子树和树2是否相同
return isSubTree(root1,root2) || hasSubtree(root1.left,root2) || hasSubtree(root1.right,root2);
}
public boolean isSubTree(TreeNode root1,TreeNode root2){
//此处return返回true,是直至第二个二叉树已经遍历完成,均和第一个二叉树的结点相同
if (root2 == null){
return true;
}
if (root1 == null){
return false;
}
if (root1.val == root2.val){
return isSubTree(root1.right, root2.right) && isSubTree(root1.left, root2.left);
}
return false;
}
public static void main(String[] args) {
//创建第一颗树
TreeNode root1 = new TreeNode(1);
root1.left = new TreeNode(2);
root1.right = new TreeNode(3);
root1.left.left = new TreeNode(4);
root1.left.right = new TreeNode(5);
//创建第二颗树
TreeNode root2 = new TreeNode(1);
root2.left = new TreeNode(2);
root2.right = new TreeNode(3);
root2.left.left = new TreeNode(8);
HasSubtree hasSubtree = new HasSubtree();
System.out.println(hasSubtree.hasSubtree(root1,root2));
}
}
这种解法有问题
(4)操作给定的二叉树,将其变换为源二叉树的镜像
题目:
操作给定的二叉树,将其变换为源二叉树的镜像
代码实现:
方法1:递归实现
public class Mirror {
public void mirror(TreeNode root) {
if (root == null)
return;
//保证二叉树的左右子树至少存在一个
if (root != null && (root.left != null || root.right != null)){
//交换左右子树
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
//递归调用继续交换下一个左右子树
mirror(root.left);
mirror(root.right);
}
}
}
方法2. 循环
public void mirror(TreeNode root) {
//方法2:循环
if (root == null)
return;
**//java中的栈怎么实现的?**
Stack<TreeNode> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
//将根结点的左右子树相互交换
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
//将根结点塞进栈中
stack.push(root);
//继续交换左子树的结点
root = root.left;
}
//**这一步是为啥?**
if (!stack.isEmpty()) {
root = stack.pop();
root = root.right;
}
}
}
补充知识点:
Java中的Stack类实现
- 继承自Vector类
- 底层也是数组,默认长度10
- 线程安全,性能不好
- Stack提供的方法:peek() peek() push()