栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

数据结构与算法(十三)——二叉树

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数据结构与算法(十三)——二叉树

二叉树的遍历
  1. 前(根)序遍历:先输出父节点,再遍历左子树和右子树
  2. 中(根)序遍历:先输出左子树,再输出父节点,在输出右子树
  3. 后(根)序遍历:先输出左子树,在输出右子树,最后输出父结点
  4. 三种遍历方式的区别在于根节点的输出时机
代码实现
class BinaryTree{
	
	Node root;
	public BinaryTree(Node root) {
		this.root = root;
	}
	
	
	
	public void preOrder() {
		if(this.root==null) {
			System.out.println("二叉树为空");
		}
		this.root.preOrder();
	}
	
	
	public void infixOrder() {
		if(this.root==null) {
			System.out.println("二叉树为空");
		}
		this.root.infixOrder();
	}
	
	
	public void postOrder() {
		if(this.root==null) {
			System.out.println("二叉树为空");
		}
		this.root.postOrder();
	}
}



class Node{
	int no;
	String name;
	
	Node left;
	
	Node right;
	
	public Node(int no,String name) {
		this.no = no;
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "[no="+no+"tname="+name+"]";
	}
	
	
	public void preOrder() {
		//1.打印当前节点
		System.out.println(this);
		//2.如果左子树不为空,则向左递归遍历
		if(this.left!=null) {
			this.left.preOrder();
		}
		//3.如果有子树不为空,则向右递归遍历
		if(this.right!=null) {
			this.right.preOrder();
		}
	}
	
	
	public void infixOrder() {
		//1.如果左子树不为空,则向左递归遍历
		if(this.left!=null) {
			this.left.infixOrder();
		}
		//2.打印当前节点
		System.out.println(this);
		//3.如果右子树不为空,则向右递归遍历
		if(this.right!=null) {
			this.right.infixOrder();
		}
	}
	
	
	public void postOrder() {
		//1.如果左子树不为空,则向左递归遍历
		if(this.left!=null) {
			this.left.postOrder();
		}
		//2.如果右子树不为空,则向右递归遍历
		if(this.right!=null) {
			this.right.postOrder();
		}
		//3.打印当前节点
		System.out.println(this);
	}
	
	
}
二叉树的查找

二叉树的查找和遍历一样,也分前、中、后序查找;根据比对树中节点的顺序来区分

代码实现
class BinaryTree {
	
	Node root;

	public BinaryTree(Node root) {
		this.root = root;
	}

	
	public void preOrder() {
		if (this.root == null) {
			System.out.println("二叉树为空");
		}
		this.root.preOrder();
	}

	
	public void infixOrder() {
		if (this.root == null) {
			System.out.println("二叉树为空");
		}
		this.root.infixOrder();
	}

	
	public void postOrder() {
		if (this.root == null) {
			System.out.println("二叉树为空");
		}
		this.root.postOrder();
	}
	
	
	public Node preSearch(int no) {
		Node helper = this.root.preSearch(no);
		if(helper==null) {
			throw new RuntimeException("没有找到目标节点");
		}
		return helper;
	}
	
	
	public Node infixSearch(int no) {
		Node helper = this.root.infixSearch(no);
		if(helper==null) {
			throw new RuntimeException("没有找到目标节点");
		}
		return helper;
	}
	
	
	public Node postSearch(int no) {
		Node helper = this.root.postSearch(no);
		if(helper==null) {
			throw new RuntimeException("没有找到目标节点");
		}
		return helper;
	}
	
}


class Node {
	int no;
	String name;
	
	Node left;
	
	Node right;

	public Node(int no, String name) {
		this.no = no;
		this.name = name;
	}

