一,二叉树的创建
当二叉树采用顺序存储结构的时候,我们可以模仿二叉树的层次遍历创建一棵二叉树
代码如下:
/**
* 创建二叉树
*
* @Documented
* @param values
* <p>
* <blockquote>
*
* <pre>
* 节点的值 形如 {"a","b","c",null, null,"d","e"}
* a
* / \
* b c
* / \
* d e
* </pre>
*
* </blockquote>
* <p>
* @return 根节点
*/
public TreeNode<T> createBinaryTree(T[] values) {
if (values == null || values.length == 0) {
return null;
}
List<TreeNode<T>> container = new ArrayList<TreeNode<T>>();
// step 1:对每一个value创建TreeNode节点
int len = values.length;
for (int i = 0; i < len; i++) {
if (values[i] != null) {
container.add(new TreeNode<T>(values[i]));
} else {
container.add(null);
}
}
// step 2: 使用快慢索引进行遍历节点. fast索引指向的节点是慢索引节点的子节点
int slow = 0, fast = 1;
TreeNode<T> node = null;
while (fast < len) {
// 得到父节点
node = container.get(slow);
if (node != null) {
// 父节点不为空,则父节点左子树指向fast子节点
node.left = container.get(fast++);
if (fast < len) {
// 父节点不为空,则父节点右子树指向slow子节点
node.right = container.get(fast++);
}
} else {
// 如果父节点为空,那么跳过两个为null的节点
fast += 2;
}
// 处理完左右节点后,slow偏移1
slow++;
}
return container.get(0);
}
二,删除二叉树
二叉树的删除我们可以使用二叉树的递归性质进行
如果根节点为空,返回
否则
递归删除左子树
递归删除右子树
删除根节点
代码如下:
/**
* 删除二叉树
*
* @param root
* 根节点
*/
public void destoryBinaryTree(TreeNode<T> root) {
if (root == null) {
return;
}
destoryBinaryTree(root.left);
destoryBinaryTree(root.right);
root = null;
}
1,利用二叉树的递归性质查询
如果根节点为空,返回0
如果根节点等于要查询要查询的节点返回 level+1
查询左子树
如果得到层数不为0,则返回查找的层数
否则,返回右子树查到的层数
代码如下:
/**
*
* @param root
* 根节点
* @param value
* 查找节点的值
* @param level
* 节点的层级
* @return 节点的层级
*/
/*
* 根节点为空返回0 根节点不为空且等于value 返回level+1 否则 查找左子树,查找到返回level 查找不到返回右子树的查找的值
*/
public int getLevel(TreeNode<T> root, T value, int level) {
if (root == null) {
return 0;
}
else if (root.value == value) {
return level + 1;
}
else {
int leftLevle = getLevel(root.left, value, level + 1);
if (leftLevle != 0) {
return leftLevle;
} else {
return getLevel(root.right, value, level + 1);
}
}
}
2,我们可以由求二叉树的高度得到启发
当我们使用非递归的后序遍历的时候,使用栈作为辅助数据结构, 当遍历到某个节点的时候,栈中元素从栈顶到栈底就是根节点到该节点的路径. 当我们访问某个节点的时候,判断该节点是不是要查找的节点,如果是,直接返回栈中元素的个数,就是该元素在二叉树中的层次
3,通过层次遍历实现
通过打印层号的二叉树层次遍历可知,当我们每访问一个节点的时候,就判断该节点是不是要查找的节点,如果是,返回当前的层号
四,求指定层数的节点个数
1,通过层次遍历实现.
代码如下:
/**
* 获取指定层数的节点的个数
*
* @param root
* 根节点
* @param level
* 节点的层数
* @return
*/
public int getNodeInLevel(TreeNode<T> root, int level) {
if (root == null) {
return 0;
}
int currentLevel = 1;
int front = 0, rear = 1;
int start = 0, last = 1;
List<TreeNode<T>> stack = new ArrayList<TreeNode<T>>();
stack.add(root);
TreeNode<T> node = null;
while (front != rear) {
if (currentLevel == level) {
return last - start;
}
node = stack.get(front);
if (node.left != null) {
stack.add(node.left);
rear++;
}
if (node.right != null) {
stack.add(node.right);
rear++;
}
front++;
if (front == last) {
currentLevel++;
last = rear;
start = front;
}
}
return 0;
}
2,由求二叉树宽度的递归算法得到启发
最后取得数组中对应层数下标的元素就是该层数节点的个数
五,求叶子节点到根节点的路径
1,利用层次遍历,但是需要一个辅助的数据结构用来存放该节点的直接父节点在队列中的索引,根节点默认是-1
代码如下:
private static class PNode<T> {
public PNode(TreeNode<T> node, int index) {
super();
this.node = node;
this.parent = index;
}
TreeNode<T> node;
int parent;// 层次遍历中某个节点的父节点在队列中的位置,根节点的父节点位置是-1
}
/**
* 二叉树中叶子节点到根节点的逆路径.
*
* @param root
* 根节点
*/
// 利用层次遍历,外加辅助数据结构 PNode<T>
public void getPathFromLeafToRoot1(TreeNode<T> root) {
if (root == null) {
return;
}
List<PNode<T>> queue = new ArrayList<PNode<T>>();
queue.add(new PNode<T>(root, -1));
int front = 0, rear = 1;
PNode<T> node = null;
while (front != rear) {
node = queue.get(front);
if (node.node.left == null && node.node.right == null) {
printLeaftToRootPath(queue, node);
}
if (node.node.left != null) {
queue.add(new PNode<>(node.node.left, front));
rear++;
}
if (node.node.right != null) {
queue.add(new PNode<>(node.node.right, front));
rear++;
}
front++;
}
}
private void printLeaftToRootPath(List<PNode<T>> queue, PNode<T> node) {
if (queue == null || queue.size() == 0 || node == null) {
return;
}
System.out.print(node.node.value + " 到根节点的逆路径是 : ");
System.out.print(node.node.value + " ");
int parent = node.parent;
PNode<T> temp = null;
while (parent >= 0) {
temp = queue.get(parent);
System.out.print(temp.node.value + " ");
parent = temp.parent;
}
System.out.println();
}
2,利用先序遍历
/**
* 二叉树中叶子节点到根节点的逆路径.
*
* @param root
* 根节点
*/
// 利用先序遍历,叶子节点到根节点的路径
public void getPathFromLeafToRoot2(TreeNode<T> root) {
if (root == null) {
return;
}
List<TreeNode<T>> paths = new ArrayList<TreeNode<T>>();
dfs(root,paths);
}
private void dfs(TreeNode<T> root, List<TreeNode<T>> stack) {
if (root.left == null && root.right == null) {
System.out.print(root.value + "到根节点的路径是: " + root.value + " ");
for (int i = stack.size() - 1; i >= 0; i--) {
System.out.print(stack.get(i).value + " ");
}
System.out.println();
} else {
if (root.left != null) {
List<TreeNode<T>> temp = new ArrayList<TreeNode<T>>(stack);
temp.add(root);
dfs(root.left, temp);
}
if (root.right != null) {
List<TreeNode<T>> temp = new ArrayList<TreeNode<T>>(stack);
temp.add(root);
dfs(root.right, temp);
}
}
}
3,利用后序遍历
利用后序遍历,节点到根节点的路径就是栈中的元素,叶子节点到根节点的路径
代码如下:
/**
* 二叉树中叶子节点到根节点的逆路径.
*
* @param root
* 根节点
*/
// 利用后序遍历,节点到根节点的路径就是栈中的元素,叶子节点到根节点的路径
public void getPathFromLeafToRoot3(TreeNode<T> root) {
if (root == null) {
return;
}
List<TreeNode<T>> stack = new ArrayList<TreeNode<T>>();
TreeNode<T> node = root;
do {
while (node != null) {
stack.add(node);
node = node.left;
}
boolean flag = true;
TreeNode<T> temp = null;
while (flag && stack.size() > 0) {
node = stack.get(stack.size() - 1);
if (node.right == temp) {
node = stack.remove(stack.size() - 1);
temp = node;
if (node.left == null && node.right == null) {
System.out.print(root.value + "到根节点的路径是: " + node.value + " ");
for (int i = stack.size() - 1; i >= 0; i--) {
System.out.print(stack.get(i).value + " ");
}
System.out.println();
}
} else {
node = node.right;
flag = false;
}
}
} while (stack.size() > 0);
}
六,公共祖先(LCA)
1,利用非递归后序遍历
非递归后序遍历,需要使用到栈这个辅助数据结构,栈中元素栈底到栈顶的元素顺序就是根节点到栈顶节点的路径. 我们找到从根节点到这个两个节点的路径,然后进行逆序遍历比对,第一个相同的节点就是最近的公共祖先
代码如下:
/**
* 两个节点的最近公共祖先节点
*
* @param root
* 根节点
* @param p
* 节点p
* @param q
* 节点q
* @return
*/
// 利用后序遍历
public TreeNode<T> lowestCommonAncestor(TreeNode<T> root, TreeNode<T> p, TreeNode<T> q) {
if (root == null || root == p || root == q) {
return root;
}
List<TreeNode<T>> first = new ArrayList<TreeNode<T>>();
List<TreeNode<T>> second = new ArrayList<TreeNode<T>>();
getPath(root, p, first);
getPath(root, q, second);
return getCommonNode(first, second);
}
private TreeNode<T> getCommonNode(List<TreeNode<T>> first, List<TreeNode<T>> second) {
for (int i = first.size() - 1; i >= 0; i--) {
TreeNode<T> temp = first.get(i);
for (int j = second.size() - 1; j >= 0; j--) {
if (temp == second.get(j)) {
return temp;
}
}
}
return null;
}
private void getPath(TreeNode<T> root, TreeNode<T> node, List<TreeNode<T>> path) {
TreeNode<T> temp = root;
do {
while (temp != null) {
path.add(temp);
if (node == temp) {
return;
}
temp = temp.left;
}
TreeNode<T> t = null;
boolean flag = true;
while (flag && path.size() > 0) {
temp = path.get(path.size() - 1);
if (temp.right == t) {
t = path.remove(path.size() - 1);
} else {
flag = false;
temp = temp.right;
}
}
} while (path.size() > 0);
}
2,二叉树按照顺序存储结构进行存储. 求距离下标p和q两个节点最近的公共祖先节点的值
代码如下:
/**
* 二叉树按照顺序存储结构进行存储. 求距离下标p和q两个节点最近的公共祖先节点的值
* @param array
* @param p
* @param q
* @return
*/
public TreeNode<T> getLCAIndex(TreeNode<T>[] array, int p, int q) {
int m = p, n = q;
while (m != n) {
if (m > n) {
m = m / 2;
} else {
n = n / 2;
}
}
return array[m];
}