减治法在查找算法中的应用
二叉查找树的查找与插入:
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根节点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树。
对于二叉查找树这里我们介绍查找、插入和删除操作:
这些操作会将问题的规模变成一个更小的二叉树,也运用到了减治法的思想。
查找思路:如果树为空,直接返回,查找失败。反之,将查找值key与根root的值比较,如果相等,则root即为所找;若key < root.v,则继续在左子树查找;若key > root.v,则继续在右子树查找。
public static Node find(int key) {if (root == null) {System.out.println("The tree is empty!");return null;}Node cur = root;while (cur.v != key) {if (key < cur.v) {cur = cur.l;}else {cur = cur.r;}if (cur == null) {return null;}}return cur;
插入思路:基本思想和查找操作类似,如果为空树,直接返回,插入节点即为根节点;否则,比较根节点的值与插入节点的值。注意用parent记录遍历到最后的cur值。
public static Node find(int key) {if (root == null) {System.out.println("The tree is empty!");return null;}Node cur = root;while (cur.v != key) {if (key < cur.v) {cur = cur.l;}else {cur = cur.r;}if (cur == null) {return null;}}return cur;
}
删除思路:删除操作是二叉树操作中最复杂的问题。对于二叉树的删除有一下三种情况:
(1)删除叶子结点。可直接删除,不会影响其他
(2)删除结点有且只有一侧孩子结点。孩子结点覆盖待删除结点,删除孩子结点
(3)删除结点下既有左孩子,又有右孩子。在待删除结点的右孩子下,找到v值最小的,用这个结点覆盖要删除的结点。(这里是因为中序遍历结点的后继结点一定是在右子树中v值最小的结点)
public static boolean delete(int key) {Node cur = root;Node parent = root;boolean hasLeft = true;while (cur != null && cur.v != key) {parent = cur;if (key < cur.v) {cur = cur.l;hasLeft = true;} else {cur = cur.r;hasLeft = false;}}if (cur == null) {return false;}if (cur.l == null && cur.r == null) {/*** 要删除的节点为叶子节点,直接删除* */if (cur == root) {root = null;}if (hasLeft) {parent.l = null;} else {parent.r = null;}} else if (cur.r == null) {/*** 要删除的节点只有左孩子* */if (cur == root) {root = cur.l;}if (hasLeft) {parent.l = cur.l;} else {parent.r = cur.l;}} else if (cur.l == null) {/*** 要删除的节点只有右孩子* */if (cur == root) {root = cur.r;}if (hasLeft) {parent.l = cur.r;}else {parent.r = cur.r;}} else {/*** 要删除的节点既有左孩子又有右孩子* 思路:用待删除节点右子树中的v值最小的结点来替代要删除的节点,然后删除右子树中结点* 右子树中v值最小的节点一定没有左子树,所以删除的这个结点一定是属于叶子节点或只有右子树的节点* */Node directPostNode = getPost(cur);cur.v = directPostNode.v;}return true;
}
private static Node getPost(Node delNode) {Node parent = delNode;Node dir = delNode;Node cur = delNode.r;while (cur != null) {parent = dir;dir = cur;cur = cur.l;}if (dir != delNode.r) {//从树中删除此直接后继节点parent.l = dir.r;dir.r = null;}return dir;
}
完整代码如下:
class Node {int v;Node l;Node r;public Node(int v) {this.v = v;}
}
public class Main {public static Node root;public static Node find(int key) {if (root == null) {System.out.println("The tree is empty!");return null;}Node cur = root;while (cur.v != key) {if (key < cur.v) {cur = cur.l;}else {cur = cur.r;}if (cur == null) {return null;}}return cur;}public static void insert(Node node) {if (root == null) {root = node;return;}Node cur = root;Node parent = root;boolean hasLeft = true;while (cur != null) {parent = cur;if (node.v > cur.v) {cur = cur.r;hasLeft = true;} else {cur = cur.l;hasLeft = false;}}if (hasLeft) {parent.l = node;} else {parent.r = node;}}public static boolean delete(int key) {Node cur = root;Node parent = root;boolean hasLeft = true;while (cur != null && cur.v != key) {parent = cur;if (key < cur.v) {cur = cur.l;hasLeft = true;} else {cur = cur.r;hasLeft = false;}}if (cur == null) {return false;}if (cur.l == null && cur.r == null) {/*** 要删除的节点为叶子节点,直接删除* */if (cur == root) {root = null;}if (hasLeft) {parent.l = null;} else {parent.r = null;}} else if (cur.r == null) {/*** 要删除的节点只有左孩子* */if (cur == root) {root = cur.l;}if (hasLeft) {parent.l = cur.l;} else {parent.r = cur.l;}} else if (cur.l == null) {/*** 要删除的节点只有右孩子* */if (cur == root) {root = cur.r;}if (hasLeft) {parent.l = cur.r;}else {parent.r = cur.r;}} else {/*** 要删除的节点既有左孩子又有右孩子* 思路:用待删除节点右子树中的v值最小的结点来替代要删除的节点,然后删除右子树中结点* 右子树中v值最小的节点一定没有左子树,所以删除的这个结点一定是属于叶子节点或只有右子树的节点* */Node directPostNode = getPost(cur);cur.v = directPostNode.v;}return true;}private static Node getPost(Node delNode) {Node parent = delNode;Node dir = delNode;Node cur = delNode.r;while (cur != null) {parent = dir;dir = cur;cur = cur.l;}if (dir != delNode.r) {//从树中删除此直接后继节点parent.l = dir.r;dir.r = null;}return dir;}public static void preorder(Node node) {System.out.print(node.v + " ");if (node.l != null)preorder(node.l);if (node.r != null)preorder(node.r);}public static void main(String[] args) {/*** 插入* */insert(new Node(20));insert(new Node(10));insert(new Node(30));/*** 查找* */System.out.println(find(20));/*** 删除* */delete(20);/*** 前序遍历* */preorder(root);}
}