一 :为什么需要树这种数据结构
1) 数组存储方式分析
优点: 通过下标方式访问元素, 速度快。 对于有序数组, 还可以使用二分查找法并提高检索的速度。
缺点: 如果要检索某个具体的值, 或者插入值, 会造成整体移动, 效率较低。
2) 链式存储方式分析
优点 : 在一定程度上对数组的存储方式有了优化(比如:插入,删除节点)
缺点 : 在进行检索时,仍然效率较低。
3 ) 树式存储方式分析
能提高数据的存储,读取速率,比如利用二叉排序树等。
二 : 二叉树的前, 中 , 后序遍历。
本质: 只是输出当前节点的顺序不同。
这是二叉树类
class BinaryTree {
public Node root;
public void setRoot(Node node) {
root = node;
}
// 前序遍历
public void preOrder() {
if (root != null) {
root.preOrder();
}
}
// 中序遍历
public void infixOrder() {
if (root != null) {
root.infixOrder();
}
}
// 后序遍历
public void postOrder() {
if (root != null) {
root.postOrder();
}
}
}
这是节点类。
class Node {
public int id;
public String name;
public Node left;
public Node right;
public Node(int id, String name) {
this.id = id;
this.name = name;
}
// 前序遍历
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
// 中序遍历
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
// 后续遍历
public void postOrder() {
if (this.left != null) {
this.left.postOrder();
}
if (this.right != null) {
this.right.postOrder();
}
System.out.println(this);
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
三 : 前,中,后序查找
思路(以前序查找为例):
1) 先判断当前节点的值是否等于要查找的值, 如果相等, 则返回该节点
2) 如果不等, 判断当前节点的左子节点是否为空, 如果不为空, 则递归前序查找。
3) 如果左递归找到节点,则返回节点, 否则继续判断,当前节点的右字节点是否为空,如果不为空,则继续向右递归查找。
// 树
class BinaryTree {
public Node root;
// 前序遍历查找
public Node preFind(int id) {
if (root != null) {
return root.preOrderFind(id);
}
return null;
}
}
// 节点
class Node {
public int id;
public String name;
public Node left;
public Node right;
public Node(int id, String name) {
this.id = id;
this.name = name;
}
// 前序遍历查找
public Node preOrderFind(int id) {
if (this.id == id) {
return this;
}
Node findNode = null;
if (this.left != null) {
findNode = this.left.preOrderFind(id);
}
if (findNode != null) { // 说明已经找到,无需继续 right 查找
return findNode;
}
if (this.right != null) {
findNode = this.right.preOrderFind(id);
}
return findNode;
}
四 : 二叉树删除节点
1) 如何删除的节点为叶子节点, 则删除该节点。
2) 如果删除的节点为非叶子节点, 则删除该子树(到二叉排序树时,在详分类)
步骤如下:
1) 考虑如果树是空树, 如果只有root 一个节点, 则等价将树置空。
2) 我们的二叉树是单向树, 所以我们判断的是 当前节点的子节点 是否需要删除节点, 而不能去判断当前节点是不是要删除的节点。
3) 如果当前节点的左子节点不为空, 并且左子节点就是要删除的节点, 就将 this . left = null, 并且返回。
4) 否则, 如果当前节点的右子节点不为空, 并且右子节点就是要删除的节点, 就将 this . right = null, 并且返回。
5 ) 如果 3, 4 步都没有删除节点, 则需要向左右子树进行递归删除
// 树
class BinaryTree {
public Node root;
public void setRoot(Node node) {
root = node;
}
// 删除节点
public void delete(int id) {
if(root != null) {
if(root.id == id) {
root = null;
} else {
root.deleteNode(id);
}
}else {
System.out.println("空树");
}
}
}
// 节点
class Node {
public int id;
public String name;
public Node left;
public Node right;
public Node(int id, String name) {
this.id = id;
this.name = name;
}
public void deleteNode(int i) {
// 判断左字节点是否是删除的节点
if (this.left != null && this.left.id == id) {
this.left = null;
return;
}
// 判断右字节点
if (this.right != null && this.right.id == id) {
this.right = null;
return;
}
// 如果都不是,则需要递归删除
if (this.left != null) {
this.left.deleteNode(id);
}
if (this.right != null) {
this.right.deleteNode(id);
}
}
}
五:顺序存储二叉树
基本说明: 从数据的存储来看, 数组存储方式 和 树的存储方式可以相互转换, 即数组可以转换成树, 树也可以转换成数组。
特点:
1) 顺序存储二叉树通常只会考虑完全二叉树
2) 第 n 个节点的左子节点为 2 * n + 1
3) 第 n 个节点的右子节点为 2 * n + 2
4) 第 n 个节点的父节点为 (n - 1)/ 2 (n 是数组的下标)
代码实现:
// 测试类
public class ArrBinaryTree {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7};
ArrTree arrTree = new ArrTree();
arrTree.setArr(arr);
arrTree.preList();
}
}
// 顺序存储二叉树
class ArrTree {
public int[] arr;
public void setArr(int[] arr) {
this.arr = arr;
}
public void preList() {
preList(0);
}
public void preList(int index) {
if (arr == null || arr.length == 0) {
System.out.println("空");
}
// 输出当前节点
System.out.println(arr[index]);
// 递归输出左子节点
if ((2 * index + 1) < arr.length) {
preList(2 * index + 1);
}
// 递归输出右字节点
if((2 * index + 2) < arr.length) {
preList(2 * index + 2);
}
}
}
六 : 堆排序
基本介绍:
1)堆排序是利用 堆 这种数据结构而设计的一种排序算法, 堆排序是一种选择排序, 它的最坏、最好、平均时间复杂度都为O(nlogn), 是不稳定的排序。
2)堆是具有以下性质的完全二叉树:每个节点的值大于或者等于其左右孩子的节点,称为大顶堆,每个节点的值小于或者等于其左右孩子节点的值,称为小顶堆。
3 )升序采用大顶堆, 降序采用小顶堆。
基本思想:
1) 将待排序列构造成一个大顶堆。
2) 此时,整个序列的最大值就是堆顶的根节点
3) 将其与末尾元素进行交换, 此时末尾就是最大值。
4) 然后将剩余 n - 1 个元素重新构造成一个堆, 这样会得到 n 个元素的次小值。 如此反复执行, 得到有序序列。
public class HeapSort {
public static void main(String[] args) {
int[] arr = {4, 6, 8, 5, 9};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
// 编写堆排序方法
public static void heapSort(int[] arr) {
// arr.length / 2 - 1 这是第一个非叶子节点的节点下标
for (int i = arr.length / 2 - 1; i >= 0; i--) {
buildHeap(arr, i, arr.length);
}
for (int i = arr.length - 1; i > 0; i--) {
swap(arr, 0, i);
buildHeap(arr, 0 , i);
}
}
// 将数组构建成一个大顶堆
public static void buildHeap(int[] arr, int index, int size) {
int temp = arr[index];
for (int i = 2 * index + 1; i < size; i = i * 2 + 1) {
if ((i + 1) < size && arr[i + 1] > arr[i]) {
i = i + 1;
}
if (arr[i] > temp) {
arr[index] = arr[i];
index = i;
} else {
break;
}
}
arr[index] = temp;
}
// 交换元素
public static void swap(int[] arr, int l, int r) {
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
}
}