二叉树的定义,它的子节点最多有两个(左节点,右节点)。二叉搜索树是二叉树中一个特殊的存在,它规定所有左侧节点的值都小于本节点,所有右侧节点的值都大于本节点。二叉搜索树对于关键值的查找非常快,执行效率是(lg N -- N)。


节点类



class Node {
// 存放的数据
private int key;
// 左子节点
private Node left;
// 右子节点
private Node right;

public Node(int key) {
this.key = key;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("key=").append(key);
sb.append('}');
return sb.toString();
}
}


插入节点



public void insert(int key) {
// 创建一个新节点
Node node = new Node(key);
Node parent = null;
Node temp = root;
// 标志 新节点插入 父节点的左侧还是右侧
boolean isLeft = true;

// 如果temp为空,表示新节点就插入在这个位置
while (temp != null) {
parent = temp;
// 如果小于temp.key,表示从左侧寻找插入位置
if (key < temp.key) {
isLeft = true;
temp = temp.left;
}
else {
isLeft = false;
temp = temp.right;
}
}

// 如果parent为空,表示root为空,新插入的节点作为root节点
if (parent == null) root = node;
else {
if (isLeft) parent.left = node;
else parent.right = node;
}
}


主要步骤:

  • 创建一个新节点node。
  • 创建三个局部变量parent、temp、isLeft。

因为我们的节点类Node没有指向父节点的引用,所以这个要有个parent局部变量,temp变量是用来遍历整个树的,isLeft变量是用标志当前temp是左节点还是右节点。




  • 通过while循环找到新节点就插入的位置。
  • 如果parent为空,表示root为空,新插入的节点作为root节点
  • 如果parent不为空,那么根据isLeft的值,来决定新节点插入在父节点的左边还是右边。


删除节点


对于二叉树来说,删除一个节点还是比较麻烦的。第一步我们还是有找到这个节点:



public Node remove(int key) {
Node parent = null;
Node temp = root;
boolean isLeft = true;
while (temp != null) {
// 相等,表示找到要删除的节点了
if (temp.key == key) {
removeNode(parent, temp, isLeft);
return temp;
}
parent = temp;
if (key < temp.key) {
isLeft = true;
temp = temp.left;
}
else {
isLeft = false;
temp = temp.right;
}
}
return null;
}


遍历树,找到与要删除的节点,然后调用removeNode方法进行删除操作。

要删除二叉树的一个节点,分三种情况:

  • 被删除节点没有子节点,也就是说它是一个子叶节点。那就很简单了,直接删除这个节点,就是将它的父节点对应它的引用置位null就行了。
  • 被删除节点有一个子节点。

如果被删除节点是父节点的左子节点,那么它的子节点一定是比父节点小的,直接使用被删除节点子节点取代删除节点就可以了。如果被删除节点是父节点的右子节点,流程一样。

  • 被删除节点有两个子节点。

这个时候就比较麻烦了,你删除了本节点后,完全不知道,用哪个子节点取代自己的位置。

根据搜索二叉树的定义规则,取代被删除节点的节点,也必须满足比被删除节点左节点的值都大,比被删除节点右边的值都小。这个就是它的后继节点,问题就变为寻找它的后继结点。

它的后继节点其实就是它右子节点的最小值,也就是右测最左边的值。



private Node findSuccessorNode(Node rightNode) {
// 如果rightNode的左子节点为空,那么它就是最小节点,直接返回它。
if (rightNode.left == null) return rightNode;
Node parent = rightNode;
Node successor = parent.left;
while (successor.left != null) {
parent = successor;
successor = successor.left;
}
// successor表示最小节点,它要取代被删除的节点,
//所以它的右节点移动到父节点左侧,它的右节点指向rightNode。
parent.left = successor.right;
successor.right = rightNode;
return successor;
}


这个方法就是寻找右侧最左边的值,即后继节点。



