目录:
1. 树
2.二叉树
1. 树
树是一种非线性的数据结构,它是由n(n>=0)个有限节点,组成一个具有层次关系的集合。
特点:Ⅰ. 有一个特殊的节点,称为根节点,根节点没有前驱节点。
Ⅱ. 除根节点外,其余节点被分为M(M>0)个互不相交的集合T1,T2......Tm,其中每一个集合又是一棵与树类似的子树。每棵树的根节点有且仅有一个前驱,可有0个或多个后继。
Ⅲ. 树是递归定义的。
判断树与非树:Ⅰ. 子树是不相交的。
Ⅱ. 除根节点外每个节点有且仅有一个父节点。
Ⅲ. 一棵N个节点的树有N - 1条边。
节点的度:一个节点含有子树的个数。(度为0的节点称为叶子节点)
树的度:所有度的最大值。
根节点:没有双亲结点的节点。
节点层次:根为第一层,以此类推。
树的高度或深度:所有节点层次中的最大层次。
树结构相对复杂,要储存起来比较麻烦,有很多方式:双亲表示法,孩子表示法,孩子双亲表示法,孩子兄弟表示法。
双亲表示法:
节点中储存着父节点的地址。
孩子表示法:
节点中储存着左孩子和右孩子。
孩子双亲表示法:
节点中储存着左孩子和右孩子和父节点。
孩子兄弟表示法:
节点中储存着兄弟节点和父节点。
2. 二叉树:
二叉树的每个节点的度都小于等于2。
性质:
1. 若规定根节点为1,则一棵非空二叉树的第i层最多有2^(i-1)(i > 0)个节点。
2. 若规定只有根节点的二叉树的深度为1,则深度为k的二叉树的最大节点数是2^k - 1(k >= 0)。
3. 对任何一个二叉树,若叶节点为n0,度为2的节点为n2,则n1 = n2 + 1。
4. 具有n个节点的完全二叉树的深度为k,2^k = n + 1,求k的时候向上取整。
5. 对于具有n个节点的完全二叉树,如果按从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的节点有:
Ⅰ. 若i > 0,双亲序号:(i - 1)/ 2;i = 0,i为根节点编号,无双亲。
Ⅱ. 若2i + 1< n,左孩子序号:2i + 1,否则无左孩子。
Ⅲ. 若2i + 2 < n,右孩子序号:2i + 2,否则无右孩子。
满二叉树:
一棵二叉树,如果每层的节点数都达到最大值,则这棵树就是完全二叉树。也就是说,如果一棵二叉树的层数为k,且节点总数是2^k - 1,则它就是满二叉树。
完全二叉树:
完全二叉树:
完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树引出来的。对于深度为k的,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中编号从0至n - 1的节点--对应时称之为完全二叉树。
(注意:满二叉树是特殊的完全二叉树。)
2.1 二叉树的遍历:
遍历路径:
前序遍历:
public static void preTraversal(Tree tree){
if (tree == null) return;
System.out.print(tree.val + " ");
preTraversal(tree.left);
preTraversal(tree.right);
}
所以树A的前序遍历结果为:A B D E C F G
中序遍历:
//中序遍历:
public static void inorderTraversal(Tree tree){
if (tree == null) return;
inorderTraversal(tree.left);
System.out.print(tree.val + " ");
inorderTraversal(tree.right);
}
树A的中序遍历结果为:D B E A F C G
后序遍历:
//后序遍历:
public static void posTraversal(Tree tree){
if (tree == null) return;
posTraversal(tree.left);
posTraversal(tree.right);
System.out.print(tree.val + " ");
}
树A的后序遍历结果为:D E B F G C A
2.2 二叉树的基本操作:
2.2.1 获取树中节点的个数: int size(Tree root)
//获取树中节点的个数
public static int size(Tree root){
if (root == null) return 0;
return size(root.left) + size(root.right) + 1;
}
思路:
第一次调用函数时参数的根节点,如果根节点为空,则树中节点的个数为0,所以直接返回0;当根节点不为空时,就返回它左树节点的个数加上右树节点的个数并且加一,因为就算左右树都为空,这棵树也会至少有一个根节点,所以需要加一。
2.2.2 获取叶子节点的个数: int getLeafNodeCount(Tree root)
(叶子节点就是度为0的节点)
//获取叶子节点的个数
public static int getLeafNodeCount(Tree root){
if (root == null) return 0;
if (root.left == null && root.right == null){
return 1;
}
return getLeafNodeCount(root.left) + getLeafNodeCount(root.right);
}
思路:
第一次进入函数传入的参数为根节点,如果根节点为空,则说明该树为空树,有0个叶子节点,返回值为0;如果根节点为叶子节点,则说明该树只有一个节点,也就是说根节点就是叶子节点,返回值为1;若以上情况都不是,则返回左树的叶子节点和右树叶子节点的和。
2.2.3 获取第K层节点的个数: int getKLevelNodeCount(Tree root,int k)
//获取第K层节点的个数
public static int getKLevelNodeCount(Tree root,int k){
if (root == null) return 0;
if (k == 1) return 1;
return getKLevelNodeCount(root.left, k - 1)
+ getKLevelNodeCount(root.right, k - 1);
}
思路:
第一次进入函数传入的是根节点,如果根节点为空,则说明该树为空树,返回值为0;如果根节点不为空,并且为第k层,返回1;如果以上情况都不满足,就继续往下递归,返回左树第k层的节点个数与右树第k层节点的个数。
2.2.4 获取二叉树的高度: int getHeight(Tree root)
//获取二叉树的高度
public static int getHeight(Tree root){
if (root == null) return 0;
int highLeft = getHeight(root.left);
int highRight = getHeight(root.right);
return Math.max(highLeft, highRight) + 1;
}
思路:
第一次进入函数传入的是根节点,如果根节点为空,则说明该树为空树,空树的高度为0,返回值为0;如果这棵树不是空树,则获取其左右树高度,返回值为大的那个数加一,因为如果根节点为叶子节点,那这棵树的高度就为1,所以要加1.
2.2.5 检测值为value的元素是否存在:boolean find(Tree root, char val)
//检测值为value的元素是否存在
public static boolean find(Tree root, char val){
if (root == null) return false;
if (root.val == val) return true;
return find(root.left, val) || find(root.right, val);
}
讲解:
找元素就是在遍历这棵树的同时判断某个节点的元素是否为要找的元素,如果找到了就返回true,如果找不到,就返回false;在进入函数时,如果该树为空树,则不会有任何元素,所以直接返回false即可;如果该节点的元素为要找的元素,立即返回true;如果以上情况都未发生,去该节点的左右树中寻找,只要有一个返回的是真,那么就代表该树中含有val元素,返回true。
2.2.6 层序遍历: void levelOrder(Tree root)
//层序遍历
public static void levelOrder(Tree root){
Queue<Tree> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
Tree cur = queue.poll();
System.out.print(cur.val+ " ");
if (cur.left != null) queue.offer(cur.left);
if (cur.right != null) queue.offer(cur.right);
}
}
讲解:
借用队列先进先出的特点实现二叉树的层序遍历,先让根节点入队,然后让cur接受队列弹出的节点,打印并且如果左右节点不为空就入队,以此为循环,知道队列为空。
2.2.7 判断一棵树是不是完全二叉树: boolean isCompleteTree(Tree root)
//判断一棵树是不是完全二叉树
public static boolean isCompleteTree(Tree root){
Queue<Tree> queue = new LinkedList<>();
queue.offer(root);
Tree cur = root;
while (cur != null){
queue.offer(cur.left);
queue.offer(cur.right);
cur = queue.poll();
}
return isAllNull(queue);
}
public static boolean isAllNull(Queue<Tree> queue){
while (!queue.isEmpty()){
Tree cur = queue.poll();
if (cur != null) return false;
}
return true;
}
讲解:
像层序遍历一样将节点放入队列中并不断弹出,不一样的是会将null入队列,当弹出的节点是null时就结束循环,然后去检查队列中是否还有非空节点的存在,如果有,则为非完全二叉树;反之,则为完全二叉树。