树:一个结点下面有多个子结点。 二叉树:一个节点下面最多有两个子结点
关于二叉树的一些概念,可以参考:https://www.cnblogs.com/idorax/p/6441043.html
下面主要是总结以下两点:
1.使用数组表示二叉树,例:堆排序(这里演示小根堆);
2.使用链表表示二叉树,例:中序排序。
一、用数组表示二叉树
在此不再深究二叉树的种种定义,用数组表示二叉树里讲的是完全二叉树,下面就简述为二叉树,
!!!程序里面就是一个数组,为了方便理解,我们把它画成了一颗二叉树,大脑里把它想象成一颗二叉树,程序里面是不存在二叉树的,也不存在父结点,子结点这些概念,我们唯一知道的就是数组的长度,父结点的个数,子结点的。
先看下面的例子:
[2,5,4,8,1,7,3,9,6]
注意:第一个元素放什么数都是没有意义的,所以上面这个数组中元素2是不用考虑的。
1.建堆
1.1儿子节点中比较出最大的儿子结点;
1.2最大的儿子结点与父节结比,若比父结点大,就与父结点交换。
下面我们来推算一下父结点个数、父结点索引、儿子结点索引:
数组长度 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | n |
父结点个数 | 0 | 1 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | ... | (n-1)/2 |
父结点索引 | - | 1 | 1 | 1 | 1~2 | 1~2 | 1~3 | 1~3 | 1~4 | ... | 1 ~ (n-1)/2 |
package com.review08.sort;
public class HeapSort {
public static void buildheap(int[] arr) {
int size = arr.length;
//循环所有的父节点(从后向前循环)
for(int i=(size-1)/2; i>=1; i--) {
//1.1先找到最大的儿子
int maxIndex = i*2;//假设最大的儿子是左儿子
//右儿子存在,且比左儿子大
if(maxIndex+1<size && arr[maxIndex+1]>arr[maxIndex]) {
maxIndex ++; //把右儿子标记为最大的儿子
}
//1.2最大的儿子与父亲比较,儿子大的话,就交换
if(arr[maxIndex]>arr[i]) {
int temp = arr[maxIndex];
arr[maxIndex] = arr[i];
arr[i] = temp;
}
}
}
public static void main(String[] args) { //测试
int []arr = {2,5,4,8,1,7,3,9,6};
buildheap(arr);
for (int i=1; i<arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
}
打印结果:
2.将根节点与最后一个结点交换
上面代码中for循环(外)后面加上:
int temp = arr[1];
arr[1] = arr[size-1];
arr[size-1] = temp;
3.重复第1步和第2步,(每一次重复都不考虑从根节点换下来的那个结点,要排序的部分越来越小)
最后排序方法的代码为(只加了while循环和size--):
package com.review08.sort;
public class HeapSort {
public static void buildheap(int[] arr) {
int size = arr.length;
while(size>2) {
//循环所有的父结点(从后向前循环)
for(int i=(size-1)/2; i>=1; i--) {
//1.1先找到最大的儿子
int maxIndex = i*2;//假设最大的儿子是左儿子
//右儿子存在,且比左儿子大
if(maxIndex+1<size && arr[maxIndex+1]>arr[maxIndex]) {
maxIndex ++; //把右儿子标记为最大的儿子
}
//1.2最大的儿子与父亲比较,儿子大的话,就交换
if(arr[maxIndex]>arr[i]) {
int temp = arr[maxIndex];
arr[maxIndex] = arr[i];
arr[i] = temp;
}
}
//2.将根节点与最后一个结点交换
int data = arr[1];
arr[1] = arr[size-1];
arr[size-1] = data;
size --;
}
}
public static void main(String[] args) {
int []arr = {2,5,4,8,1,7,3,9,6};
buildheap(arr);
for (int i=1; i<arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
}
其实堆排序的效率并不高,用的不多,不过我们可以借鉴它的思想,扩展我们的思维。
二、用链表表示二叉树
这里讲的二叉树就是普通的二叉树!!!
和之前写LinkedList一样,先要定义一个结点类:
package com.review07.linkedTree;
public class Node {
private int value;
private Node left;
private Node right;
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
}
2.1、添加结点
下面模拟顺序二叉树添加结点的过程:
添加以下结点:6,9,5,1,3,4,7,8,0
下面用代码模拟添加结点:
package com.review07.linkedTree;
//顺序二叉树(小的在左边,大的在右边)
public class DoubleTree {
Node root;//根节点
//添加是第一个数作为根节点,后面添加的依次与根节点比较
public void add(int value) {
Node newNode = new Node(value);
if(root == null) { //空二叉树
root = newNode;
}else{
Node temp = root; //从根开始比较起
while(true) {
if(value<temp.getValue()) { //小的放左边
if(temp.getLeft() == null) {
temp.setLeft(newNode);
break;
}else {
temp = temp.getLeft();
}
}else { //大的放右边
if(temp.getRight() == null) {
temp.setRight(newNode);
break;
}else {
temp = temp.getRight();
}
}
}
}
}
}
2.2、二叉树的遍历
树的遍历可以分为前序遍历、中序遍历和后序遍历
所谓的前序、中序、后序是相对于根结点而言的。前序表示先遍历根结点,再分别遍历左右两个结点;中序表示先遍历左结点,再遍历根结点,最后遍历右边的结点;后序遍历表示先分别遍历左右两个结点,最后遍历根结点
看下面几个例子,可以比较比较:
从上面两个例子可以看到:(1)前序遍历时根结点始终在第一个被遍历,(2)后序遍历时根结点始终在最后一个被遍历
(3)中序遍历时根结点的位置是不确定的,但是很明显可以看到的是中序遍历后,结点值都是从小到大排列的!
由此我们可以用中序遍历进行排序,不过前提是——要排序的二叉树必须是顺序二叉树!
package com.review07.linkedTree;
//顺序二叉树(小的在左边,大的在右边)
public class DoubleTree {
Node root;//根结点
//添加是第一个数作为根结点,后面添加的依次与根结点比较
public void add(int value) {
Node newNode = new Node(value);
if(root == null) {
root = newNode;
}else{
Node temp = root;
while(true) {
if(value<temp.getValue()) { //小的放左边
if(temp.getLeft() == null) {
temp.setLeft(newNode);
break;
}else {
temp = temp.getLeft();
}
}else { //大的放右边
if(temp.getRight() == null) {
temp.setRight(newNode);
break;
}else {
temp = temp.getRight();
}
}
}
}
}
public void show() {
showNode(root);
}
public void showNode(Node parentnode) { //这里用到了递归!!!
// System.out.print("前序遍历"+parentnode.getValue()+" "); //放在这里表示前序遍历
if(parentnode.getLeft() != null) {
showNode(parentnode.getLeft());
}
System.out.print("中序遍历"+parentnode.getValue()+" "); //放在这里表示中序遍历
if(parentnode.getRight() != null) {
showNode(parentnode.getRight());
}
// System.out.print("后序遍历"+parentnode.getValue()+" "); //放在这里表示后序遍历
}
public static void main(String[] args) {
DoubleTree doubleTree = new DoubleTree();
doubleTree.add(6);
doubleTree.add(4);
doubleTree.add(5);
doubleTree.add(2);
doubleTree.add(8);
doubleTree.add(1);
doubleTree.add(9);
doubleTree.show();
}
}
中序遍历: