使用伸展树(SplayTree)统计单词频率的Java实现

伸展树(Splay Tree)的深度理解 伸展树(Splay Tree)的深度理解 伸展树作为一种非常受欢迎的数据结构,其最初的想法(标记为想法一)是通过变换树的结构将最近访问的元素置于树根,倘若一系列操作都是和该元素有关,那么在变成树根之后将极大的提高该元素的访问速度。基于该想法,每次访问该元素时,沿着访问路径,逐层进行简单的单层旋转,从而使该元素的深度减小,最后变成树根。 图1 (P表示路径path,V表示访问visit) (这是左旋... 阅读详情

伸展树(Splay Tree)是特殊的二叉搜索树。

伸展树在查询时进行旋转,使得刚刚查到的节点成为树根,越频繁使用的节点会越靠近树根,这样可以加快访问速度。关于伸展树的介绍很多,在了解二叉树和AVL树后,伸展树的原理会比较好理解。本文不再介绍伸展树,而是统计各个单词在一篇文章中出现的频率,并将它们构造成伸展树。


1、构造二叉搜索树

二叉搜索树节点:
/**
 * Node of Binary Search Tree
 * @author cuiods
 */
public class BSTNode<T extends Comparable<? super T>> {
    protected T data;
    protected BSTNode<T> left, right;

    public BSTNode() {
        this(null, null, null);
    }

    public BSTNode(T data) {
        this(data, null, null);
    }

    public BSTNode(T data, BSTNode left, BSTNode right) {
        this.data = data;
        this.left = left;
        this.right = right;
    }
}
二叉搜索树:
/**
 * Binary Search Tree
 * @author cuiods
 */
public class BSTree<T extends Comparable<? super T>> {
    protected BSTNode<T> root;

    public T search(T data) {
        return search(root, data);
    }

    public void insert(T data) {
        BSTNode<T> p = root, prev = null;
        while (p != null) {
            prev = p;
            if (p.data.compareTo(data) < 0) {
                p = p.right;
            } else {
                p = p.left;
            }
        }
        if (root == null) {
            root = new BSTNode<T>(data);
        } else if (prev.data.compareTo(data) < 0) {
            prev.right = new BSTNode<T>(data);
        } else {
            prev.left = new BSTNode<T>(data);
        }
    }

    public void inorder() {
        inorder(root);
    }

    protected void inorder(BSTNode<T> node) {
        if (node != null) {
            inorder(node.left);
            visit(node.data);
            inorder(node.right);
        }
    }

    protected T search(BSTNode<T> p, T data) {
        while (p != null) {
            if (data.equals(p.data)) {
                return data;
            } else if (data.compareTo(p.data) < 0) {
                p = p.left;
            } else {
                p = p.right;
            }
        }
        return null;
    }

    protected void visit(T node) {
        System.out.println(node.toString() + " ");
    }
}

2、构造伸展树

主要实现的是伸展树的插入和搜索方法,最核心的是伸展方法,借鉴:
http://www.cnblogs.com/skywang12345/p/3604286.html

伸展树节点:
/**
 * node of splay tree
 * @author cuiods
 */
public class SplayTreeNode<T extends Comparable<? super T>> extends BSTNode<T> {

    public SplayTreeNode() {
        right = left = null;
    }
    public SplayTreeNode(T data) {
        this(data,null,null);
    }
    public SplayTreeNode(T data, SplayTreeNode<T> left, SplayTreeNode<T> right) {
        this.data = data;
        this.left = left;
        this.right = right;
    }
}
伸展树:
/**
 * splay tree implement
 * http://www.cnblogs.com/skywang12345/p/3604286.html
 * @author cuiods
 */
public abstract class SplayTree<T extends Comparable<? super T>> extends BSTree<T> {

    /**
     * 处理插入时遇到相同的节点
     * @param data 插入时已经存在的节点
     */
    protected abstract void handleSame(T data);

    @Override
    public void insert(T key) {
        SplayTreeNode<T> z=new SplayTreeNode<T>(key);

        // 插入节点
        root = insert((SplayTreeNode<T>) root, z);
        // 将节点(key)旋转为根节点
        root = splay((SplayTreeNode<T>) root, key);
    }

    @Override
    public T search(T key) {
        T result = super.search(key);
        splay(key);
        return result;
    }

    private void splay(T key) {
        root = splay((SplayTreeNode<T>) root, key);
    }


    /*
    * 旋转key对应的节点为根节点,并返回根节点。
    *
    * 注意:
    *   (a):伸展树中存在"键值为key的节点"。
    *          将"键值为key的节点"旋转为根节点。
    *   (b):伸展树中不存在"键值为key的节点",并且key < tree.key。
    *      b-1 "键值为key的节点"的前驱节点存在的话,将"键值为key的节点"的前驱节点旋转为根节点。
    *      b-2 "键值为key的节点"的前驱节点不存在的话,则意味着,key比树中任何键值都小,那么此时,将最小节点旋转为根节点。
    *   (c):伸展树中不存在"键值为key的节点",并且key > tree.key。
    *      c-1 "键值为key的节点"的后继节点存在的话,将"键值为key的节点"的后继节点旋转为根节点。
    *      c-2 "键值为key的节点"的后继节点不存在的话,则意味着,key比树中任何键值都大,那么此时,将最大节点旋转为根节点。
    */
    private SplayTreeNode<T> splay(SplayTreeNode<T> tree, T data) {
        if (tree == null)
            return null;

        SplayTreeNode<T> N = new SplayTreeNode<T>();
        SplayTreeNode<T> l = N;
        SplayTreeNode<T> r = N;
        SplayTreeNode<T> c;

        for (;;) {
            int cmp = data.compareTo(tree.data);
            if (cmp < 0) {
                if (tree.left == null)
                    break;
                if (data.compareTo(tree.left.data) < 0) {
                    c = (SplayTreeNode<T>) tree.left;                           /* rotate right */
                    tree.left = c.right;
                    c.right = tree;
                    tree = c;
                    if (tree.left == null)
                        break;
                }
                r.left = tree;                               /* link right */
                r = tree;
                tree = (SplayTreeNode<T>) tree.left;
            } else if (cmp > 0) {
                if (tree.right == null)
                    break;
                if (data.compareTo(tree.right.data) > 0) {
                    c = (SplayTreeNode<T>) tree.right;                          /* rotate left */
                    tree.right = c.left;
                    c.left = tree;
                    tree = c;
                    if (tree.right == null)
                        break;
                }
                l.right = tree;                              /* link left */
                l = tree;
                tree = (SplayTreeNode<T>) tree.right;
            } else {
                break;
            }
        }
        l.right = tree.left;                                /* assemble */
        r.left = tree.right;
        tree.left = N.right;
        tree.right = N.left;
        return tree;
    }

    /*
    * 将结点插入到伸展树中,并返回根节点
    *
    * 参数说明:
    *     tree 伸展树的
    *     z 插入的结点
    */
    private SplayTreeNode<T> insert(SplayTreeNode<T> tree, SplayTreeNode<T> z) {
        int cmp;
        SplayTreeNode<T> y = null;
        SplayTreeNode<T> x = tree;

        // 查找z的插入位置
        while (x != null) {
            y = x;
            cmp = z.data.compareTo(x.data);
            if (cmp < 0)
                x = (SplayTreeNode<T>) x.left;
            else if (cmp > 0)
                x = (SplayTreeNode<T>) x.right;
            else {
                handleSame(z.data);
                return tree;
            }
        }

        if (y==null)
            tree = z;
        else {
            cmp = z.data.compareTo(y.data);
            if (cmp < 0)
                y.left = z;
            else
                y.right = z;
        }

        return tree;
    }


}

3、统计单词频率

特殊的单词伸展树:
/**
 * word splay tree
 * @author cuiods
 */
public class WordSplay extends SplayTree<Word>{

    private int differentWords, wordCount;

    @Override
    protected void handleSame(Word data) {
        data.setFreq(data.getFreq()+1);
    }

    @Override
    protected void visit(Word word) {
        super.visit(word);
        differentWords ++;
        wordCount += word.getFreq();
    }

    public void run(InputStream inputStream, String fileName) {
        int ch = 1;
        Word p = null;
        try {
            while (ch >-1) {
                while (true) {
                    if (ch > -1 && !Character.isLetter((char) ch))
                        ch = inputStream.read();
                    else break;
                }
                if (ch == -1) break;
                String s = "";
                while (ch > -1 && Character.isLetter((char) ch)) {
                    s += Character.toUpperCase((char) ch);
                    ch = inputStream.read();
                }
                insert(new Word(s));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        inorder();
        System.out.println("\nFile "+fileName+" contains "+ wordCount + " words among which "+differentWords+" are different.\n");
    }
}

单词类:

/**
 * @author cuiods
 */
public class Word implements Comparable<Word>{

    private String word;
    private int freq = 1;

    public Word(String w) {
        word = w;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public int getFreq() {
        return freq;
    }

    public void setFreq(int freq) {
        this.freq = freq;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Word word1 = (Word) o;

        return word != null ? word.equals(word1.word) : word1.word == null;

    }

    @Override
    public int compareTo(Word o) {
        return word.compareTo(o.word);
    }

    @Override
    public String toString() {
        return "Word{" +
                "word='" + word + '\'' +
                '}';
    }
}

使用单词伸展树统计:
/**
 * splay word test
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = null;
        String fileName = "";
        try {
            if (args.length == 0) {
                System.out.print("Enter a file name: ");
                Scanner scanner = new Scanner(System.in);
                fileName = scanner.nextLine();
                inputStream = new FileInputStream(fileName);
            } else {
                inputStream = new FileInputStream(args[0]);
                fileName = args[0];
            }
            WordSplay splay = new WordSplay();
            splay.run(inputStream,fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}





Java进阶:17.3 伸展树 1、伸展树简介 伸展树(Splay Tree)是特殊的二叉查找树(BST)。 它的特殊是指,它除了本身是棵二叉查找树之外,它还具备一个特点: 当某个节点被访问时,伸展树会通过旋转使该节点成为树根。这样做的好处是,下次要访问该节点时,能够迅速的访问到该节点。 通过之前的学习,知道连续m次查找,对于AVL树来说,共需要O(mlogn)时间。根据局部性原理,我们可以改进AVL树!!-----> 引... 阅读详情

相关推荐

Nacos漏洞利用工具V3.0.5实战:从认证绕开到内存马植入的全流程指南

本文详细解析了Nacos漏洞利用工具V3.0.5在实战中的应用,从认证绕开到内存马植入的全流程。文章深入探讨了如何利用Nacos的历史漏洞,如默认口令、认证逻辑绕过、Derby SQL注入及Jraft反序列化,实现权限提升与持久化控制,并提供了防御视角与加固建议,旨在帮助安全人员理解攻击链以加强防护。

h6j7k8l9p0的博客 483

【数据结构】详细解读 Splay Tree(附完整代码)

本鱼干最近在家忙着玩,大脑许久不怎么用。昨日闲来无事,遂翻看书籍。正巧《机器学习》决策树这一块不是太熟悉。本打算品茶而观之,但发现有一树名Treap,觉得挺好玩,遂实现了一下,做了几个相关题目(用的STL中的map...因为懒),又深入发现了一种名为Splay的树,更加好玩,可苦于没有办法用STL里的东西代替它,遂于今日下午深入了解,并写成博客~~水经验~~ 分享之。

qq_25426559的博客 4517

CVE-2025-25249漏洞深度解析:FortiOS认证绕过与实战缓解

远程代码执行(RCE)是网络安全中最危险的漏洞类型之一,其核心往往源于认证机制失效或内存安全缺陷。CVE-2025-25249正属于此类高危漏洞——它不依赖用户凭证,也不需要交互触发,而是通过超长HTTP头(如X-Forwarded-For)引发内存越界写入,直接绕过FortiOS和FortiSwitchManager的完整认证流程。该漏洞影响广泛、CVSS评分高达9.8,凸显了嵌入式设备在HTTP协议栈实现中的深层风险。在企业网络中,FortiGate等设备常作为边界守门人,一旦失陷将导致root级控制权

weixin_33694620的博客 524

高级搜索——伸展树Splay详解

伸展树(splay tree),也是平衡二叉搜索树的一种形式,但并非严格的平衡。但是其实现相较于AVL树和红黑树更为简捷。伸展树无需时刻都严格地保持全树的平衡,但却能够在任何足够长的真实操作序列中,保持分摊意义上的高效率。伸展树也不需要对基本的二叉树节点结构,做任何附加的要求或改动,更不需要记录平衡因子或高度之类的额外信息,故适用范围更广。动态版本的我们先不对权值进行扩展,只是先实现一棵基本的Splay,所以节点定义和普通的二叉搜索树相同。

EQUINOX1的博客 3706

单相变换器的二次纹波分析

1. 单相PWM整流器的二次纹波产生原因   单相变换器的二次纹波产生的原因是功率含有二次脉动分量。设电网电压 us=Vmsin(wt),电网电流IL=Imsin(wt−θ)I_L=I_msin(wt-\theta)IL​=Im​sin(wt−θ)。其中w是电网电压披绿,θ\thetaθ是电流之后电压相位。保证输入功率和输出功率相等,就要保证两者的直流量和交流量对应相等。   uDu_DuD​为 PWM 整流器直流电容电压平均值,IoI_oIo​ 为负载电流平均值 ,uDC^\widehat {u_{DC}

qq_38847810的博客 4973

CATIA二次开发一一一计算 CATIA 零件模型在全局坐标系下的包围盒方法

本文介绍了一种在CATIA中计算零件模型全局坐标系下包围盒(BoundingBox)的方法。通过遍历零件几何体特征点,并应用坐标变换矩阵,最终返回精确的零件包围盒。核心步骤包括初始化与输入校验、坐标变换处理、几何体特征点遍历以及全局坐标变换。关键方法涉及CATMathBox操作和坐标变换接口。该方法适用于碰撞检测、可视化优化和装配分析等场景,能够快速判断零件间的空间干涉,提升渲染性能,并计算零件在装配体中的全局位置范围。

工业软件开发 894

伸展树(splay tree)

对于二叉查找树而言,每次操作的最坏时间复杂度是O(N)。(当树退化为链表的时候)。为了解决这个问题,我们给树附加了一个平衡条件。平衡条件限制了任何节点的深度都不能过深。其中一种限制条件是:一颗二叉查找树的左子树和右子树的高度差不能超过1,这个条件限制产生了AVL树。 二叉查找树的最坏操作是O(N)。但是这样的操作并不常见。所以累加起来的时间就变得比单次操作时间的...

weixin_30611509的博客 703

2024-江苏科技大学-模拟-动态接触角,模拟液滴撞击,comsol软件模拟。

相反,其对液滴粘度和平衡接触角的变化基本不敏感。重力势能的影响显著受过程参数调节,随着液滴直径和密度的增加而增加,而随着初始液滴速度和表面张力系数的增加而减小。通过考虑形状因子和粘度修正因子,可以准确定义表面能和粘性耗散能,使得所提出的半经验修正勺状模型表现出良好的性能,在所有65个研究案例中,液滴最大铺展长度因子(βmax)和中心厚度(h)的相对误差基本分别落在5%和10%的区间内。重力势能的影响受过程参数显著调节,随液滴直径和密度的增大而增大,随液滴初始速度和表面张力系数的增大而减小。

qq_15963745的博客 512

树:伸展树

二叉搜索树中左子树的值小于根节点的值,右子树的值大于根节点的值。当要搜索的项目是根节点或根节点的左子节点时,我们使用锯齿形旋转。在这种情况下,首先,我们执行了锯齿形旋转,然后执行了锯齿形旋转。假设我们得到了一个具有不同节点的二叉搜索树,我们知道在二叉搜索树中,左边的元素比根节点小,右边的元素比根节点大。这种旋转类似于之字形旋转,唯一的区别是这里每个节点向左移动一个位置,然后向当前位置的右侧移动一个位置。在本例中,首先,我们执行了锯齿形旋转,然后执行了锯齿形旋转。下面给出的旋转是我们将在张开树中执行的旋转。

新华编程特战队 1155

搭建一个简单的 Django + Vue 项目

在本文中,我将为您详细介绍如何搭建一个简单的 Django + Vue 项目。Django 是一个使用 Python 编写的高级 Web 框架,而 Vue 是一个流行的 JavaScript 框架,用于构建用户界面。通过结合 Django 和 Vue,我们可以创建强大的全栈 Web 应用程序。创建 Django 项目设置 Django 后端创建 Vue 前端连接 Django 和 Vue让我们开始吧!

JfpBlockchain的博客 352

PCL 4PCS算法实现点云粗配准【2026最新版】

博客长期更新,本文最新更新时间为:2026年5月11日。

点云侠的博客 30万+

伸展树(Splay Tree)

伸展树(Splay Tree),又称分裂树,是一种在AVL树基础上进一步放松平衡条件的二叉排序树,它能够在O(log n)时间复杂度内完成插入、查找和删除操作。伸展树上的一般操作都是基于伸展操作的,还可以说伸展树是一种自调整形式的二叉查找树,他会沿着从某个节点到树根之间的路径,将当前访问的节点通过一系列的旋转操作迁移到树根。很重要的一点是:伸展树不需要维护平衡因子。它由丹尼尔·斯立特Daniel S

Bruce 1770

银河麒麟编译libjpeg库并配置qt环境

银河麒麟下编译libjpeg库并配置qt环境验证

小米的修行之路 1012

一个伸展树的实现

伸展树实现(java语言版本) 一、伸展树的基本概念 伸展树是一种二叉搜索树,伸展树也是一种平衡树,不过伸展树并不像AVL树那样对树的平衡有很严格的要求(左右孩子高度之差不能超过1),伸展树通过一系列的伸展操作,可以保证对伸展树的任意连续M次操作,其时间复杂度不会超过MlogN级别,并且伸展树的实现相较于AVL树也简单了很多,不论是插入还是删除算法 二、伸展树的伸展操作 伸展树之所以能够保证MlogN的界,就是因为伸展树的伸展操作,每一次调用伸展树的search方法查找一个结点的时候,如果存在,那么就

西城风雨楼 737

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

java中的二叉树超全基础知识(小白版)

本文详细介绍了二叉树的基础知识以及如何使用java代码实现二叉树,希望对大家有帮助。

于枭森的博客 362

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C++编写Splay Tree——算法设计与分析,详细解释

C++编写Splay Tree,带你详细了解Splay Tree原理和代码编写

m0_56222998的博客 555

平衡二叉树之伸展树(Splay Tree)原理

伸展树是一种自平衡二叉搜索树自适应性:每次访问节点(查找、插入、删除)后,通过“伸展(Splay)”操作将该节点移动到根位置。无需额外存储平衡信息:不依赖AVL树的平衡因子或红黑树的颜色标记,仅通过旋转操作调整结构。局部性原理:频繁访问的节点会被移动到靠近根的位置,减少后续访问时间。优点实现简单,无需额外平衡信息。适合局部性强的访问场景。缺点单次操作可能较慢(但摊仍高效)。严格实时系统需谨慎使用。伸展树通过动态调整结构,在多数场景下提供高效操作。

xiaoyu❅的博客 910

数据结构之伸展树(Splay Tree)

伸展树(Splay Tree)是一种自调整的二叉搜索树,由Daniel Sleator和Robert Tarjan在1985年提出。它通过一种称为"伸展"(splay)的特殊操作,将最近访问的节点移动到根节点附近,从而提高后续访问的效率。 与传统的平衡二叉搜索树(如AVL树、红黑树)不同,伸展树不保证树在任何时刻都保持平衡。相反,它通过动态调整树的结构,使得频繁访问的节点更靠近根节点,从而在长期运行中实现良好的平均性能。

d3y1`5 81_09 641

伸展树(Splay tree)图解与实现

个人技术博客:http://blog.ztgreat.cn 一、伸展树    本文介绍了二叉查找树的一种改进数据结构–伸展树(Splay Tree)。它的主要特点是不会保证树一直是平衡的,但各种操作的平摊时间复杂度是O(log n),因而,从平摊复杂度上看,二叉查找树也是一种平衡二叉树。另外,相比于其他树状数据结构(如红黑树,AVL树等),伸展树的空间要求与编程复杂度要小得多。 伸展树...

小张的专栏 4万+

伸展树(Splay)理论-笔记

简介 前置知识: 树->二叉搜索树->平衡树->AVL树->Treap->伸展树 左单(双)旋、右单(双)旋、左右双旋 伸展树(Splay Tree) 是平衡二叉查找树的一种,具有二叉查找树的所有性质;伸展树又称Self-Adjusting Search Trees,即自调整的二叉搜索树 。与普通的二叉查找树相比,其维护更少的节点额外信息,空间性能更优且编程复杂度...

帅比王的博客 1754

splay tree java移除_伸展树(三)之 Java的实现

概要前面分别通过C和C++实现了伸展树,本章给出伸展树的Java版本。基本算法和原理都与前两章一样。1.2.3.伸展树的介绍伸展树(Splay Tree)是特殊的二叉查找树。它的特殊是指,它除了本身是棵二叉查找树之外,它还具备一个特点: 当某个节点被访问时,伸展树会通过旋转使该节点成为树根。这样做的好处是,下次要访问该节点时,能够迅速的访问到该节点。伸展树的Java实现1. 基本定义public ...

weixin_35562225的博客 363

Splay详讲

编者注:(为了更好的理解Splay,需要读者对Treap有一定的了解) 那么话不多说,我们直接进入正题吧: 什么是Splay 伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。 学过Treap的人或许会问那么Treap和Splay到底有什么区别呢? 其实很简单,相比于Treap,Splay能够更好的处理区间的问题,虽然Treap好像也可...

Zbw_OIer的博客 927

Splay Tree(伸展树)

假设想要对一个二叉查找树执行一系列的查找操作。为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法,在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。splay tree应运而生。splay tree是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。重构方法1、单旋:在查

jia_xiaoxin的专栏 1289

高级搜索树之伸展树

伸展树(Splay Tree)是平衡二叉搜索树的一种形式。相对于 AVL,伸展树的实现更为简捷。伸展树无需时刻都严格地保持全树的平衡,但却能够在任何足够长的真实操作序列中,保持分摊意义上的高效率。伸展树也不需要对基本的二叉树节点结构,做任何附加的要求或改动,更不需要记录平衡因子或高度之类的额外信息,故适用范围更广。

_ 3121

普通平衡树 Splay

普通平衡树,Splay

wanglianda2007的博客 1809
上一篇: 从语言归纳语法的5种经验方法
下一篇: 补充:消除epsilon产生式等
cuiods
博客等级 码龄12年 41粉丝 15原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值