数据结构——完全二叉树和满二叉树
这里写目录标题完全二叉树实现(基于数组)原理代码实现测试代码完全二叉树、堆、优先级队列满二叉树完全二叉树实现(基于数组)完全二叉树除最后一层外,其它各层的节点数目均已达最大值,且最后一层所有节点从左向右连续地紧密排列,故使用数组实现不会造成空间浪费,相比递归更容易理解原理根据树的层次结构,可知若父节点从1开始,其左节点为2n,右节点为2n+1若父节点从0开始,其左节点为2n+1,右节点为2n+2若
完全二叉树和满二叉树
完全二叉树实现(基于数组)
完全二叉树除最后一层外,其它各层的节点数目均已达最大值,且最后一层所有节点从左向右连续地紧密排列,故使用数组实现不会造成空间浪费,相比递归更容易理解
原理
根据树的层次结构,可知
- 若父节点从1开始,其左节点为2n,右节点为2n+1
- 若父节点从0开始,其左节点为2n+1,右节点为2n+2
- 若子节点为n,则其父节点为n-1/2
代码实现
- getTreeNum()获取总节点数,返回size
- getTreeDepth()获取树高度,n个数两两平分为log2n+1层(层数从1算起)
- getLeafNum()获取叶节点数,其为总节数减去上层节点数总和(等差数列和为2n+1)
- getCurrentLevelNum()获取指定层次节点,最后一层即为叶节点数,其余各层都是2的幂
class CompleteBinaryTree<T> {
private Object[] elements;
private int size;
public CompleteBinaryTree() {
elements = new Object[7];
size = 0;
}
public CompleteBinaryTree(T[] array) {
elements = array;
size = array.length;
}
public void add(int value) {
elements[size] = value;
size++;
}
public T remove() {
T temp = (T) elements[0];
elements[0] = elements[size - 1];
elements[size - 1] = 0;
size--;
return temp;
}
public int getTreeNum() {
return size;
}
public int getTreeHeight() {
if (size == 0) {
return 0;
}
return (int) (Math.log(size) / Math.log(2)) + 1;
}
public int getLeafNum() {
if (size == 0) {
return 0;
}
if (size == 1) {
return 1;
}
return size - (2 * (getTreeDepth() - 2) + 1);
}
public int getCurrentLevelNum(int level) {
if (level > getTreeDepth()) {
return 0;
} else if (level == getTreeDepth()) {
return getLeafNum();
} else {
return (int) Math.pow(2, level - 1);
}
}
public void preOrderTraversalWithoutRecursion() {
Stack<Integer> stack = new Stack<>();
if (size != 0) {
stack.push(0);
while (!stack.empty()) {
int index = stack.pop();
visitNode(index);
if (2 * index + 2 < size) {
stack.push(2 * index + 2);
}
if (2 * index + 1 < size) {
stack.push(2 * index + 1);
}
}
}
}
public void MidOrderTraversalWithoutRecursion() {
Stack<Integer> stack = new Stack<>();
int index = 0;
while (index < size || !stack.empty()) {
if (index < size) {
stack.push(index);
index = 2 * index + 1;
} else {
index = stack.pop();
visitNode(index);
index = 2 * index + 2;
}
}
}
public void SufOrderTraversalWithoutRecursion() {
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
if (size != 0) {
stack1.push(0);
while (!stack1.empty()) {
int index = stack1.pop();
stack2.push(index);
if (index * 2 + 1 < size) {
stack1.push(index * 2 + 1);
}
if (index * 2 + 2 < size) {
stack1.push(index * 2 + 2);
}
}
while (!stack2.empty()) {
visitNode(stack2.pop());
}
}
}
public void LevelTraversal() {
for (int i = 0; i < size; i++) {
visitNode(i);
}
}
private void visitNode(int index) {
System.out.print((T) elements[index] + " ");
}
public boolean BreadthFirstSearch(T value) {
for (int i = 0; i < size; i++) {
visitNode(i);
if ((T) elements[i] == value) {
return true;
}
}
return false;
}
public boolean DepthFirstSearch(T value) {
Stack<Integer> stack = new Stack<>();
if (size != 0) {
stack.push(0);
while (!stack.empty()) {
int index = stack.pop();
visitNode(index);
if ((T) elements[index] == value) {
return true;
}
if (2 * index + 2 < size) {
stack.push(2 * index + 2);
}
if (2 * index + 1 < size) {
stack.push(2 * index + 1);
}
}
}
return false;
}
}
-
preOrderTraversalWithoutRecursion():前序遍历,先入栈根节点,再出栈根节点,判断该节点是否存在子节点,若存在则入栈其右左节点,随后出栈其左右节点,即根-左-右
-
MidOrderTraversalWithoutRecursion():中序遍历,先入栈根节点,再循环入栈其全部左节点,直到左节点全部入栈,出栈左节点,再出栈子树根节点判断是否有右节点,若有则入栈,随后出栈右节点,即左-根-右
-
SufOrderTraversalWithoutRecursion():后序遍历,栈1入栈根节点,出栈并入栈2,栈1入栈左右节点,每次出栈右左节点,故栈2出栈顺序为左-右-根
-
LevelTraversal:层次遍历,由于完全二叉树是按顺序摆放的,故根据下标顺序遍历即可
-
BreadthFirstSearch():广度优先搜索,同层次遍历
-
DepthFirstSearch():深度优先搜索,同前序遍历
测试代码
Integer[] array = {1, 2, 3, 4, 5, 6};
CompleteBinaryTree completeBinaryTree = new CompleteBinaryTree(array);
System.out.print("不用递归前序遍历:");
completeBinaryTree.preOrderTraversalWithoutRecursion();
System.out.println();
System.out.print("不用递归中序遍历:");
completeBinaryTree.MidOrderTraversalWithoutRecursion();
System.out.println();
System.out.print("不用递归后序遍历:");
completeBinaryTree.SufOrderTraversalWithoutRecursion();
System.out.println();
System.out.print("层次遍历: ");
completeBinaryTree.LevelTraversal();
System.out.println();
System.out.print("广度优先搜索: ");
boolean breadthFirstResult = completeBinaryTree.BreadthFirstSearch(5);
System.out.print(breadthFirstResult ? "found" : "not found");
System.out.println();
System.out.print("深度优先搜索: ");
boolean depthFirstResult = completeBinaryTree.DepthFirstSearch(5);
System.out.print(depthFirstResult ? "found" : "not found");
System.out.println();
System.out.println("树高度:" + completeBinaryTree.getTreeHeight());
System.out.println("总节点数:" + completeBinaryTree.getTreeNum());
System.out.println("叶节点数:" + completeBinaryTree.getLeafNum());
System.out.println("第三层节点数:" + completeBinaryTree.getCurrentLevelNum(3));
这里的打印同上面普通的二叉树
不用递归前序遍历:1 2 4 5 3 6
不用递归中序遍历:4 2 5 1 6 3
不用递归后序遍历:4 5 2 6 3 1
层次遍历: 1 2 3 4 5 6
广度优先搜索: 1 2 3 4 5 found
深度优先搜索: 1 2 4 5 found
树高度:3
总节点数:6
叶节点数:3
第三层节点数:3
完全二叉树、堆和优先级队列的关系
为了保证完全二叉树的性质,上面代码的
- add():添加元素到二叉树尾部
- remove():删除头部元素,并将末尾元素补上(或删除元素移动后序元素)
无参数指定的删除对于普通的二叉树是无任何意义的,因为无法知道接下来获取的元素是哪个
但对基于完全二叉树实现的堆,每次删除可获取最大或最小值
与此同时,添加元素默认添加到尾部,根据这两个特性可进一步构成优先级队列
具体介绍可看数据结构——堆和优先级队列
满二叉树实现
每层节点都达到最大值(即2的幂)的二叉树为满二叉树,其也用数组实现,原理同完全二叉树,不再赘述

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)