当前位置: 首页 > news >正文

04树 + 堆 + 优先队列 + 图(D1_树(D11_伸展树))

目录

一、基本介绍

二、伸展操作

1. 左右情况的伸展

2. 左左情况的伸展

3. 右左情况的伸展

4. 右右情况的伸展

三、其它操作

1. 插入

2. 删除

四、代码实现


一、基本介绍

伸展树是一种二叉搜索树,伸展树也是一种平衡树,不过伸展树并不像AVL树那样对树的平衡有很

严格的要求(左右孩子高度之差不能超过1),伸展树通过一系列的伸展操作,可以保证对伸展树

的任意连续M次操作,其时间复杂度不会超过MlogN级别,并且伸展树的实现相较于AVL树也简单

了很多,不论是插入还是删除算法

二、伸展操作

伸展树之所以能够保证MlogN的界,就是因为伸展树的伸展操作,每一次调用伸展树的search方法

查找一个结点的时候,如果存在,那么就需要对该结点进行一系列的伸展操作,经过一系列的伸展

操作之后,该结点最终会变成根节点

1. 左右情况的伸展

本质上就是AVL树的右双旋转

2. 左左情况的伸展

这种情况和AVL树的单右旋转不同,需要先将A进行单向左旋,然后再将B进行单向左旋

3. 右左情况的伸展

略,情况同1

4. 右右情况的伸展

略,情况同2

三、其它操作

1. 插入

伸展树的插入就是普通BST的插入算法,无差别

2. 删除

伸展树的删除步骤如下

(1)找到被删除的结点D的左子树DL的最大结点M,然后将M结点伸展到DL的根节点处

(2)然后将D删除,因为M一定没有右孩子,即使被伸展到DL的根节点也不会有右孩子,此时只

需要将D的右子树DR挂到M的右孩子上即可

四、代码实现

