文章目录
- 一、基本概念
- 1-1 什么是二叉树?
- 1-2 两种特殊的二叉树
- 1-3 二叉树节点的存储
- 二、二叉树的递归操作
- 三、二叉树的迭代遍历
- 3-1 前序遍历 (prevOrder)
- 3-2 中序遍历 (inOrder)
- 3-3 后序遍历 (postOrder)
- 3-4 层序遍历 (levelOrder)
提示:以下是本篇文章正文内容,Java系列学习将会持续更新
一、基本概念
1-1 什么是二叉树?
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
二叉树的特点:
每个结点最多有两棵子树,即二叉树不存在度大于 2 的结点。
二叉树的子树有左右之分,其子树的次序不能颠倒,因此二叉树是有序树。
1-2 两种特殊的二叉树
满二叉树的性质:
当树的高度为n时,共有(2^n - 1)个节点。
第i层的节点数是:2^(n - 1)
1-3 二叉树节点的存储
二叉树的存储结构分为:顺序存储和类似于链表的链式存储。
二叉树的链式存储是通过一个个的节点引用起来的,常见的表示方式有二叉和三叉表示方式。
//孩子表示法
class Node {
int val; // 数据域
Node 1eft; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
//孩子双亲表示法
class Node {
int val; // 数据域
Node 1eft; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
Node parent; // 当前节点的根节点
}
回到目录…
二、二叉树的递归操作
/**
* 基础二叉树的操作
*/
public class MyBinTree {
/**
* 前序遍历:根左右
* 传入一颗二叉树的根节点,就可以按照先序遍历的方式来输出节点值
*/
public static void prevOrder(TreeNode root) {
//边界条件
if(root == null){
return;
}
//先输出根节点
System.out.print(root.val + " ");
// 按照先序遍历的方式递归访问左子树
prevOrder(root.left);
// 按照先序遍历的方式递归访问右子树
prevOrder(root.right);
}
//中序遍历:左根右
public static void inOrder(TreeNode root) {
if(root == null){
return;
}
inOrder(root.left);
System.out.print(root.val + " ");
inOrder(root.right);
}
//后序遍历:左右根
public static void postOrder(TreeNode root) {
if(root == null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val + " ");
}
/** 返回二叉树的节点数 **/
public static int getNodes(TreeNode root) {
if(root == null){
return 0;
}
return 1 + getNodes(root.left) + getNodes(root.right);
}
/** 返回二叉树的叶子节点数 **/
public static int getLeafNodes(TreeNode root) {
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
return getLeafNodes(root.left) + getLeafNodes(root.right);
}
/** 判断该二叉树是否有val元素 **/
public static boolean contains(TreeNode root, char val) {
if(root == null){
return false;
}
if(root.val == val){
return true;
}
return contains(root.left,val) || contains(root.right,val);
}
/** 返回该二叉树的深度 **/
public static int height(TreeNode root) {
if(root == null){
return 0;
}
return 1 + Math.max(height(root.left),height(root.right));
}
/** 返回该二叉树k层的节点数 **/
public static int getKLevelNodes(TreeNode root,int k) {
if(root == null || k<=0 || k > height(root)){
return 0;
}
if(k == 1){
return 1;
}
return getKLevelNodes(root.left,k-1) + getKLevelNodes(root.right,k-1);
}
}
回到目录…
三、二叉树的迭代遍历
例:
3-1 前序遍历 (prevOrder)
根左右;结果集中,第一个遍历的节点就是根节点
牛客地址
public class Solution {
public int[] preorderTraversal (TreeNode root) {
if(root == null) {
return new int[0];
}
List<Integer> list = new ArrayList<>();
// 使用栈
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.pop();
list.add(cur.val);
// 因为栈先进后出,所以先压入右孩子
if(cur.right != null){
stack.push(cur.right);
}
if(cur.left != null){
stack.push(cur.left);
}
}
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
return arr;
}
}
回到目录…
3-2 中序遍历 (inOrder)
左根右;左子树的遍历结果在根节点的左侧,右子树的遍历结果在根节点的右侧
牛客地址
public class Solution {
public int[] inorderTraversal (TreeNode root) {
if(root == null) {
return new int[0];
}
List<Integer> list = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
while(root != null || !stack.isEmpty()) {
// 每次先找最左节点
while(root != null){
stack.push(root);
root = root.left;
}
TreeNode node = stack.pop();
list.add(node.val);
// 进入右节点
root = node.right;
}
return list.stream().mapToInt(Integer::intValue).toArray();
}
}
回到目录…
3-3 后序遍历 (postOrder)
左右根;结果集中,最后一个遍历的节点就是根节点
牛客地址
public class Solution {
public int[] postorderTraversal (TreeNode root) {
if(root == null) {
return new int[0];
}
List<Integer> list = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
//当前节点
TreeNode cur = root;
//上一个完全处理过(左右根)的节点
TreeNode prev = null;
while(cur != null || !stack.isEmpty()){
//先向左走到尽头
while(cur != null){
stack.push(cur);
cur = cur.left;
}
//此时cur==null,让cur取栈顶元素
cur = stack.pop();
//判断右树是否为null或完全处理过的节点
if(cur.right == null || prev == cur.right){
list.add(cur.val);
prev = cur;
cur = null;
}else{
//此时右树不为空且未完全处理过,则继续处理右树
stack.push(cur);
cur = cur.right;
}
}
return list.stream().mapToInt(Integer::intValue).toArray();
}
}
回到目录…
3-4 层序遍历 (levelOrder)
逐层访问,从左到右
牛客地址
public class Solution {
public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
ArrayList<ArrayList<Integer>> order = new ArrayList<>();
if(root == null){
return order;
}
// 使用队列
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while(!queue.isEmpty()){
// 统计当前层的元素个数
int count = queue.size();
// 建立链表存储当前层的元素
ArrayList<Integer> list = new ArrayList<>();
// 将当前层逐个出队加入链表中,并让它们的孩子入队
for(int i = 0; i < count; i ++) {
TreeNode cur = queue.poll();
list.add(cur.val);
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
order.add(list);
}
return order;
}
}
回到目录…
总结:
提示:这里对文章进行总结:
以上就是今天的学习内容,本文是Java数据结构的学习,认识了二叉树,以及二叉树的相关操作(前序、中序、后序、层序遍历)。之后的学习内容将持续更新!!!