伸展树详解

news2024/11/26 21:36:59

伸展树的概念

伸展树(Splay Tree)是仅依靠局部性原理和局部平衡分析,从而实现高效的自适应平衡树结构。是一种二叉查找树,其核心思想是将最近访问的节点旋转到根节点。每次进行访问、插入、删除等操作时,都会选择与之操作的节点作为根节点进行旋转。通过不断进行旋转操作,会引起每次访问节点和其父节点之间的关系发生变化,从而使得经常被访问的节点移动到树顶部,而不常被访问的节点则移动到树的底部。

伸展树中最重要的操作是旋转(Splay),旋转操作可以分为一次旋转和双旋转两种情况。一次旋转是将一个节点沿其父节点的左右儿子其中一个进行旋转,双旋转则是在一次旋转的基础上再进行一次旋转。

伸展树的平衡性是通过特殊的旋转来实现的。特别地,伸展树在每次插入或者删除一个节点后,都将它旋转到根节点,从而维护树的平衡性。

伸展树的实现

在学习伸展树之前,我们需要了解AVL树,明白了AVL树中的旋转,那么伸展树理解起来就不是什么难事了。想了解AVL树可以参考下面这篇博客。

AVL树详解_小白麋鹿的博客-CSDN博客https://yt030917.blog.csdn.net/article/details/130176502其实说白了,伸展树就是每次对一个节点操作之后,把它移动到根的位置。我们可以先通过递归找到这个节点,在递归回去的过程中对其旋转。具体的实现见代码。

要注意,伸展树是一种二叉查找树,但并不是平衡二叉树,这里不要混淆了。

伸展树视频演示

也可以自己尝试:

Splay Tree Visualzation (usfca.edu)icon-default.png?t=N3I4https://www.cs.usfca.edu/~galles/visualization/SplayTree.html

代码示例

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef char ElementType;
typedef struct SplayTreeNode
{
	ElementType Data; //暂定节点内容只有单个字符
	int Height;
	struct SplayTreeNode* Left;
	struct SplayTreeNode* Right;
}SplayTree;

