树形结构中最重要的就是二叉树,很多经典的算法与数据结构其实都是通过二叉树演变而来。
二叉树:一种特殊的树形结构,每个节点至多只有两颗子树
满二叉树:除叶子结点外每个结点都有左右两个子结点
完全二叉树:除最后一层之外的结点个数达到最大,并且最后一层结点都连续靠左排列
二叉搜索树:
1.如果它的左子树不为空,则左子树上结点的值都小于根结点。
2.如果它的右子树不为空,则右子树上结点的值都大于根结点。
3.子树同样也要遵循以上两点
使用链表实现一个二叉搜索树,测试其遍历、查找、插入和删除方法
public class Node { private int data; private Node left; private Node right; private Node parent; public Node(int data, Node left, Node right) { this.data = data; this.left = left; this.right = right; } @Override public String toString() { return "Node{" + "data=" + data + '}'; } }
public class BinarySearchTree { /** * 从某个节点开始找到data所在的节点 * * @return */ public static Node find(Node root, int data) { Node cur = root; while (null != cur) { if (data < cur.getData()) { cur = cur.getLeft(); } else if (data > cur.getData()) { cur = cur.getRight(); } else { return cur; } } return null; } /** * 输出节点 * * @param node */ public static void print(Node node) { if (null != node) { System.out.print(node.getData() + ","); } } /** * 前序遍历:根左右,10,6,3,2,1,4,5,8,7,9,15,13,12,11,14,17,16,18,19,20 * * @param root */ public static void pre(Node root) { print(root); if (null != root.getLeft()) { pre(root.getLeft()); } if (null != root.getRight()) { pre(root.getRight()); } } /** * 中序遍历:左根右,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,19,18,20 * * @param root */ public static void mid(Node root) { if (null != root.getLeft()) { mid(root.getLeft()); } print(root); if (null != root.getRight()) { mid(root.getRight()); } } /** * 后序遍历:左右根,1,2,5,4,3,7,9,8,6,11,12,14,13,16,19,20,18,17,15,10 * * @param root */ public static void post(Node root) { if (null != root.getLeft()) { post(root.getLeft()); } if (null != root.getRight()) { post(root.getRight()); } print(root); } /** * 层次遍历:一层一层的取出数据,借用队列先进先出的特性实现 * 10,6,15,3,8,13,17,2,4,7,9,12,14,16,18,1,5,11,19,20 * * @param root */ public static void levelOrder(Node root) { Queue<Node> queue = new ArrayDeque<>(); queue.offer(root); while (!queue.isEmpty()) { Node cur = queue.poll(); System.out.print(cur.getData() + ","); if (null != cur.getLeft()) { queue.offer(cur.getLeft()); } if (null != cur.getRight()) { queue.offer(cur.getRight()); } } } /** * 计算某个节点的高度 * * @param root * @return */ public static int getTreeHight(Node root) { return root == null ? 0 : 1 + Math.max(getTreeHight(root.getLeft()), getTreeHight(root.getRight())); } /** * 插入的过程其实就是查找的过程。插入的位置是叶子节点 * * @param data */ public static void insert(Node root, int data) { if (root.getData() < data) { if (root.getRight() != null) { insert(root.getRight(), data); } else { Node newNode = new Node(data, null, null); newNode.setParent(root); root.setRight(newNode); } } else { if (root.getLeft() != null) { insert(root.getLeft(), data); } else { Node newNode = new Node(data, null, null); newNode.setParent(root); root.setLeft(newNode); } } } /** * 找后继结点,右边第一个左节点为空的节点 * * @param node * @return */ public static Node findSuccessor(Node node) { if (node.getRight() == null) { return node; } Node cur = node.getRight(); Node pre = node.getRight(); while (cur != null) { pre = cur; cur = cur.getLeft(); } return pre; } /** * 删除,分几种情况,左为空,右为空,全为空,全不为空 * * @param root * @param data */ public static Node remove(Node root, int data) { // 找到要删除的节点 Node delNode = find(root, data); if (delNode == null) { System.out.println(data + "节点为空"); return root; } // 左节点和右节点都为空,这是最简单的情况 if (delNode.getLeft() == null && delNode.getRight() == null) { // 如果删除的是根节点,将根节点置空即可 if (delNode == root) { root = null; return root; } // 非根节点,有父节点,判断要删除的节点是父节点的左节点还是右节点,对应置空 else if (delNode.getParent().getData() < delNode.getData()) { delNode.getParent().setRight(null); } else { delNode.getParent().setLeft(null); } } // 最复杂的情况,要删除的节点的左右节点都不为空 else if (delNode.getLeft() != null && delNode.getRight() != null) { // 找到要删除节点的后继结点 Node successor = findSuccessor(delNode); // 将后继结点移动到删除节点的位置 successor.setLeft(delNode.getLeft()); // 后继结点的左节点的父节点赋值为后继结点 successor.getLeft().setParent(successor); // 后继结点的右节点不为空,并且后继结点的父节点不是要删除的节点 if (successor.getRight() != null && successor.getParent() != delNode) { // 后继结点的右节点的父节点指向后继结点的父节点,就相当于把后继结点移走了 successor.getRight().setParent(successor.getParent()); // 后继结点的父节点的左节点,指向后继结点的右节点 successor.getParent().setLeft(successor.getRight()); // 后继结点的右节点指向原删除节点的右节点 successor.setRight(delNode.getRight()); // 原删除节点的右节点现在变成后继结点的右节点,所以要改变其父节点 successor.getRight().setParent(successor); } // 后继结点的右节点为空 else if (successor.getRight() == null) { // 如果后节点的父节点不为空,需要指针重新赋值 if (successor.getParent() != delNode) { successor.getParent().setLeft(null); // 剩下的和上哪面一样了,后继上移至删除节点位置,指针重新指向对应的子节点和父节点 successor.setRight(delNode.getRight()); successor.getRight().setParent(successor); } } // 要删除的节点是根节点,置空其父节点 if (delNode == root) { successor.setParent(null); root = successor; return root; } // 后继结点的父节点指向原删除节点的父节点 successor.setParent(delNode.getParent()); // 原删除节点的子节点指向后继结点 if (delNode.getData() > delNode.getParent().getData()) { delNode.getParent().setRight(successor); } else { delNode.getParent().setLeft(successor); } } // 删除节点的左节点或者右节点其中之一为空 else { // 右节点不为空 if (delNode.getRight() != null) { // 如果删除的是根节点,将右节点上移到根节点即可,而且左节点为空,不需要处理 if (delNode == root) { root = delNode.getRight(); return root; } else { // 非根节点情况 delNode.getRight().setParent(delNode.getParent()); if (delNode.getData() < delNode.getParent().getData()) { delNode.getParent().setLeft(delNode.getRight()); } else { delNode.getParent().setRight(delNode.getRight()); } } } else { if (delNode == root) { root = delNode.getLeft(); return root; } else { delNode.getLeft().setParent(delNode.getParent()); if (delNode.getData() < delNode.getParent().getData()) { delNode.getParent().setLeft(delNode.getLeft()); } else { delNode.getParent().setRight(delNode.getLeft()); } } } } return root; } public static void main(String[] args) {
// 构建一个二叉搜索树,测试上面的方法 Node node1 = new Node(1, null, null); Node node5 = new Node(5, null, null); Node node7 = new Node(7, null, null); Node node9 = new Node(9, null, null); Node node11 = new Node(11, null, null); Node node14 = new Node(14, null, null); Node node16 = new Node(16, null, null); Node node20 = new Node(20, null, null); Node node18 = new Node(18, null, null); Node node2 = new Node(2, node1, null); Node node4 = new Node(4, null, node5); Node node3 = new Node(3, node2, node4); Node node8 = new Node(8, node7, node9); Node node12 = new Node(12, node11, null); Node node13 = new Node(13, node12, node14); Node node19 = new Node(19, node18, node20); Node node17 = new Node(17, node16, node19); Node node6 = new Node(6, node3, node8); Node node15 = new Node(15, node13, node17); Node root = new Node(10, node6, node15); node1.setParent(node2); node5.setParent(node4); node2.setParent(node3); node4.setParent(node3); node7.setParent(node8); node9.setParent(node8); node11.setParent(node12); node12.setParent(node13); node14.setParent(node13); node18.setParent(node19); node20.setParent(node19); node16.setParent(node17); node19.setParent(node17); node3.setParent(node6); node8.setParent(node6); node13.setParent(node15); node17.setParent(node15); node6.setParent(root); node15.setParent(root); // mid(root); // Node node = find(root, 2); // System.out.println(node.toString()); // pre(root); // System.out.println(); // mid(root); // System.out.println(); // post(root); // System.out.println(); // levelOrder(root);
root= remove(root, 15); mid(root); // Node successor = findSuccessor(node4); // System.out.println(successor); } }
输出结果就不展示了......