树结构
树结构定义
- 树结构是一种非线性存储结构,能表示“一对多”的关系。
树结构特点
- 只有一个节点的树也是树
- 新节点与任何一个节点连接形成的树称为子树
- 根节点,没有父亲节点,叶子节点没有子节点
- 除根节点外,任何一个节点都只有一个父节点
二叉树结构
二叉树的定义
- 每个节点最多有两个子节点的树称为二叉树
- 一棵深度为k,且有2^k-1个节点的二叉树,称为满二叉树。这种树的特点是每一层上的节点数都是最大节点数。
- 一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树
- 具有n个节点的完全二叉树的深度为floor(log2n)+1。深度为k的完全二叉树,至少有2k-1个叶子节点,至多有2k-1个节点。
二叉树的特点
- 左子树都小于父节点,右子树都大于父节点
- 左右子树分别是二叉排序树
二叉树的遍历
- 前序遍历:根节点、左节点,右节点
- 中序遍历:左节点,根节点,右节点
- 后序遍历:左节点,有节点,根节点
- 小技巧:
所谓的序,其实就是从左往右,根节点的位置
如图:
- 前序遍历:10,5,3,8,9,12,11,14
- 中序遍历:3,5,8,9,10,11,12,14
- 后序遍历:10,5,3,8,9,12,11,14
- 小技巧:
前序 + 中序 => 后序 + 层序
后序 + 中序 => 前序 + 层序
重要的几个代码实现(java 版本):
- 创建树
/**
* 使用递归添加元素
*
* @param node
* @param data
* @return
*/
private Node createTree(Node node, E data) {
//数据不能为空
if (Objects.isNull(data)) {
return null;
}
//当当前节点为空时,添加元素,并且容量+1
if (Objects.isNull(node)) {
return new Node(data);
}
//元素比较,如果输入的元素比存在的小,往当前元素的左边继续查找。如果还是小,继续往左边查找。否则往右边查找。直到值为空。添加新的元素
if (data.compareTo(node.data) < 0) {
node.leftChild = createTree(node.leftChild, data);
} else if (data.compareTo(node.data) > 0) {
node.rightChild = createTree(node.rightChild, data);
} else {//暂时不比较重复的元素
logger.info("不能包含重复数据");
}
return node;
}
- 前序遍历
/**
* 1.先放中节点
* 2.有右节点放右节点
* 3.有左节点放左节点
*
* @param currentNode
* @return
*/
public Collection<E> preOrder2(Node currentNode) {
Collection<E> res = new ArrayList<E>();
Stack<Node> stack = new Stack();
stack.add(currentNode);
//当前节点不等空,或者栈顶不为空,都需要继续遍历
while (!stack.isEmpty()) {
currentNode = stack.pop();
res.add(currentNode.data);
if (Objects.nonNull(currentNode.rightChild)) {
stack.push(currentNode.rightChild);
}
if (Objects.nonNull(currentNode.leftChild)) {
stack.push(currentNode.leftChild);
}
}
return res;
}
- 中序遍历
/**
* 中序遍历方式2
* // 1.左节点不为null则压入左节点
* // 2.左节点为null时,pop并打印,左节点
* // 3.在往右,右节点为null时,pop并打印
* // 4.右节点不为null时,压入右节点
*
* @param currentNode
* @return
*/
public Collection<E> inOrder2(Node currentNode) {
Collection<E> res = new ArrayList<E>();
Stack<Node> stack = new Stack();
//当前节点不等空,或者栈顶不为空,都需要继续遍历
while (Objects.nonNull(currentNode) || !stack.isEmpty()) {
if (Objects.nonNull(currentNode)) {
stack.add(currentNode);
currentNode = currentNode.leftChild;
} else {
currentNode = stack.pop();
res.add(currentNode.data);
currentNode = currentNode.rightChild;
}
}
return res;
}
- 后序遍历
/**
* 非递归后序遍历,使用栈结构进行遍历
* //和前序遍历一样的只不过是使用了两个栈
* //在前序遍历的时候将 中 右 左 节点压栈
* //在原来是打印的地方不打印,将本该打印的节点压到第二个栈中
* //这样第二个栈的出栈顺序就是 右 左 中了
* <p>
* //
* // 10 //
* // / \ //
* // 5 12 //
* // / \ / \ //
* // 3 8 11 14 //
* // \ //
* // 9 //
* //
*
* @param currentNode 需要遍历的节点
* @return
*/
public Collection<E> postOrder(Node currentNode) {
Collection<E> res = new ArrayList<E>();
Stack<Node> stack = new Stack();
Stack<Node> resStack = new Stack();
//把当前元素添加到栈低
stack.push(currentNode);
//当前节点不等空,或者栈顶不为空,都需要继续遍历
while (!stack.isEmpty()) {
currentNode = stack.pop();
resStack.push(currentNode);
if (Objects.nonNull(currentNode.leftChild)) {
stack.push(currentNode.leftChild);
}
if (Objects.nonNull(currentNode.rightChild)) {
stack.push(currentNode.rightChild);
}
}
while (!resStack.isEmpty()) {
res.add(resStack.pop().data);
}
return res;
}