Java实现Treap树的示例代码
作者:lolxxs
本文主要介绍了Java实现Treap树的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
Treap树
Treap树是平衡二叉搜索树的一种实现方式,但它不是完全平衡的。平衡二叉搜索树的实现方式还有AVL树、红黑树、替罪羊树、伸展树
数据结构
Treap树的节点除了有二叉搜索树的必须有的值,还有一个随机生成的优先级priority,供构造小顶堆使用,小顶堆的特性就是父节点、左右子结点中永远是父节点的优先级最小,最多和子结点的相等。而大顶堆则是父节点的最大。堆中左右子结点的优先级并没有特定要求
class TreeNode { int value; int priority; TreeNode left; TreeNode right; public TreeNode(int value, int priority) { this.value = value; this.priority = priority; } }
遍历
Treap树虽然是不完全平衡树,但是其完全满足二叉搜索树的特征,即中序遍历得到的是有序数组
public void printTree(TreeNode root) { if (root != null) { printTree(root.left); System.out.println(root.value); printTree(root.right); } }
查询
Treap树满足二叉搜索树的特征,则直接根据其特征查询
// 查询 // 根据二叉搜索树性质查询 public TreeNode query(TreeNode root, int value) { //这里的root才是真root,上面的方法只是局部变量 //所以不能在查询中改变根节点 TreeNode temp = root; while (temp != null) { if (temp.value > value) { temp = temp.left; } else if (temp.value < value) { temp = temp.right; } else { return temp; } } return null; }
增加
步骤
- 按照二叉搜索树的插入方式,将节点插入到叶子节点,如果在查找的过程找到要插入的值,则不会进行插入,具有去重效果
- 插入节点后,根据随机生成的priority优先级,按照小顶堆,即priority较小的成为父节点,来进行左旋右旋
//增加 // 1.按照二叉查找树的插入方式,将节点插入到树叶中 // 2.再根据priority优先级的小顶堆性质进行左旋右旋 public TreeNode insert(int value, TreeNode root) { // 如果父节点为空,则创建一个父节点并返回 // 第一次父节点为根节点 if (root == null) { return new TreeNode(value, random.nextInt()); } // 如果要根节点的值大于要插入结点的值,则应该插入到根节点的左边 if (root.value > value) { // 递归进行插入,一直递归到叶子节点才会插入 // 如果递归到一个相等的节点,则不会创建一个新节点,会直接返回 root.left = insert(value, root.left); // 插入完成后,根据堆的优先级进行旋转操作 // 左子结点的优先级值小于根结点的优先级, // 根据小顶堆的规则,需要进行右旋操作 if (root.left.priority < root.priority) { // 传入root根结点,返回的是左子结点, // 但此时左子结点已经旋转成为根结点所以赋值给根结点 root = rightRotate(root); } } // 如果根节点的值小于要插入结点的值大于,则应该插入到根节点的右边 else if (root.value < value) { // 递归插入 root.right = insert(value, root.right); // 右子结点的优先级值小于根结点的优先级, // 根据小顶堆的规则,需要进行左旋操作 if (root.right.priority < root.priority) { root = leftRotate(root); } } // 如果已经有该值,则无须插入,什么都不动 else { } // 返回根结点 return root; }
删除
步骤
- 按照二叉搜索树的特点,先找到对应的节点
- 若该结点为叶子结点,则直接删除,若该结点为非叶子节点, 则进行相应的旋转,直到该结点为叶子节点,然后进行删除
//删除、 // 1.根据二叉搜索树的性质找到相应的结点 // 2.若该结点为叶子结点,则直接删除,若该结点为非叶子节点, // 则进行相应的旋转,直到该结点为叶子节点,然后进行删除。 public TreeNode delete(int value, TreeNode root) { // 当树不为空才进行删除 if (root != null) { // 先进行查找 // 往左找 if (root.value > value) { // 因为可能找到了后会进行左旋右旋, // 所以其实左子结点会改变 root.left = delete(value, root.left); } // 往右找 else if (root.value < value) { root.right = delete(value, root.right); } //找到了 else { // 首先找到这里,root已经变为目标结点,如果root是叶子结点删去即可 if (root.left == null && root.right == null) { // 返回当前节点,即删除后的样子 null // 会递归到其父节点的root.right = delete(value, root.right); // 即指向了root.right = null 或 root.left = null,即删除了我们要删除的节点 return null; } else if (root.left != null && root.right != null) { // 如果root左右子结点健在 // 此时就是想把目标结点旋转到底层去, // 然后需要选择一个优先级值比较小的结点放在目标结点位置 // 如果左子结点优先级较小 if (root.left.priority < root.right. priority) { // 旋转root已经变为左子结点,原来的根结点变为右子节点 root = rightRotate(root); // 去找那被换下去的节点,将它删除掉 root.right = delete(value, root.right); } else { root = leftRotate(root); // 去找那被换下去的节点,将它删除掉 root.left = delete(value, root.left); } } else if (root.left != null) { // 没有右子节点,只能右旋了 root = rightRotate(root); // 去找那被换下去的节点,将它删除掉 root.right = delete(value, root.right); } else if (root.right != null) { // 没有左子节点,只能左旋了 root = leftRotate(root); // 去找那被换下去的节点,将它删除掉 root.left = delete(value, root.left); } } } return root; }
完整代码
public class Treap { // 优先级随机数发生器 private static final Random random = new Random(); //增加 // 1.按照二叉查找树的插入方式,将节点插入到树叶中 // 2.再根据priority优先级的小顶堆性质进行左旋右旋 public TreeNode insert(int value, TreeNode root) { // 如果父节点为空,则创建一个父节点并返回 // 第一次父节点为根节点 if (root == null) { return new TreeNode(value, random.nextInt()); } // 如果要根节点的值大于要插入结点的值,则应该插入到根节点的左边 if (root.value > value) { // 递归进行插入,一直递归到叶子节点才会插入 // 如果递归到一个相等的节点,则不会创建一个新节点,会直接返回 root.left = insert(value, root.left); // 插入完成后,根据堆的优先级进行旋转操作 // 左子结点的优先级值小于根结点的优先级, // 根据小顶堆的规则,需要进行右旋操作 if (root.left.priority < root.priority) { // 传入root根结点,返回的是左子结点, // 但此时左子结点已经旋转成为根结点所以赋值给根结点 root = rightRotate(root); } } // 如果根节点的值小于要插入结点的值大于,则应该插入到根节点的右边 else if (root.value < value) { // 递归插入 root.right = insert(value, root.right); // 右子结点的优先级值小于根结点的优先级, // 根据小顶堆的规则,需要进行左旋操作 if (root.right.priority < root.priority) { root = leftRotate(root); } } // 如果已经有该值,则无须插入,什么都不动 else { } // 返回根结点 return root; } //删除、 // 1.根据二叉搜索树的性质找到相应的结点 // 2.若该结点为叶子结点,则直接删除,若该结点为非叶子节点, // 则进行相应的旋转,直到该结点为叶子节点,然后进行删除。 public TreeNode delete(int value, TreeNode root) { // 当树不为空才进行删除 if (root != null) { // 先进行查找 // 往左找 if (root.value > value) { // 因为可能找到了后会进行左旋右旋, // 所以其实左子结点会改变 root.left = delete(value, root.left); } // 往右找 else if (root.value < value) { root.right = delete(value, root.right); } //找到了 else { // 首先找到这里,root已经变为目标结点,如果root是叶子结点删去即可 if (root.left == null && root.right == null) { // 返回当前节点,即删除后的样子 null // 会递归到其父节点的root.right = delete(value, root.right); // 即指向了root.right = null 或 root.left = null,即删除了我们要删除的节点 return null; } else if (root.left != null && root.right != null) { // 如果root左右子结点健在 // 此时就是想把目标结点旋转到底层去, // 然后需要选择一个优先级值比较小的结点放在目标结点位置 // 如果左子结点优先级较小 if (root.left.priority < root.right. priority) { // 旋转root已经变为左子结点,原来的根结点变为右子节点 root = rightRotate(root); // 去找那被换下去的节点,将它删除掉 root.right = delete(value, root.right); } else { root = leftRotate(root); // 去找那被换下去的节点,将它删除掉 root.left = delete(value, root.left); } } else if (root.left != null) { // 没有右子节点,只能右旋了 root = rightRotate(root); // 去找那被换下去的节点,将它删除掉 root.right = delete(value, root.right); } else if (root.right != null) { // 没有左子节点,只能左旋了 root = leftRotate(root); // 去找那被换下去的节点,将它删除掉 root.left = delete(value, root.left); } } } return root; } // 查询 // 根据二叉搜索树性质查询 public TreeNode query(TreeNode root, int value) { //这里的root才是真root,上面的方法只是局部变量 //所以不能在查询中改变根节点 TreeNode temp = root; while (temp != null) { if (temp.value > value) { temp = temp.left; } else if (temp.value < value) { temp = temp.right; } else { return temp; } } return null; } // 右旋,左子节点右旋 public TreeNode rightRotate(TreeNode treeNode) { // temp为左子结点 TreeNode temp = treeNode.left; //将父结点的左边指向 temp的右子结点 treeNode.left = temp.right; // 将temp结点的右边指向父结点 temp.right = treeNode; // 进行上面两步操作,在纸上画一下就找到其右旋成功了, // 即左子结点变为根结点了 // 返回此时旋转后的真正根结点 return temp; } // 左旋,右子结点左旋 public TreeNode leftRotate(TreeNode treeNode) { // temp为右子结点 TreeNode temp = treeNode.right; //将父结点的右边指向 temp的左子结点 treeNode.right = temp.left; // 将temp结点的左边指向父结点 temp.left = treeNode; // 进行上面两步操作,在纸上画一下就找到其左旋成功了, // 即右子结点变为根结点了 // 返回此时旋转后的真正根结点 return temp; } public void printTree(TreeNode root) { if (root != null) { printTree(root.left); System.out.println(root.value); printTree(root.right); } } public static void main(String[] args) { Treap treap = new Treap(); TreeNode root = null; root = treap.insert(1, root); root = treap.insert(2, root); root = treap.insert(3, root); root = treap.insert(4, root); root = treap.insert(5, root); root = treap.insert(6, root); //中序遍历,如果打印的值由小到大,说明满足二叉搜索树特征 treap.printTree(root); System.out.println(); // 测试查询 TreeNode query = treap.query(root, 1); System.out.println(query.value); query = treap.query(root, 2); System.out.println(query.value); query = treap.query(root, 3); System.out.println(query.value); query = treap.query(root, 4); System.out.println(query.value); query = treap.query(root, 5); System.out.println(query.value); query = treap.query(root, 6); System.out.println(query.value); query = treap.query(root, 7); System.out.println(query); System.out.println(); // 测试删除 root = treap.delete(2,root); root = treap.delete(3,root); root = treap.delete(5,root); root = treap.delete(7,root); treap.printTree(root); } } class TreeNode { int value; int priority; TreeNode left; TreeNode right; public TreeNode(int value, int priority) { this.value = value; this.priority = priority; } }
到此这篇关于Java实现Treap树的示例代码的文章就介绍到这了,更多相关Java Treap树内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!