	@Override
	public String toString() {
		return "[no=" + no + "tname=" + name + "]";
	}

	
	public void preOrder() {
		// 1.打印当前节点
		System.out.println(this);
		// 2.如果左子树不为空,则向左递归遍历
		if (this.left != null) {
			this.left.preOrder();
		}
		// 3.如果有子树不为空,则向右递归遍历
		if (this.right != null) {
			this.right.preOrder();
		}
	}

	
	public void infixOrder() {
		// 1.如果左子树不为空,则向左递归遍历
		if (this.left != null) {
			this.left.infixOrder();
		}
		// 2.打印当前节点
		System.out.println(this);
		// 3.如果右子树不为空,则向右递归遍历
		if (this.right != null) {
			this.right.infixOrder();
		}
	}

	
	public void postOrder() {
		// 1.如果左子树不为空,则向左递归遍历
		if (this.left != null) {
			this.left.postOrder();
		}
		// 2.如果右子树不为空,则向右递归遍历
		if (this.right != null) {
			this.right.postOrder();
		}
		// 3.打印当前节点
		System.out.println(this);
	}

	
	public Node preSearch(int no) {
		// 辅助节点,存储找到的目标节点
		Node helper = null;
		// 1.判断当前节点是否是目标节点
		if (this.no == no) {
			helper = this;
		}
		// 2.如果左子树不为空,且目标节点还没有找到,则向左子树递归查找
		if (this.left != null && helper == null) {
			helper = this.left.preSearch(no);
		}
		// 3.如果右子树不为空,且目标节点还没有找到,则向右子树递归查找
		if (right != null && helper == null) {
			helper = this.right.preSearch(no);
		}
		// 返回最终结果
		return helper;
	}

	
	public Node infixSearch(int no) {
		// 辅助节点,存储找到的目标节点
		Node helper = null;
		// 1.如果左子节点不为空,则向左递归查找
		if (this.left != null) {
			helper = this.left.infixSearch(no);
		}
		// 2.如果左子树没找到目标节点,判断当前是否是目标节点
		if (helper == null && this.no == no) {
			helper = this;
		}
		// 3.如果左子树和当前当前节点都没有找到,则向右递归查找
		if (helper == null && this.right != null) {
			helper = this.right.infixSearch(no);
		}
		return helper;
	}

	
	public Node postSearch(int no) {
		// 辅助节点,存储找到的目标节点
		Node helper = null;
		// 1.如果左子节点不为空,则向左递归查找
		if (this.left != null) {
			helper = this.left.postSearch(no);
		}
		// 3.如果左子树和当前当前节点都没有找到,则向右递归查找
		if (helper == null && this.right != null) {
			helper = this.right.postSearch(no);
		}
		// 2.如果左子树没找到目标节点,判断当前是否是目标节点
		if (helper == null && this.no == no) {
			helper = this;
		}
		return helper;
	}

}
二叉树的简单删除
  1. 如果删除的是叶子节点,将该叶子节点的父节点的指针置空
  2. 如果删除的是非叶子节点,删除该节点为根节点的整颗子树
代码实现
class BinaryTree {
	
	Node root;

	public BinaryTree(Node root) {
		this.root = root;
	}

	
	public void preOrder() {
		if (this.root == null) {
			System.out.println("二叉树为空");
			return;
		}
		this.root.preOrder();
	}

	
	public void infixOrder() {
		if (this.root == null) {
			System.out.println("二叉树为空");
			return;
		}
		this.root.infixOrder();
	}

	
	public void postOrder() {
		if (this.root == null) {
			System.out.println("二叉树为空");
			return;
		}
		this.root.postOrder();
	}
	
	
	public Node preSearch(int no) {
		Node helper = this.root.preSearch(no);
		if(helper==null) {
			throw new RuntimeException("没有找到目标节点");
		}
		return helper;
	}
	
	
	public Node infixSearch(int no) {
		Node helper = this.root.infixSearch(no);
		if(helper==null) {
			throw new RuntimeException("没有找到目标节点");
		}
		return helper;
	}
	
	
	public Node postSearch(int no) {
		Node helper = this.root.postSearch(no);
		if(helper==null) {
			throw new RuntimeException("没有找到目标节点");
		}
		return helper;
	}
	
	
	public void delete(int no) {
		if(this.root==null) {
			System.out.println("树为空");
			return;
		}
		//如果待删的是根节点,则根节点置空
		if(root.no==no) {
			root = null;
			return;
		}
		this.root.delete(no);
	}
	
}


class Node {
	int no;
	String name;
	
	Node left;
	
	Node right;

	public Node(int no, String name) {
		this.no = no;
		this.name = name;
	}

	@Override
	public String toString() {
		return "[no=" + no + "tname=" + name + "]";
	}

	
	public void preOrder() {
		// 1.打印当前节点
		System.out.println(this);
		// 2.如果左子树不为空,则向左递归遍历
		if (this.left != null) {
			this.left.preOrder();
		}
		// 3.如果有子树不为空,则向右递归遍历
		if (this.right != null) {
			this.right.preOrder();
		}
	}

	
	public void infixOrder() {
		// 1.如果左子树不为空,则向左递归遍历
		if (this.left != null) {
			this.left.infixOrder();
		}
		// 2.打印当前节点
		System.out.println(this);
		// 3.如果右子树不为空,则向右递归遍历
		if (this.right != null) {
			this.right.infixOrder();
		}
	}

	
	public void postOrder() {
		// 1.如果左子树不为空,则向左递归遍历
		if (this.left != null) {
			this.left.postOrder();
		}
		// 2.如果右子树不为空,则向右递归遍历
		if (this.right != null) {
			this.right.postOrder();
		}
		// 3.打印当前节点
		System.out.println(this);
	}

	
	public Node preSearch(int no) {
		// 辅助节点,存储找到的目标节点
		Node helper = null;
		// 1.判断当前节点是否是目标节点
		if (this.no == no) {
			helper = this;
		}
		// 2.如果左子树不为空,且目标节点还没有找到,则向左子树递归查找
		if (this.left != null && helper == null) {
			helper = this.left.preSearch(no);
		}
		// 3.如果右子树不为空,且目标节点还没有找到,则向右子树递归查找
		if (right != null && helper == null) {
			helper = this.right.preSearch(no);
		}
		// 返回最终结果
		return helper;
	}

	
	public Node infixSearch(int no) {
		// 辅助节点,存储找到的目标节点
		Node helper = null;
		// 1.如果左子节点不为空,则向左递归查找
		if (this.left != null) {
			helper = this.left.infixSearch(no);
		}
		// 2.如果左子树没找到目标节点,判断当前是否是目标节点
		if (helper == null && this.no == no) {
			helper = this;
		}
		// 3.如果左子树和当前当前节点都没有找到,则向右递归查找
		if (helper == null && this.right != null) {
			helper = this.right.infixSearch(no);
		}
		return helper;
	}

	
	public Node postSearch(int no) {
		// 辅助节点,存储找到的目标节点
		Node helper = null;
		// 1.如果左子节点不为空,则向左递归查找
		if (this.left != null) {
			helper = this.left.postSearch(no);
		}
		// 3.如果左子树和当前当前节点都没有找到,则向右递归查找
		if (helper == null && this.right != null) {
			helper = this.right.postSearch(no);
		}
		// 2.如果左子树没找到目标节点,判断当前是否是目标节点
		if (helper == null && this.no == no) {
			helper = this;
		}
		return helper;
	}
	
	
	public void delete(int no) {
		//1.判断左节点是否是目标节点
		if(this.left!=null&&this.left.no==no) {
			this.left=null;
			return;
		}
		//2. 如果左子树不为空,则向左递归删除
		if(this.left!=null) {
			this.left.delete(no);
		}
		//3. 判断右节点是否是目标节点
		if(this.right!=null&&this.right.no==no) {
			this.right=null;
			return;
		}
		//4.如果有子树不为空则向右递归删除
		if(this.right!=null) {
			this.right.delete(no);
		}
	}

}
顺序存储二叉树

从数据存储来看,数组存储方式和树存储方式可以相互转换;顺序存储的数组在遍历的时候使用树的遍历方式

特点
  1. 顺序二叉树通常只考虑完全二叉树
  2. 第n个元素的左子节点为2n+1
  3. 第n个元素的右子节点为2n+2
  4. 第n个元素的父节点为(n-1)/2
代码实现
class ArrayBinaryTree{
	
	private int []arr;
	
	public ArrayBinaryTree(int[] arr) {
		this.arr = arr;
	}
	
	
	public void preOrder() {
		preOrder(0);
	}
	
	
	public void infixOrder() {
		infixOrder(0);
	}
	
	
	public void postOrder() {
		postOrder(0);
	}
	
	
	private void preOrder(int index) {
		//判空
		if(arr==null||arr.length==0) {
			System.out.println("空树");
			return;
		}
		//输出当前元素
		System.out.println(arr[index]);
		//向左递归遍历,2*index+1即左子节点的索引
		if((2*index+1)< arr.length) {
			preOrder(2*index+1);
		}
		//向右递归遍历,2*index+2即右子节点的索引
		if((2*index+2)
			preOrder(2*index+2);
		}
		
	}
	
	
	private void infixOrder(int index) {
		//向左子节点递归遍历
		if((2*index+1)
			infixOrder(2*index+1);
		}
		//输出当前节点
		System.out.println(arr[index]);
		//向右子节点递归遍历
		if((2*index+2)
			infixOrder(2*index+2);
		}
	}
	
	
	private void postOrder(int index) {
		//向左子节点递归遍历
		if((2*index+1)
			postOrder(2*index+1);
		}
		
		//向右子节点递归遍历
		if((2*index+2)
			postOrder(2*index+2);
		}
		//输出当前节点
		System.out.println(arr[index]);
	}
}
线索化二叉树

  1. 如上,在含有n个节点的二叉链表中含有n+1个空指针域(上方3,4,5三个节点的左右指针域),利用二叉链表中的空指针域,存放指向节点在某种遍历次序下的前驱和后继节点的指针(这种附加的指针称作线索),即二叉树的线索化
  2. 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树。根据线索性质的不同,可以分为前序线索二叉树、中序线索二叉树、后序线索二叉树三种
  3. 在遍历中,某个节点前一个遍历的节点称为前驱节点,后一个称为后继节点
