数据结构Java05【二叉树概述、二叉树遍历、堆排序、线索二叉树实现及遍历】

目   录

P34-4.7顺序存储的二叉树的概述

P35-4.8顺序存储的二叉树的遍历

P36-4.9常用排序算法之堆排序

P37-4.10线索二叉树的概述

P38-4.11线索二叉树代码实现

1、TestThreadedBinaryTree.java

2、ThreadedBinaryTree.java

3、ThreadedNode.java

P39-4.12线索二叉树的遍历


P34-4.7顺序存储的二叉树的概述

二叉树数组的转换

顺序存储的二叉树通常情况,只考虑完全二叉树

第n个元素的左子节点是:2*n+1;
第n个元素的右子节点是:2*n+2;
第n个元素的父节点是:(n-1)/2;

P35-4.8顺序存储的二叉树的遍历

将 数组 看成 完全二叉树!以数组的方式遍历二叉树!

因为 根节点的左子节点 也会有 左子节点,所以需要递归。

P36-4.9常用排序算法之堆排序

大顶堆:父节点永远大于子节点!数值大的 在 上面!

大顶堆 是 从大到小,小顶堆 是 从小到大!

升序排序 使用 大顶堆;降序排序 使用 小顶堆。

package demo4;

import java.util.Arrays;

public class HeapSort {

	public static void main(String[] args) {
		int[] arr = new int[] { 9, 6, 8, 7, 0, 1, 10, 4, 2 };
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	public static void heapSort(int[] arr) {
		// 开始位置是最后一个非叶子节点,即最后一个节点的父节点
		int start = (arr.length - 1) / 2;
		// 调整为大顶堆
		for (int i = start; i >= 0; i--) {
			maxHeap(arr, arr.length, i);
		}
		// 先把数组中的第0个和堆中的最后一个数交换位置,再把前面的处理为大顶堆
		for (int i = arr.length - 1; i > 0; i--) {
			int temp = arr[0];
			arr[0] = arr[i];
			arr[i] = temp;
			maxHeap(arr, i, 0);
		}
	}

	public static void maxHeap(int[] arr, int size, int index) {
		// 左子节点
		int leftNode = 2 * index + 1;
		// 右子节点
		int rightNode = 2 * index + 2;
		int max = index;
		// 和两个子节点分别对比,找出最大的节点
		if (leftNode < size && arr[leftNode] > arr[max]) {
			max = leftNode;
		}
		if (rightNode < size && arr[rightNode] > arr[max]) {
			max = rightNode;
		}
		// 交换位置
		if (max != index) {
			int temp = arr[index];
			arr[index] = arr[max];
			arr[max] = temp;
			// 交换位置以后,可能会破坏之前排好的堆,所以,之前的排好的堆需要重新调整
			maxHeap(arr, size, max);
		}
	}

}

P37-4.10线索二叉树的概述

左右指针,如果没有存东西,则 可以 将 该节点的指针 指向 该节点的前一个节点 或 后一个节点。

找一个标记 区分 左(右子树)、前(后节点)。

线索化二叉树时,一个节点的一个节点,叫前驱节点;
线索化二叉树时,一个节点的一个节点,叫后继节点。

P38-4.11线索二叉树代码实现

1、TestThreadedBinaryTree.java

package demo7;

public class TestThreadedBinaryTree {

	public static void main(String[] args) {
		// 创建一颗树
		ThreadedBinaryTree binTree = new ThreadedBinaryTree();
		// 创建一个根节点
		ThreadedNode root = new ThreadedNode(1);
		// 把根节点赋给树
		binTree.setRoot(root);
		// 创建一个左节点
		ThreadedNode rootL = new ThreadedNode(2);
		// 把新创建的节点设置为根节点的子节点
		root.setLeftNode(rootL);
		// 创建一个右节点
		ThreadedNode rootR = new ThreadedNode(3);
		// 把新创建的节点设置为根节点的子节点
		root.setRightNode(rootR);
		// 为第二层的左节点创建两个子节点
		rootL.setLeftNode(new ThreadedNode(4));
		
		ThreadedNode fiveNode = new ThreadedNode(5);
		
		rootL.setRightNode(fiveNode);
		// 为第二层的右节点创建两个子节点
		rootR.setLeftNode(new ThreadedNode(6));
		rootR.setRightNode(new ThreadedNode(7));
		
		// 中序遍历树
		binTree.midShow();
		System.out.println("\n===============");
		
		// 中序线索化二叉树
		binTree.threadNodes();
		ThreadedNode afterFive = fiveNode.rightNode;
		System.out.println(afterFive.value);
	}

}

2、ThreadedBinaryTree.java

package demo7;

public class ThreadedBinaryTree {

	ThreadedNode root;

	// 用于临时存储前驱节点
	// 每次调用threadNodes()函数-临时节点都会改变
	ThreadedNode pre = null;