int Height(SplayTree* Node) //规定叶节点的高度为1
{
	if (Node == NULL)
		return 0;
	return Node->Height;
}
//单左旋,指的是左边节点进行旋转,并不是指的旋转到左边(这与大多数教程是不同的,但是无伤大雅)
SplayTree* SingleLeftRotate(SplayTree* k2) 
{
	//旋转节点
	SplayTree* k1 = k2->Left;
	k2->Left = k1->Right;
	k1->Right = k2;
	//更新高度
	k2->Height = max(Height(k2->Left), Height(k2->Right)) + 1;
	k1->Height = max(Height(k1->Left), Height(k1->Right)) + 1;
	//返回
	return k1;
}
//单右旋,指的是右边节点进行旋转,并不是指的旋转到右边(这与大多数教程是不同的,但是无伤大雅)
SplayTree* SingleRightRotate(SplayTree* k2) 
{
	//旋转节点
	SplayTree* k1 = k2->Right;
	k2->Right = k1->Left;
	k1->Left = k2;
	//更新高度
	k2->Height = max(Height(k2->Left), Height(k2->Right)) + 1;
	k1->Height = max(Height(k1->Left), Height(k1->Right)) + 1;
	//返回
	return k1;
}
SplayTree* InsertElement(SplayTree** root, ElementType data)
{
	//走到空节点(即插入位置),执行插入操作 
	if ((*root) == NULL)
	{
		(*root) = (SplayTree*)calloc(1, sizeof(SplayTree)); 
		(*root)->Data = data; 
	}
	//data比节点的值小,向左侧插入
	else if(data < (*root)->Data)
	{
		(*root)->Left = InsertElement(&(*root)->Left, data);
	}
	//data比节点的值大,向右侧插入
	else if (data > (*root)->Data)
	{
		(*root)->Right = InsertElement(&(*root)->Right, data);
	}
	//data与节点的值相同,暂定什么都不做   
	else {  /*暂定如果插入的元素内容相同的话,那么什么都不会做*/ }
	//更新高度
	(*root)->Height = max(Height((*root)->Left), Height((*root)->Right)) + 1;
	//返回
	return *root;
}
//这里是将查找和伸展操作放在同一个函数的,所以就会出现即使data不在树中也会执行伸展操作,
//一般是将理论上离data最近的节点移到根位置。要想实现只有当data出现在树中才伸展,
//可以考虑用一个额外的函数判断data是否存在,进而决定是否需要伸展
bool FindElement(SplayTree** root, ElementType data) //在以*root为根的树中查找data元素。同时对树进行伸展
{   
	//没找到,返回NULL    
	if ((*root) == NULL)
	{
		puts("404 - Not Found!");
		return false;
	}
	//data小,向左走
	else if (data < (*root)->Data)
	{	
		if(FindElement(&(*root)->Left, data) != 0)
			(*root) = SingleLeftRotate((*root));
	}
	//data大,向右走
	else if (data > (*root)->Data)
	{
		if (FindElement(&(*root)->Right, data) != 0)
			(*root) = SingleRightRotate((*root));
	}
	//返回
	return true;
	
}
int main()
{
	SplayTree* root = NULL;
	InsertElement(&root, 'h');
	InsertElement(&root, 'd');
	InsertElement(&root, 'e');
	InsertElement(&root, 'b');
	InsertElement(&root, 'c');
	InsertElement(&root, 'j');
	InsertElement(&root, 'p');
	InsertElement(&root, 'x');
	InsertElement(&root, 't');
	InsertElement(&root, 'f');
	puts("************** insert over ****************");
	FindElement(&root, 'e');
	//FindElement(&root, 'x');
	puts("**************  find over  ****************");
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/513438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【云计算】Hadoop集群安装

文章目录 前言一、环境二、安装虚拟机及配置配置网络 三、安装Ubuntu及配置下载ISO镜像VMware安装UbuntuUbuntu配置&#xff1a;配置结果IP免密登录 JAVA安装hadoop安装&#xff1a;文件的作用core-site.xml&#xff1a;core-site.xmlyarn-site.xmlmapred-site.xml 修改配置&am…

【Linux网络】网络层IP和数据链路层

文章目录 1、网络层IP1.1 认识网络层1.2 IP报文格式和IP报文切片1.3 网段划分以及路由 2、数据链路层2.1 以太网帧格式2.2 ARP协议 3、其它重要协议或技术3.1 DNS技术3.2 ICMP协议3.3 NAT技术3.4 NAT和代理服务器 1、网络层IP 1.1 认识网络层 浅谈一下各层 应用层&#xff1a;…

JBoss 5.x/6.x 反序列化漏洞(CVE-2017-12149)复现

文章目录 一.前言二.影响版本三.环境搭建四.漏洞复现1.编写反弹shell的命令2.序列化数据生成3.发送POC 一.前言 该漏洞为 Java反序列化错误类型&#xff0c;存在于 Jboss 的 HttpInvoker 组件中的 ReadOnlyAccessFilter 过滤器中。该过滤器在没有进行任何安全检查的情况下尝试…

基于AT89C51单片机的温度计设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87773445 源码获取 主要内容&#xff1a; 设计一个简易温度计&#xff1b;要求电路实现如下功能&#xff1a; 设计通过单片机和数码管、led灯等组成&#xff0c;可…

巧用千寻位置GNSS软件| 一文学会曲线样

曲线放样是圆曲线形放样工具&#xff0c;在线路初堪、临时设计、临时放样采集坐标等作业过程中&#xff0c;曲线放样更加简单和方便。千寻位置GNSS软件提供了三种线型文件编辑放&#xff0c;分别是直线、圆曲线 和缓曲线。圆曲线说明:线型上任意一点的曲率、半径都相同;缓曲线说…

【剧前爆米花--爪哇岛寻宝】网络编程一些概念以及Java实现网络编程流程

作者&#xff1a;困了电视剧 专栏&#xff1a;《JavaEE初阶》 文章分布&#xff1a;这是一篇关于网络初识的文章&#xff0c;在这篇文章中剖析了网络编程的一些概念以及用Java实现网络编程的一些流程&#xff0c;希望对你有所帮助&#xff01; 目录 网络编程 含义和一些概念 …

sysMaster: 全新1号进程实现方案,秒级自愈,保障系统全天在线

认识 1 号进程和 sysMaster 在 Linux 操作系统中&#xff0c;1 号进程是 init 进程&#xff0c;它是所有其他进程的祖先进程。init 进程是系统启动时第一个被创建的进程&#xff0c;它负责启动和管理其他所有进程&#xff0c;并在系统关机时关闭它们。在现代 Linux 系统中&…

数组--part 4--长度最小的子数组(力扣299/904/76)

文章目录 算法基本思想leetcode 209 长度最小的子数组leetcode 904 水果成篮leetcode 76 最小覆盖子串 算法基本思想 首先对于滑动窗口&#xff0c;题目可以先去看看leetcode 209 进行相关的了解后&#xff0c;再来书写代码。 首先我们的第一想法肯定就是暴力解法&#xff1a…

html实现汉诺塔小游戏

文章目录 1.设计来源汉诺塔由来1.1 主界面1.2 游戏规则1.3 游戏完成界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/130606736 html实现汉诺塔小游戏源码 汉诺塔&…

数据结构-链表(详解)

前言&#xff1a;内容包括&#xff1a;链表的分类&#xff0c;无头单向非循环链表的增删查改的实现&#xff0c;带头双向循环链表的增删查改的实现 目录 链表的分类 1. 单向或者双向 ​编辑 2. 带头或者不带头 3. 循环或者非循环 无头单向非循环链表&#xff1a; ​编辑…

造梦日记 Printidea 用户手册

*Hi 造梦日记* 欢迎来到造梦日记的空间&#xff5e; 造梦日记Printidea是一款基于AI算法、输入文字或图片即可生成高质量图片的工具&#xff0c;由西湖大学深度学习实验室和西湖心辰联合出品&#xff0c;超强算力&#xff0c;0.8秒出图&#xff0c;目前支持微信小程序、网页端…

边缘计算盒子的特点?边缘计算盒子适用于什么场景

边缘计算盒子&#xff08;Edge Computing Box&#xff09;是一种用于边缘计算的硬件设备&#xff0c;它通常是一个小型的计算设备&#xff0c;具备一定的计算能力和存储资源&#xff0c;并且能够连接到网络。边缘计算盒子的主要目的是在物联网&#xff08;IoT&#xff09;和分布…

续ShaderEditor、Inspector之后又一成功爆品,2周260+单!

01 前言 大家好&#xff0c;我是98K&#xff01;五一前&#xff0c;我上架 Cocos Store 的『高性能割草框架』增加 Cocos Creator 2.4.x 引擎&#xff0c;已经支持的老铁可免费更新&#xff01; 后续的更新计划是&#xff1a;完善2D游戏案例、增加3D案例、支持RVO和群聚&#…

Android WebView 长按弹出的文本选择器如何监听滑动和如何弹出完全自定义的菜单栏

在这次改版中&#xff0c;h5小伙伴与我沟通说要把长按选择改用成原生的拉选框&#xff0c;之前我也没搞过呀&#xff0c;开始研究吧。 怎么研究呀&#xff0c;当然是百度一下了。 百度了一天总结如下&#xff1a; 好多文章都是告诉你如何在系统的基础上来修改自己的文字和点…

vue2 框架运行原理剖析系列(二)之 组件挂载$mount神秘之旅!!!

一、vue组件挂载 1.1 上一篇文章中&#xff0c;介绍到组件执行 mountComponent 函数&#xff0c;本文对此展开详细的讲解。 1.2 调用改方法的位置在于entry-runtime-with-compiler.js 的Vue.prototype.$mount&#xff0c;具体代码如下&#xff1a; 其中&#xff0c; &#xff…

【图像融合】Dif-Fusion:基于扩散模型的红外/可见图像融合方法

文章目录 摘要一、前言二、相关工作1.红外线和可见光的图像融合2.扩散模型&#xff08;可见博主之前的博客&#xff09; 三、方法1.红外线和可见光图像的联合扩散2*.多通道扩散特征的融合 四、实验1.实验设置2.融合性能分析&#xff08;效果展示&#xff09;3.泛化实验 总结 摘…

通知短信 API 技术细节以及发送流程机制原理解析

引言 短信是一种简单、直接、高效的通信方式&#xff0c;被广泛应用于各个领域。在移动互联网时代&#xff0c;短信成为了客户服务、政府通知、公共服务等方面的重要工具。为了更好地利用短信这种通信方式&#xff0c;通知短信 API应运而生。短信API可以帮助企业、政府和应用程…

RK3588旗舰32T人工智能多网口边缘智能网关交换机

32T边缘智能网关发布&#xff0c;助力多行业数字化升级&#xff0c;运维降本增效&#xff0c;搭载RK3588旗舰芯 搭载瑞芯微RK3588芯片的边缘智能网关XM-RK3588&#xff0c;算力可扩展至32T&#xff0c;适用于电力能源、智慧交通、智慧城市、智慧安防、智慧医疗、工业互联网等领…

前端的加密和解密,crypto-js的应用,AES / RSA / md5

每日鸡汤&#xff1a;每个你想学习的瞬间&#xff0c;都是未来你的向自己求救 内容预警*****新手内容&#xff0c;自己学习总结用****大佬请绕道 之前看https原理&#xff0c;看到对称加密和非对称加密&#xff0c;各种加密方法&#xff0c;看得云里雾里&#xff0c;即便是总结…

报错main.py: error: unrecognized arguments: stack_size 4 1001,770,123

运行从GitHub上面下载下来的代码时&#xff0c;按照作者提供的输入命令输入后报错&#xff1a; main.py: error: unrecognized arguments: stack_size 4 1001,770,123 将报错的部分在网上百度&#xff0c;找到部分方法&#xff0c;得出理解&#xff1a;输入的命令是出错的&am…