示意图


如上,线索化二叉树之后,Node节点的left和right有两种情况:

  1. left指向的是左子树,也有可能是指向前驱节点,比如1节点的left指向左子树,而5节点的left指向前驱节点
  2. right指向的是右子树,也有可能指向后继节点,比如1节点的right指向的是右子树,而5节点的right指向的是后继节点
代码实现(中序线索化)
class ThreadBinaryTree{
	
	Node root;
	
	
	Node pre = null;

	public ThreadBinaryTree(Node root) {
		this.root = root;
	}
	
	
	public void infixThread() {
		infixThreadNodes(root);
	}
	
	
	public void infixThreadNodes(Node node) {
		//判空,节点为null不能线索化
		if(node==null) {
			System.out.println("节点为空,不可线索化");
			return;
		}
		//1. 向左递归线索化
		if(node.left!=null) {
			infixThreadNodes(node.left);
		}
		
		//2. 线索化当前节点
		//如果当前节点的左指针为空,则将左指针指向前驱节点,然后将左指针的类型改为节点
		if(node.left==null) {
			node.left=pre;
			node.leftType = Node.NODE;
		}
		//如果当前节点的前驱节点的右指针为空,则将前驱节点的后继节点指向当前节点,并且将右指针类型改为节点
		if(pre!=null&&pre.right==null) {
			pre.right = node;
			pre.rightType = Node.NODE;
		}
		//每处理一个节点后,让pre指向当前节点,遍历到下一个节点后,pre就是前驱节点了
		pre = node;
		//3. 向右递归线索化
		if(node.right!=null) {
			infixThreadNodes(node.right);
		}
		
	}
}


class Node {
	
	public static final int CHILD_TREE=0;
	
	public static final int NODE=1;
	int no;
	String name;
	
	Node left;
	
	Node right;
	
	int leftType = Node.CHILD_TREE;
	
	int rightType = Node.CHILD_TREE;

	public Node(int no, String name) {
		this.no = no;
		this.name = name;
	}

	@Override
	public String toString() {
		return "[no=" + no + "tname=" + name + "]";
	}

}
中序线索化二叉树的遍历
	
	public void infixThreadList() {
		// 辅助节点帮助遍历
		Node helper = root;
		// 当helper节点为空时退出循环
		while (helper != null) {
			// 向左找,一直找到leftType为node的节点,该节点就是最左侧叶子节点
			while (helper.leftType == Node.CHILD_TREE) {
				helper = helper.left;
			}
			// 输出当前节点
			System.out.println(helper);
			// 一直向右沿着线索化遍历,直到下一个节点的right非线索化
			while (helper.rightType == Node.NODE) {
				helper = helper.right;
				System.out.println(helper);
			}
			// 当前节点的right非线索化,说明该节点不是叶子节点,且左侧的叶子节点已经遍历完了,直接向右移动一位
			helper = helper.right;
		}
	}
前、中、后序线索化二叉树和遍历
class ThreadBinaryTree {
	
	Node root;

	
	Node pre = null;