private void removeNode(Node parent, Node delNode, boolean isLeft) {
// 表示准备添加父节点的新子节点,取代被删除的节点位置
Node newNode;
// 如果被删除的节点 左右子节点都为空,那么就直接删除,所以这里的newNode为空,表示不会向父节点添加任何子节点
if (delNode.left == null && delNode.right == null) {
newNode = null;
} else if (delNode.left == null) {
// 如果被删除的节点 左子节点为空,那么 右子节点就取代删除的节点位置
newNode = delNode.right;
} else if (delNode.right == null) {
// 如果被删除的节点 右子节点为空,那么 左子节点就取代删除的节点位置
newNode = delNode.left;
} else {
// 如果左右子节点都不为空,那么就从右侧寻找后继节点,即最小值节点,用它取代删除的节点位置
newNode = findSuccessorNode(delNode.right);
newNode.left = delNode.left;
}

// parent为空,表示删除的是root节点,当然你也可以用delNode == root来判断。
if (parent == null) {
root = newNode;
} else {
if (isLeft) parent.left = newNode;
else parent.right = newNode;
}
}


newNode节点用来取代被删除节点,具体的判断逻辑与前面叙述的一样。

用递归来遍历二叉树

前序遍历



public void preOrder(StringBuilder sb, Node node) {
if (node == null) return;
sb.append(node.key+" ");
preOrder(sb, node.left);
preOrder(sb, node.right);
}


中序遍历



public void inOrder(StringBuilder sb, Node node) {
if (node == null) return;
inOrder(sb, node.left);
sb.append(node.key+" ");
inOrder(sb, node.right);
}


后序遍历



public void postOrder(StringBuilder sb, Node node) {
if (node == null) return;

postOrder(sb, node.left);
postOrder(sb, node.right);
sb.append(node.key+" ");
}


用栈来遍历二叉树

前序遍历



private void preOrderByStack(StringBuilder sb, Node node) {
Stack<Node> stack = new Stack();
while (node != null || !stack.isEmpty()) {
if (node != null) {
sb.append(node.key+" ");
stack.push(node);
node = node.left;
} else {
Node temp = stack.pop();
node = temp.right;
}
}
}


中序遍历



private void inOrderByStack(StringBuilder sb, Node node) {
Stack<Node> stack = new Stack();
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
Node temp = stack.pop();
sb.append(temp.key+" ");
node = temp.right;
}
}
}


后序遍历



private void postOrderByStack(StringBuilder sb, Node node) {
if (node == null) return;
Stack<Node> stack = new Stack();
Stack<Node> tempStack = new Stack();
stack.push(node);
while (!stack.isEmpty()) {
node = stack.pop();
tempStack.push(node);

if (node.left != null) stack.push(node.left);
if (node.right != null) stack.push(node.right);
}

while (!tempStack.isEmpty()) {
node = tempStack.pop();
sb.append(node.key+" ");
}
}


完整源码



import tree.Stack;

import java.util.Random;

class SpendTime {
private long start = -1;
public SpendTime() {
start = System.currentTimeMillis();
}

public double getSpendSeconds() {
long times = System.currentTimeMillis() - start;
return times / 1000d;
}

public void priteTime(){
long times = System.currentTimeMillis() - start;
System.out.println();
System.out.println("花费了"+(times / 1000d)+"秒"+(times % 1000)+"毫秒");
}
}