package splaytree;/*** 实现伸展树** @author CodingW*/
public class SplayTree<K extends Comparable<K>, V> {private TreeNode<K, V> root;/*** 向伸展树中插入一个新的结点** @param key   关键字* @param value 值*/public void insert(K key, V value) {TreeNode<K, V> cur = root;TreeNode<K, V> pre = null;while (cur != null) {int cmp = cur.key.compareTo(key);if (cmp < 0) {pre = cur;cur = cur.right;} else if (cmp > 0) {pre = cur;cur = cur.left;} else {// 如果相等,那么就进行替换cur.value = value;break;}}TreeNode<K, V> node = new TreeNode<>(key, value);node.setParent(pre);if (pre == null) {// 说明当前插入的结点是根节点root = node;return;}// 如果pre不为null,那么说明cur不为null即// root不为null,所以pre一定指向的是bst中// node需要插入位置的前驱结点if (pre.key.compareTo(key) < 0) {pre.right = node;} else {pre.left = node;}}/*** 返回先序遍历** @return 返回先序遍历序列字符串*/public String preOrder() {StringBuilder pre = new StringBuilder();if (root == null) {return pre.toString();}TreeNode<K, V> cur = root;while (cur != null) {if (cur.left != null) {TreeNode<K, V> mostRight = cur.left;while (mostRight.right != null && mostRight.right != cur) {mostRight = mostRight.right;}if (mostRight.right == null) {pre.append("{").append(cur.key).append(",").append(cur.value).append(",").append(cur.parent == null ? "null" : cur.parent.value).append("}\t");mostRight.right = cur;cur = cur.left;continue;} else {mostRight.right = null;}} else {pre.append("{").append(cur.key).append(",").append(cur.value).append(",").append(cur.parent == null ? "null" : cur.parent.value).append("}\t");}cur = cur.right;}return pre.toString();}public String inOrder() {StringBuilder in = new StringBuilder();if (root == null) {return in.toString();}TreeNode<K, V> cur = root;while (cur != null) {if (cur.left != null) {TreeNode<K, V> mostRight = cur.left;while (mostRight.right != null && mostRight.right != cur) {mostRight = mostRight.right;}if (mostRight.right == null) {mostRight.right = cur;cur = cur.left;continue;} else {mostRight.right = null;in.append("{").append(cur.key).append(",").append(cur.value).append(",").append(cur.parent == null ? "null" : cur.parent.value).append("}\t");}} else {in.append("{").append(cur.key).append(",").append(cur.value).append(",").append(cur.parent == null ? "null" : cur.parent.value).append("}\t");}cur = cur.right;}return in.toString();}/*** 删除伸展树指定的关键字,基本的删除算法的思想是* 首先查找这个被删除的元素,然后这个元素就会变成根元素* 然后删除这个根元素,同时查找其左子树的最大值,这个最大值* 也会变成根元素,这个根元素一定右子树为空,那么把这个根元素的** @param key 关键字* @return 返回被删除的关键字对应的值*/public V delete(K key) {TreeNode<K, V> node = searchHelper(key);if (node == null) {return null;}// 找到node的左孩子的最大值TreeNode<K, V> leftMax = findMax(node.left);// 将左孩子的最大值伸展到根节点,leftMax一定是没有右孩子的// 因为leftMax的伸展百分之百是left-left型,伸展之后是right-right型// 后面的伸展只能在这两个型之间跳跃splaying(leftMax);leftMax.right = node.right;node.left = null;node.right = null;return node.value;}private TreeNode<K, V> findMax(TreeNode<K, V> root) {while (root != null && root.right != null) {root = root.right;}return root;}/*** 单向右旋转** @param node 被旋转的结点*/private void singleRightRotate(TreeNode<K, V> node) {// 特别注意在旋转过程中更新结点的父亲指针,否则将会导致回溯// 过程中产生某些意外情况TreeNode<K, V> left = node.left;node.left = left.right;if (left.right != null) {left.right.parent = node;}left.parent = node.parent;if (node.parent == null) {// 如果当前旋转的是根节点root = left;} else if (node == node.parent.left) {node.parent.left = left;} else {node.parent.right = left;}left.right = node;node.parent = left;}/*** 单向左旋转** @param node 被旋转的结点*/private void singleLeftRotate(TreeNode<K, V> node) {TreeNode<K, V> right = node.right;node.right = right.left;if (right.left != null) {right.left.parent = node;}right.parent = node.parent;if (node.parent == null) {root = right;} else if (node == node.parent.left) {node.parent.left = right;} else {node.parent.right = right;}right.left = node;node.parent = right;}/*** 双向右旋转** @param node 被旋转的结点*/private void doubleRightRotate(TreeNode<K, V> node) {singleLeftRotate(node.left);singleRightRotate(node);}/*** 双向左旋转** @param node 被旋转的结点*/private void doubleLeftRotate(TreeNode<K, V> node) {singleRightRotate(node.right);singleLeftRotate(node);}/*** 根据关键字查找指定的值,每一次查找都会导致被查找的结点* 变成根结点,并且其他结点的位置深度基本上下降一半** @param key 关键字* @return 返回key对应的值*/public V search(K key) {TreeNode<K, V> target = searchHelper(key);return target == null ? null : target.value;}/*** 从某个结点开始展开,具体的展开规则是:* (1)node.parent == root && node.parent.left == node,* 那么将node.parent直接右旋,这样node将会成为根节点* (2)node.parent == root && node.parent.right == node,* 那么将node.parent直接左旋,这样node将会成为根节点* (3)node.parent != root,node.parent == node.parent.left* && node == node.parent.right,那么将node.parent.parent双向右旋* (4)node.parent != root,node.parent == node.parent.right* && node == node.parent.left,那么将node.parent.parent双向左旋* (5)node.parent != root,node.parent == node.parent.left* && node == node.parent.left,那么将node.parent.parent单向右旋,* 然后将node.parent单向右旋* (6)node.parent != root,node.parent == node.parent.right* && node == node.parent.right,那么将node.parent.parent单向左旋,* 然后将node.parent单向左旋** @param node 待旋转的结点*/private void splaying(TreeNode<K, V> node) {while (node.parent != null) {if (node.parent == root) {if (node.parent.left == node) {// 情况(1)singleRightRotate(node.parent);} else {// 情况(2)singleLeftRotate(node.parent);}} else if (node.parent == node.parent.parent.left) {if (node == node.parent.right) {// 情况(3)doubleRightRotate(node.parent.parent);} else {// 情况(5)singleRightRotate(node.parent.parent);singleRightRotate(node.parent);}} else {if (node == node.parent.right) {// 情况(6)singleLeftRotate(node.parent.parent);singleLeftRotate(node.parent);} else {// 情况(4)doubleLeftRotate(node.parent.parent);}}}}/*** 搜索SplayTree中具有指定关键字的结点** @param key 关键字* @return 返回关键字和key相等的结点,如果不存在,返回null*/private TreeNode<K, V> searchHelper(K key) {TreeNode<K, V> cur = root;while (cur != null) {int cmp = cur.key.compareTo(key);if (cmp < 0) {cur = cur.right;} else if (cmp > 0) {cur = cur.left;} else {break;}}// 可能从break跳出,也可能完全没进入while循环if (cur != null) {splaying(cur);}return cur;}/*** 伸展树的结点类型** @param <K> 关键字类型* @param <V> 值类型*/private static class TreeNode<K extends Comparable<K>, V> {public K key;public V value;public TreeNode<K, V> left;public TreeNode<K, V> right;public TreeNode<K, V> parent;public TreeNode(K key, V value) {this.key = key;this.value = value;}public void setParent(TreeNode<K, V> parent) {this.parent = parent;}}public static void main(String[] args) {SplayTree<Integer, Integer> splayTree = new SplayTree<>();splayTree.insert(1, 1);splayTree.insert(32, 32);splayTree.insert(30, 30);splayTree.insert(31, 31);splayTree.insert(28, 28);splayTree.insert(29, 29);splayTree.insert(26, 26);splayTree.insert(27, 27);splayTree.insert(24, 24);splayTree.insert(25, 25);splayTree.insert(22, 22);splayTree.insert(23, 23);splayTree.insert(20, 20);splayTree.insert(21, 21);splayTree.insert(18, 18);splayTree.insert(19, 19);splayTree.insert(16, 16);splayTree.insert(17, 17);splayTree.insert(14, 14);splayTree.insert(15, 15);splayTree.insert(12, 12);splayTree.insert(13, 13);splayTree.insert(10, 10);splayTree.insert(11, 11);splayTree.insert(8, 8);splayTree.insert(9, 9);splayTree.insert(6, 6);splayTree.insert(7, 7);splayTree.insert(4, 4);splayTree.insert(5, 5);splayTree.insert(2, 2);splayTree.insert(3, 3);System.out.println(splayTree.preOrder());System.out.println(splayTree.inOrder());System.out.println(splayTree.search(2));System.out.println(splayTree.search(3));System.out.println(splayTree.search(4));System.out.println(splayTree.search(5));System.out.println(splayTree.search(6));System.out.println(splayTree.search(7));System.out.println(splayTree.search(8));System.out.println(splayTree.search(9));System.out.println("访问部分结点后遍历:");System.out.println(splayTree.preOrder());System.out.println(splayTree.inOrder());}
}

相关文章:

04树 + 堆 + 优先队列 + 图(D1_树(D11_伸展树))

目录 一、基本介绍 二、伸展操作 1. 左右情况的伸展 2. 左左情况的伸展 3. 右左情况的伸展 4. 右右情况的伸展 三、其它操作 1. 插入 2. 删除 四、代码实现 一、基本介绍 伸展树是一种二叉搜索树&#xff0c;伸展树也是一种平衡树&#xff0c;不过伸展树并不像AVL树那…...

c语言练习题【数据类型、递归、双向链表快速排序】

练习1&#xff1a;数据类型 请写出以下几个数据的数据类型 整数 a a 的地址 存放a的数组 b 存放a的地址的数组 b的地址 c的地址 指向 printf 函数的指针 d 存放 d的数组 整数 a 的类型 数据类型是 int a 的地址 数据类型是 int*&#xff08;指向 int 类型的指针&#xff09; …...

SliverAppBar的功能和用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容&#xff0c;本章回中将介绍SliverAppBar组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似&#xff0c;它们的…...

五、定时器实现呼吸灯

5.1 定时器与计数器简介 定时器是一种通过对内部时钟脉冲计数来测量时间间隔的模块。它的核心是一个递增或递减的寄存器&#xff08;计数器值&#xff09;。如果系统时钟为 1 MHz&#xff0c;定时器每 1 μs 计数一次。 计数器是一种对外部事件&#xff08;如脉冲信号&#xff…...

Elasticsearch的索引生命周期管理

目录 说明零、参考一、ILM的基本概念二、ILM的实践步骤Elasticsearch ILM策略中的“最小年龄”是如何计算的&#xff1f;如何监控和调整Elasticsearch ILM策略的性能&#xff1f; 1. **监控性能**使用/_cat/thread_pool API基本请求格式请求特定线程池的信息响应内容 2. **调整…...

【大模型理论篇】最近大火的DeepSeek-R1初探系列1

1. 背景介绍 这一整个春节&#xff0c;被DeepSeek-R1刷屏。各种铺天盖地的新闻以及老板发的相关信息&#xff0c;着实感受到DeepSeek-R1在国外出圈的震撼。 DeepSeek推出了新的推理模型&#xff1a;DeepSeek-R1-Zero 和 DeepSeek-R1。DeepSeek-R1-Zero 是一个在没有经过监督微调…...

【数据结构】(4) 线性表 List

一、什么是线性表 线性表就是 n 个相同类型元素的有限序列&#xff0c;每一个元素只有一个前驱和后继&#xff08;除了第一个和最后一个元素&#xff09;。 数据结构中&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列。 二、什么是 List List 是 Java 中的线性…...

【C++ STL】vector容器详解:从入门到精通

【C STL】vector容器详解&#xff1a;从入门到精通 摘要&#xff1a;本文深入讲解C STL中vector容器的使用方法&#xff0c;涵盖常用函数、代码示例及注意事项&#xff0c;助你快速掌握动态数组的核心操作&#xff01; 一、vector概述 vector是C标准模板库&#xff08;STL&am…...

OpenAI推出Deep Research带给我们怎样的启示

OpenAI 又发新产品了&#xff0c;这次是面向深度研究领域的智能体产品 ——「Deep Research」&#xff0c;貌似被逼无奈的节奏… 在技术方面&#xff0c;Deep Research搭载了优化后o3模型并通过端到端强化学习在多个领域的复杂浏览和推理任务上进行了训练。因没有更多的技术暴露…...

洛谷[USACO08DEC] Patting Heads S

题目传送门 题目难度&#xff1a;普及/提高一 题面翻译 今天是贝茜的生日&#xff0c;为了庆祝自己的生日&#xff0c;贝茜邀你来玩一个游戏。 贝茜让 N N N ( 1 ≤ N ≤ 1 0 5 1\leq N\leq 10^5 1≤N≤105) 头奶牛坐成一个圈。除了 1 1 1 号与 N N N 号奶牛外&#xff0…...

CSS 溢出内容处理:从基础到实战

CSS 溢出内容处理&#xff1a;从基础到实战 1. 什么是溢出&#xff1f;示例代码&#xff1a;默认溢出行为 2. 使用 overflow 属性控制溢出2.1 使用 overflow: hidden 裁剪内容示例代码&#xff1a;裁剪溢出内容 2.2 使用 overflow: scroll 显示滚动条示例代码&#xff1a;显示滚…...

Spring Boot项目如何使用MyBatis实现分页查询

写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油&#xff0c;冲鸭&#x…...

飞行汽车中的无刷外转子电机、人形机器人中的无框力矩电机技术解析与应用

重点:无刷外转子电机与无框力矩电机&#xff1a;技术解析与应用对比 在现代工业自动化和精密机械领域&#xff0c;无刷电机因其高效、低噪音和高可靠性而备受青睐。其中&#xff0c;无刷外转子电机和无框力矩电机更是以其独特的结构和性能特点&#xff0c;成为众多应用场景中的…...

FreeRTOS学习 --- 队列集

队列集简介 一个队列只允许任务间传递的消息为同一种数据类型&#xff0c;如果需要在任务间传递不同数据类型的消息时&#xff0c;那么就可以使用队列集 &#xff01; 作用&#xff1a;用于对多个队列或信号量进行“监听”&#xff0c;其中不管哪一个消息到来&#xff0c;都可让…...

【R语言】R语言安装包的相关操作

一、管理R语言安装包 1、安装R包 install.packages() 2、查看已安装的R包 installed.packages() 3、更新R包 update.packages() 4、卸载R包 remove.packages() 二、加载R语言安装包 打开R语言时&#xff0c;基础包&#xff08;base包&#xff09;会自动被加载到内存中…...

15.[前端开发]Day15-HTML+CSS阶段练习(网易云音乐四)

完整代码 01_网易云-header <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wid…...

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户登录

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【Spring篇】【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;1.登录-持久层 &…...

测试方案和测试计划相同点和不同点

在软件测试领域&#xff0c;测试方案与测试计划皆为举足轻重的关键文档&#xff0c;尽管它们有着紧密的关联&#xff0c;但在目的与内容层面存在着显著的差异。相同点&#xff1a; 1.共同目标&#xff1a;测试方案和测试计划的核心目标高度一致&#xff0c;均致力于保障软件的…...

c++提取矩形区域图像的梯度并拟合直线

c提取旋转矩形区域的边缘最强梯度点&#xff0c;并拟合直线 #include <opencv2/opencv.hpp> #include <iostream> #include <vector>using namespace cv; using namespace std;int main() {// 加载图像Mat img imread("image.jpg", IMREAD_GRAYS…...

Unity Shader Graph 2D - 角色身体电流覆盖效果

在游戏中,通常会有游戏角色受到“电击”的效果,此时游戏角色身体上会覆盖有电流,该效果能表明游戏角色的当前状态,让玩家能够获得更直观更好的体验。 那么如何实现呢 首先创建一个ShaderGraph文件,命名为Current,再创建对应的材质球M_Current。 基础的资源显示 老规矩,…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...