	// 遍历线索二叉树
	public void threadIterate() {
		// 用于临时存储当前遍历节点
		ThreadedNode node = root;
		while (node != null) {
			// 循环找到最开始的节点
			while (node.leftType == 0) {
				node = node.leftNode;
			}
			// 打印当前节点的值
			System.out.println(node.value);
			// 如果当前节点的右指针指向的是后继节点,
			// 可能后继节点还有后继节点、
			while (node.rightType == 1) {
				node = node.rightNode;
				System.out.println(node.value);
			}
			// 替换遍历的节点
			node = node.rightNode;
		}
	}

	// 设置根节点
	public void setRoot(ThreadedNode root) {
		this.root = root;
	}

	// 中序线索化二叉树-递归遍历-节点
	public void threadNodes() {
		threadNodes(root);
	}

	// 中序线索化二叉树-递归遍历-根据root节点不断递归
	public void threadNodes(ThreadedNode node) {
		// 当前节点如果为null,直接返回
		if (node == null) {
			return;
		}
		// 处理左子树---左子树为空,指针指向前驱节点
		threadNodes(node.leftNode);
		// 处理前驱节点
		if (node.leftNode == null) {
			// 让当前节点的左指针指向前驱节点
			node.leftNode = pre;
			// 改变当前节点左指针的类型
			node.leftType = 1;// 指向前驱节点
		}
		// 处理前驱的右指针,如果前驱节点的右指针是null(没有指向右子树)
		if (pre != null && pre.rightNode == null) {
			// 让前驱节点的右指针指向当前节点
			pre.rightNode = node;
			// 改变前驱节点的右指针类型
			pre.rightType = 1;
		}
		// 每处理一个节点,当前节点是下一个节点的前驱节点
		pre = node;
		// 处理右子树
		threadNodes(node.rightNode);
	}

	// 获取根节点
	public ThreadedNode getRoot() {
		return root;
	}

	// 前序遍历
	public void frontShow() {
		if (root != null) {
			root.frontShow();
		}
	}

	// 中序遍历
	public void midShow() {
		if (root != null) {
			root.midShow();
		}
	}

	// 后序遍历
	public void afterShow() {
		if (root != null) {
			root.afterShow();
		}
	}

	// 前序查找
	public ThreadedNode frontSearch(int i) {
		return root.frontSearch(i);
	}

	// 删除子树
	public void delete(int i) {
		if (root.value == i) {
			root = null;
		} else {
			root.delete(i);
		}
	}

}

3、ThreadedNode.java

package demo7;

public class ThreadedNode {

	int value;// 节点的权
	ThreadedNode leftNode;// 左儿子
	ThreadedNode rightNode;// 右儿子

	// 标识指针类型-默认值为0,指向左右子树
	int leftType;
	int rightType;

	public ThreadedNode(int value) {
		this.value = value;
	}

	// 设置左儿子
	public void setLeftNode(ThreadedNode leftNode) {
		this.leftNode = leftNode;
	}

	// 设置右儿子
	public void setRightNode(ThreadedNode rightNode) {
		this.rightNode = rightNode;
	}

	// 前序遍历
	public void frontShow() {
		// 先遍历当前节点的内容
		System.out.println(value);
		// 左节点
		if (leftNode != null) {
			leftNode.frontShow();
		}
		// 右节点
		if (rightNode != null) {
			rightNode.frontShow();
		}
	}

	// 中序遍历
	public void midShow() {
		// 左子节点
		if (leftNode != null) {
			leftNode.midShow();
		}
		// 当前节点
		System.out.print(value + "、");
		// 右子节点
		if (rightNode != null) {
			rightNode.midShow();
		}
	}

	// 后序遍历
	public void afterShow() {
		// 左子节点
		if (leftNode != null) {
			leftNode.afterShow();
		}
		// 右子节点
		if (rightNode != null) {
			rightNode.afterShow();
		}
		// 当前节点
		System.out.println(value);
	}

	// 前序查找
	public ThreadedNode frontSearch(int i) {
		ThreadedNode target = null;
		// 对比当前节点的值
		if (this.value == i) {
			return this;
			// 当前节点的值不是要查找的节点
		} else {
			// 查找左儿子
			if (leftNode != null) {
				// 有可能可以查到,也可以查不到,查不到的话,target还是一个null
				target = leftNode.frontSearch(i);
			}
			// 如果不为空,说明在左儿子中已经找到
			if (target != null) {
				return target;
			}
			// 查找右儿子
			if (rightNode != null) {
				target = rightNode.frontSearch(i);
			}
		}
		return target;
	}

	// 删除一个子树
	public void delete(int i) {
		ThreadedNode parent = this;
		// 判断左儿子
		if (parent.leftNode != null && parent.leftNode.value == i) {
			parent.leftNode = null;
			return;
		}
		// 判断右儿子
		if (parent.rightNode != null && parent.rightNode.value == i) {
			parent.rightNode = null;
			return;
		}

		// 递归检查并删除左儿子
		parent = leftNode;
		if (parent != null) {
			parent.delete(i);
		}

		// 递归检查并删除右儿子
		parent = rightNode;
		if (parent != null) {
			parent.delete(i);
		}
	}

}

P39-4.12线索二叉树的遍历

不需要 使用 递归!!!

猜你喜欢

转载自blog.csdn.net/weixin_44949135/article/details/106832176