public class TwoTree {

static class Node {
// 存放的数据
private int key;
// 左子节点
private Node left;
// 右子节点
private Node right;

public Node(int key) {
this.key = key;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("key=").append(key);
sb.append('}');
return sb.toString();
}
}

// 树的根节点
private Node root;

public void insert(int key) {
// 创建一个新节点
Node node = new Node(key);
Node parent = null;
Node temp = root;
// 标志 新节点插入 父节点的左侧还是右侧
boolean isLeft = true;

// 如果temp为空,表示新节点就插入在这个位置
while (temp != null) {
parent = temp;
// 如果小于temp.key,表示从左侧寻找插入位置
if (key < temp.key) {
isLeft = true;
temp = temp.left;
}
else {
isLeft = false;
temp = temp.right;
}
}

// 如果parent为空,表示root为空,新插入的节点作为root节点
if (parent == null) root = node;
else {
if (isLeft) parent.left = node;
else parent.right = node;
}
}

public Node remove(int key) {
Node parent = null;
Node temp = root;
boolean isLeft = true;
while (temp != null) {
// 相等,表示找到要删除的节点了
if (temp.key == key) {
removeNode(parent, temp, isLeft);
return temp;
}
parent = temp;
if (key < temp.key) {
isLeft = true;
temp = temp.left;
}
else {
isLeft = false;
temp = temp.right;
}
}
return null;
}

private void removeNode(Node parent, Node delNode, boolean isLeft) {
// 表示准备添加父节点的新子节点,取代被删除的节点位置
Node newNode;
// 如果被删除的节点 左右子节点都为空,那么就直接删除,所以这里的newNode为空,表示不会向父节点添加任何子节点
if (delNode.left == null && delNode.right == null) {
newNode = null;
} else if (delNode.left == null) {
// 如果被删除的节点 左子节点为空,那么 右子节点就取代删除的节点位置
newNode = delNode.right;
} else if (delNode.right == null) {
// 如果被删除的节点 右子节点为空,那么 左子节点就取代删除的节点位置
newNode = delNode.left;
} else {
// 如果左右子节点都不为空,那么就从右侧寻找后继节点,即最小值节点,用它取代删除的节点位置
newNode = findSuccessorNode(delNode.right);
newNode.left = delNode.left;
}

// parent为空,表示删除的是root节点,当然你也可以用delNode == root来判断。
if (parent == null) {
root = newNode;
} else {
if (isLeft) parent.left = newNode;
else parent.right = newNode;
}
}

private Node findSuccessorNode(Node rightNode) {
// 如果rightNode的左子节点为空,那么它就是最小节点,直接返回它。
if (rightNode.left == null) return rightNode;
Node parent = rightNode;
Node successor = parent.left;
while (successor.left != null) {
parent = successor;
successor = successor.left;
}
// successor表示最小节点,它要取代被删除的节点,所以它的右节点移动到父节点左侧,它的右节点指向rightNode。
parent.left = successor.right;
successor.right = rightNode;
return successor;
}


public void display() {
StringBuilder sb = new StringBuilder();
preOrder(sb, root);
System.out.println("前序:"+sb.toString());

sb.delete(0, sb.length());
preOrderByStack(sb, root);
System.out.println("前序:"+sb.toString());

System.out.println("-------------------------");
sb.delete(0, sb.length());
inOrder(sb, root);
System.out.println("中序:"+sb.toString());

sb.delete(0, sb.length());
inOrderByStack(sb, root);
System.out.println("中序:"+sb.toString());

System.out.println("-------------------------");
sb.delete(0, sb.length());
postOrder(sb, root);
System.out.println("后序:"+sb.toString());
sb.delete(0, sb.length());
postOrderByStack(sb, root);
System.out.println("后序:"+sb.toString());
}


public void preOrder(StringBuilder sb, Node node) {
if (node == null) return;
sb.append(node.key+" ");
preOrder(sb, node.left);
preOrder(sb, node.right);
}

private void preOrderByStack(StringBuilder sb, Node node) {
Stack<Node> stack = new Stack();
while (node != null || !stack.isEmpty()) {
if (node != null) {
sb.append(node.key+" ");
stack.push(node);
node = node.left;
} else {
Node temp = stack.pop();
node = temp.right;
}
}
}

public void inOrder(StringBuilder sb, Node node) {
if (node == null) return;
inOrder(sb, node.left);
sb.append(node.key+" ");
inOrder(sb, node.right);
}

private void inOrderByStack(StringBuilder sb, Node node) {
Stack<Node> stack = new Stack();
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
Node temp = stack.pop();
sb.append(temp.key+" ");
node = temp.right;
}
}
}

public void postOrder(StringBuilder sb, Node node) {
if (node == null) return;

postOrder(sb, node.left);
postOrder(sb, node.right);
sb.append(node.key+" ");
}

private void postOrderByStack(StringBuilder sb, Node node) {
if (node == null) return;
Stack<Node> stack = new Stack();
Stack<Node> tempStack = new Stack();
stack.push(node);
while (!stack.isEmpty()) {
node = stack.pop();
tempStack.push(node);

if (node.left != null) stack.push(node.left);
if (node.right != null) stack.push(node.right);
}

while (!tempStack.isEmpty()) {
node = tempStack.pop();
sb.append(node.key+" ");
}
}

private static int[] insertData(TwoTree twoTree, int n) {
Random random = new Random();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
// twoTree.insert(random.nextInt(1000));
twoTree.insert(i);
}
return arr;
}

public static void main(String[] args){
TwoTree twoTree = new TwoTree();

SpendTime spendTime = new SpendTime();


twoTree.insert(6);
twoTree.insert(7);
twoTree.insert(2);
twoTree.insert(3);
// twoTree.remove(7);
twoTree.insert(8);
twoTree.insert(1);
twoTree.insert(9);
twoTree.insert(4);
// twoTree.remove(2);
twoTree.insert(5);
twoTree.display();
spendTime.priteTime();

}
}