	public ThreadBinaryTree(Node root) {
		this.root = root;
	}

	
	public void infixThread() {
		infixThreadNodes(root);
	}

	
	public void preThread() {
		preThreadNodes(root);
	}

	
	public void postThread() {
		postThreadNodes(root);
	}

	
	public void infixThreadList() {
		// 辅助节点帮助遍历
		Node helper = root;
		// 当helper节点为空时退出循环
		while (helper != null) {
			// 向左找,一直找到leftType为node的节点,该节点就是最左侧叶子节点
			while (helper.leftType == Node.CHILD_TREE) {
				helper = helper.left;
			}
			// 输出当前节点
			System.out.println(helper);
			// 一直向右沿着线索化遍历,直到下一个节点的right非线索化
			while (helper.rightType == Node.NODE) {
				helper = helper.right;
				System.out.println(helper);
			}
			// 当前节点的right非线索化,说明该节点不是叶子节点,且左侧的叶子节点已经遍历完了,直接向右移动一位
			helper = helper.right;
		}
	}

	
	public void preThreadList() {
		// 辅助节点帮助遍历
		Node helper = root;
		// 当helper节点为空时停止遍历
		while (helper != null) {
			// 当左节点不为叶子节点时,先输出,然后左移
			while (helper.leftType != Node.NODE) {
				System.out.println(helper);
				helper = helper.left;
			}
			// 循环结束后到了最左侧的叶子节点,输出当前节点
			System.out.println(helper);
			// 根据线索化,依次遍历节点
			while (helper.rightType == Node.NODE) {
				helper = helper.right;
				System.out.println(helper);
			}
			// 当当前节点的right非线索化
			helper = helper.right;
		}
	}

	
	public void postThreadList() {
		postListNode(root);
	}
	
	
	private void postListNode(Node node) {
		//向左递归直到叶子节点
		if(node.leftType==Node.CHILD_TREE) {
			postListNode(node.left);
		}
		//向右递归直到叶子节点
		if(node.rightType==Node.CHILD_TREE) {
			postListNode(node.right);
		}
		//输出当前节点
		System.out.println(node);
		
	}

	
	public void infixThreadNodes(Node node) {
		// 判空,节点为null不能线索化
		if (node == null) {
			return;
		}
		// 1. 向左递归线索化
		infixThreadNodes(node.left);

		// 2. 线索化当前节点
		// 如果当前节点的左指针为空,则将左指针指向前驱节点,然后将左指针的类型改为节点
		if (node.left == null) {
			node.left = pre;
			node.leftType = Node.NODE;
		}
		// 如果当前节点的前驱节点的右指针为空,则将前驱节点的后继节点指向当前节点,并且将右指针类型改为节点
		if (pre != null && pre.right == null) {
			pre.right = node;
			pre.rightType = Node.NODE;
		}
		// 每处理一个节点后,让pre指向当前节点,遍历到下一个节点后,pre就是前驱节点了
		pre = node;
		// 3. 向右递归线索化
		infixThreadNodes(node.right);
	}

	
	public void preThreadNodes(Node node) {
		// 判空
		if (node == null) {
			return;
		}
		//标志量,如果当前进行了左指针的线索化则不在向左递归
		boolean toLeft = true;
		//标志量,如果当前进行了右指针的线索化则不再向右递归
		boolean toRight = true;
		// 1.将当前节点线索化
		if (node.left == null) {
			node.left = pre;
			node.leftType = Node.NODE;
			toLeft = false;
		}
		if (pre != null && pre.right == null) {
			pre.right = node;
			pre.rightType = Node.NODE;
			toRight = false;
		}
		pre = node;
		// 2.向左递归线索化
		if (toLeft) {
			preThreadNodes(node.left);
		}
		// 3.向右递归线索化
		if(toRight) {
			preThreadNodes(node.right);
		}
		
	}

	
	public void postThreadNodes(Node node) {
		// 判空
		if (node == null) {
			return;
		}
		// 1.向左递归线索化
		postThreadNodes(node.left);

		// 2.向右递归线索化
		postThreadNodes(node.right);
		// 3.将当前节点线索化
		if (node.left == null) {
			node.left = pre;
			node.leftType = Node.NODE;
		}
		if (pre != null && pre.right == null) {
			pre.right = node;
			pre.rightType = Node.NODE;
		}
		pre = node;
	}
}


class Node {
	
	public static final int CHILD_TREE = 0;
	
	public static final int NODE = 1;
	int no;
	String name;
	
	Node left;
	
	Node right;
	
	int leftType = Node.CHILD_TREE;
	
	int rightType = Node.CHILD_TREE;

	public Node(int no, String name) {
		this.no = no;
		this.name = name;
	}

	@Override
	public String toString() {
		return "[no=" + no + "tname=" + name + "]";
	}

}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/831942.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号