通用树查找算法

news2024/10/6 8:37:07

        想要一个树形控件来显示数据,却发现Racket的GUI库竟然没有提供这个控件。既然没有,那就自己手搓一个吧。没想到,在做这个控件中竟然有了新发现!

        树形控件有一个功能是查找树中指定的节点。这就是接下来的故事的起点。

1 找外援

        不想动脑筋,就找网络,网上一般都是二叉树、红黑树啥的,不是我需要的;找AI帮我写一个,如下:

问:用Racket写一个通用的树查找算法。

AI答:

以下是使用Racket编写的通用树查找算法:

#lang racket
(define (tree? x)
  (and (pair? x) (not (null? x))))
(define (search-tree t target)
  (cond ((null? t) #f)
        ((equal? (car t) target) t)
        (else (+ 1 (search-tree (cdr t) target)))))
(define tree
  '(("A" "B" "C") ("D" "E" "F") ("G" "H" "I")))
(displayln "Tree:")
(tree-walk tree tree-root tree)
(displayln "Search for 'D':")
(search-tree tree 'D)

        这个程序定义了一个辅助函数 ‘tree?‘,用于检查一个值是否为树。然后,它定义了 ‘search-tree‘ 函数,该函数接受一个树和目标值作为参数,并返回找到的目标节点或 "#f"(表示未找到)。最后,它创建了一个名为“tree”的示例树,并使用“tree-walk”函数遍历它。然后,它在树中搜索目标值“D”,并将结果打印到控制台上。

显然,AI也不是救世主!还是开动自己的思想解决问题吧。

2 我的思考

         树的根节点、子节点、叶子节点的处理属于基本操作,不用过多考虑。重点是不确定的子节点数量,如何处理?

        以下是一个通用树节点的结构:

;树节点结构:
(struct node
(id ;这里设定id值为数字。
 value ;节点值。
 children ;子节点,考虑用列表来存放。
 ))

        我实际要使用的树节点比这个要复杂一些,这里我就把这个结构来凑合作为例子样本吧。

        如上面所说,重点就是children这个列表了。一般会考虑采用"for-each"遍历列表元素,然后用递归方式对每个元素的children继续以上过程,直到找到需要的值或遍历整个树为止。

        但这样有一个严重的问题,这样的每次递归都会出现等待下一级递归结束后才能继续列表元素处理的问题,就会出现著名的低效递归的问题——因为这样递归在执行时需要系统提供隐式栈来实现递归,造成效率低效,甚至填满内存的严重问题(当然现在内存一般足够大,还不至于因内存溢出造成崩溃)。

        解决办法是采用尾递归。难点还是在children列表上。

        我找到一个办法——将列表与树结合起来进行尾递归,算法原理如下。

3 算法原理

        假设我们有一个查找函数"find-node",其参数有两个——"tree"为要查找的树;"id"为查找的关键值。

        在"find-node"中定义一个闭包"find-node/in",它来实现树的遍历查找,这也是常规操作。

        为了实现尾递归,为"find-node/in"设定一个参数nodes作为存放树节点的子节点的列表(根节点是这个列表参数的初始值)。然后挨个搜索树的各个节点,如果遇到节点有子节点时,就把这个节点的children添加到nodes的后面。这样就把对树的搜索和对子节点列表的遍历转换为对列表nodes的遍历,处理起来就异常简单了。关键是,复杂度也很理想,这个最后分析。

具体算    法描述如下:

  1. 首先,nodes为包含节点的列表。

  2. 初始时nodes里仅包含根节点。

  3. 如果nodes列表为空,返回#f;

  4. 如果nodes列表不为空,取列表首值(节点):

  5. 如果该值是指定节点,返回该节点;

  6. 如果该值不是指定节点,检查其子节点:

  7. 如果有子节点,将子节点加入到nodes列表里,并重复3-6对nodes向后搜索;

  8. 如果没有子节点,重复3-6对nodes向后搜索。

        注意,以上算法描述中还需要考虑一个特例,就是根节点不存在(一般设置为"#f")时的情况。为了处理这个特例,我们在取出列表首值后首先对其做一个判断——是否为未设置节点值的根节点。

4 实现树查找

        根据上边的算法原理,我们把代码写出来,如下:

;定义取得节点值函数:
;tree为需要搜索的树,id为节点标识。
(define (find-node-version-1 tree id)
  (define (find-node/in nodes)
    (cond
      ;nodes列表为空,返回#f:
      [(empty? nodes) #f]
      ;值(节点)#f,即树根未初始化,树不存在:
      [(not (car nodes)) #f]
      ;nodes列表首值(节点)即为指定节点,返回节点:
      [(= (node-id (car nodes)) id)
       ;(display-node-value (car nodes))
       (car nodes)]
      ;如果nodes首值不是指定节点,且其子列表为空:
      [(empty? (node-children (car nodes)))
       ;(display-node-value (car nodes))
       (find-node/in (cdr nodes))]
      [else
       ;(display-node-value (car nodes))
       (find-node/in
        (append (cdr nodes)
                (node-children (car nodes))))]))
  (find-node/in (list tree)))

5 测试

        现在手搓一棵树并放到测试模块中来做测试。如下:

(module+ test
  ;测试空树:
  (define tree/empty #f)
  (find-node tree/empty 3)
  ;测试自定义树:
  (define tree/user
    (node 0 "t-0" (list (node 1 "t-1" '())
                        (node 2 "t-2" (list (node 3 "t-3" (list (node 4 "t-4" '())))
                                            (node 5 "t-5" '())
                                            (node 6 "t-6" (list (node 7 "t-7" '())
                                                                (node 8 "t-8" '())))))
                        (node 9 "t-9" (list (node 10 "t-10" (list (node 11 "t-11" '())))
                                            (node 12 "t-12" '())))
                        (node 13 "t-13" (list (node 14 "t-14" (list (node 15 "t-15" (list (node 16 "t-16" '()))))))))))
  (let* ([id 13]
         [n (find-node-version-1 tree/user id)])
    (display (format "查找结果为:id=~a,value=”~a“\n" id (node-value n))))
  )

这棵树如果在手机这种移动设备上看可能有点乱。如果用图片显示应该是这样的:

        运行程序,得到的结果是:

        查找结果为:id=13,value=”t-13“

6 检查搜索路径

        我们来看看程序的搜索路径。

        为了实现这个功能,我再定义一个显示函数,以便显示出程序搜索的每一个节点情况。如下:

;显示节点值(用于测试):
(define (display-node-value node)
  (display
   (format "id=~a,value=”~a“\n"
           (node-id node)
           (node-value node))))

        也就是上边程序中被注释掉的函数。现在我们把它的注释去掉。再运行程序,得到以下结果:

        id=0,value=”t-0“

        id=1,value=”t-1“

        id=2,value=”t-2“

        id=9,value=”t-9“

        id=13,value=”t-13“

        哦,这不是遍历树的广度优先算法吗!这的确是遍历树的广度优先算法。

        我们可以把这个算法独立出来用单独函数表示。如下:

;广度优先搜索:
(define (find-node/width nodes id)
  (cond
    ;nodes列表为空,返回#f:
    [(empty? nodes) #f]
    ;值(节点)#f,即树根未初始化,树不存在:
    [(not (car nodes)) #f]
    ;nodes列表首值(节点)即为指定节点,返回节点:
    [(= (node-id (car nodes)) id)
     ;(display-node-value (car nodes))
     (car nodes)]
    ;如果nodes首值不是指定节点,且其子列表为空:
    [(empty? (node-children (car nodes)))
     ;(display-node-value (car nodes))
     (find-node/width (cdr nodes) id)]
    [else
     ;(display-node-value (car nodes))
     (find-node/width
      (append (cdr nodes)
              (node-children (car nodes)))
      id)]))

相应地,查找函数就表示为:

;定义取得节点值函数:
;tree为需要搜索的树,id为节点标识。
(define (find-node tree id)
  ;广度优先搜索指定节点:
  (display (format "广度优先搜索指定节点:\n"))
  (find-node/width (list tree) id))

        在测试模块中添加如下测试代码:

(let* ([id 13]
       [n (find-node tree/user id)])
    (display (format "查找结果为:id=~a,value=”~a“\n" id (node-value n))))

        运行程序,结果如下:

        广度优先搜索指定节点:

        id=0,value=”t-0“

        id=1,value=”t-1“

        id=2,value=”t-2“

        id=9,value=”t-9“

        id=13,value=”t-13“

        与上边一样的结果。

        那么问题来了,如果要实现深度优先算法,怎么做呢?

7 深度优先算法

        从上边的算法原理来理解,如果在将子节点与现有nodes列表合并时,不是将子节点放到nodes列表之后,而是放到nodes列表之前——也就是说优先搜索子节点内容——就可以实现深度优先算法了。实现如下:

;深度优先搜索:
(define (find-node/depth nodes id)
  (cond
    ;nodes列表为空,返回#f:
    [(empty? nodes) #f]
    ;值(节点)#f,即树根未初始化,树不存在:
    [(not (car nodes)) #f]
    ;nodes列表首值(节点)即为指定节点,返回节点:
    [(= (node-id (car nodes)) id)
     ;(display-node-value (car nodes))
     (car nodes)]
    ;如果nodes首值不是指定节点,且其子列表为空:
    [(empty? (node-children (car nodes)))
     ;(display-node-value (car nodes))
     (find-node/depth (cdr nodes) id)]
    [else
     ;(display-node-value (car nodes))
     (find-node/depth
      (append (node-children (car nodes))
              (cdr nodes))
      id)]))

        把深度优先算法添加到"find-node"函数中,如下:

;定义取得节点值函数:
;tree为需要搜索的树,id为节点标识。
(define (find-node tree id)
  ;广度优先搜索指定节点:
  (display (format "广度优先搜索指定节点:\n"))
  (find-node/width (list tree) id)
  ;深度优先搜索指定节点:
  (display (format "深度优先搜索指定节点:\n"))
  (find-node/depth (list tree) id))

        运行程序,得到如下结果:

广度优先搜索指定节点:

id=0,value=”t-0“

id=1,value=”t-1“

id=2,value=”t-2“

id=9,value=”t-9“

id=13,value=”t-13“

深度优先搜索指定节点:

id=0,value=”t-0“

id=1,value=”t-1“

id=2,value=”t-2“

id=3,value=”t-3“

id=4,value=”t-4“

id=5,value=”t-5“

id=6,value=”t-6“

id=7,value=”t-7“

id=8,value=”t-8“

id=9,value=”t-9“

id=10,value=”t-10“

id=11,value=”t-11“

id=12,value=”t-12“

id=13,value=”t-13

        从上边的运行结果来看,广度优先算法比深度优先算法更快找到了指定的节点。当然,仅是对于这里给的数据样本及指定的id来说的。比如,如果id=8,运行结果就会出现深度优先算法更快找到结果的情况。

8 时间复杂度与空间复杂度

        从以上分析和运行情况可以直观看出,本查找算法的时间复杂度与空间复杂度均为O(n),而且是稳定算法。

           注:以上内容采用Racket语言的Scribble编辑并编译生成。

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

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

相关文章

“神经网络之父”和“深度学习鼻祖”Geoffrey Hinton

“神经网络之父”和“深度学习鼻祖”Geoffrey Hinton在神经网络领域数十年如一日的研究,对深度学习的推动和贡献显著。 一、早期贡献与突破 反向传播算法的引入:Hinton是将反向传播(Backpropagation)算法引入多层神经网络训练的…

SOLIDWORKS参数化设计插件 慧德敏学

SOLIDWORKS软件是法国达索公司的产品,最初是满足欧美一些工程师产品设计需要而开发的,并没有考虑中国的企业实际情况。我们为满足国内客户的需要,对SOLIDWORKS进行了二次开发,借助SolidKits.AutoWorks参数化工具,通过一…

106、python-第四阶段-3-设计模式-单例模式

不是单例类,如下: class StrTools():pass str1StrTools() str2StrTools() print(str1) print(str2) 运用单例,先创建一个test.py class StrTools():pass str1StrTools()然后创建一个hello.py,在这个文件中引用test.py中的对象&a…

多尺度注意力创新

深度之眼17种多尺度注意力创新

蓝牙资讯|2024年Q1全球个人智能音频设备出货量达到9000万台

Canalys 发布了最新研究报告:2024 年第一季度,全球个人智能音频设备市场呈回暖的迹象,同比增长 6%,出货量超 9000 万台。数据显示,本季度的增长主要得益于 TWS 真无线蓝牙耳机和无线头戴式耳机的强劲表现,两…

Thesios: Synthesizing Accurate Counterfactual I/O Traces from I/O Samples——论文泛读

ASPLOS 2024 Paper 论文阅读笔记整理 问题 在设计大规模分布式存储系统时,I/O活动的建模至关重要。具有代表性的/O跟踪,可以对现有硬件、配置和策略进行详细的性能评估。假设跟踪进一步支持分析假设情况,例如部署新的存储硬件、更改配置和修…

EverWeb 强大的零基础Mac网页设计制作软件

搜索Mac软件之家下载EverWeb 强大的零基础Mac网页设计制作软件 EverWeb 4.2是非专业网页设计师的绝佳网页制作工具,无需编码即可创建美观、响应迅速的网站。只需拖放自己的图像、文本和其他任何html元素到网页布局的任何位置。 EverWeb的功能特性: 下…

企业公户验证API在Java、Python、PHP中的使用教程

在金融和商业领域,企业公户验证API是一种用于验证企业对公账户的真实性和合法性的技术解决方案。这种API通常由金融机构或第三方服务提供商提供,旨在帮助企业加快账户认证流程,提高效率,降低审核成本,并确保符合法规要…

手机短信删除怎么恢复?快速找回的3个秘密武器

手机,这个我们每天离不开的小玩意儿,有时候也会让我们头疼不已。比如,你一不小心,或者为了清理点空间,就把那些重要的短信给删了。这些短信可能是你和好友的深夜聊天,或者是重要的工作信息。一旦删除&#…

matplotlib绘制三维曲面图时遇到的问题及解决方法

在使用 Matplotlib 绘制三维曲面图时,可能会遇到一些常见的问题。今天我将全程详细讲解下遇到问题并且找到应对方法的全部过程,希望能帮助大家。 1、问题背景 在使用 matplotlib 绘制三维曲面图时,遇到了一个问题。代码如下: im…

走进 Apache 世界的另一扇大门

引言 作为热爱技术的你,是否也羡慕 Apache PMC 或者 Committer,此篇文章渣渣皮带你迈出如何成为技术大牛的第一步。 当然我现在还是一枚小小的 code contributor,在成为 committer 的路上还在奋力打码中,写这篇文章也是为大家有…

NAT技术

目录 前言一、NAT的基本思想二、NAT的局限性总结 前言 IP地址短缺问题并不是一个只有在将来某个时候可能发生的理论问题。现在,此时此地,这个问题已经发生。对于整个Internet而言,长期的解决方案是迁移到IPV6,它有128位地址。这个…

如何免费使用(白瞟)最新的开源大模型?

下面介绍两个可以免费白瞟开源大模型的网站,一个是国内的ModelScope ,点击链接注册后进入右上方的司南评测即可,界面效果如下,最新开源的Qwen2-72B也可用的噢! 另外一个 是LMSYS和UC伯克利分校联合开发的全球大模型测评平台Chatbo…

数据结构——算法和算法效率的度量

目录 一、引言 二、算法 1 算法的基本概念 2 算法的复杂度 2.1 时间复杂度 2.1.1 概念 2.1.2 大O的渐进表示 3 算法的空间复杂度 3.1 概念 3.2 实例 4 实例分析 5 结论 一、引言 大家在写代码的时候有没有发现写同样功能的代码有多种不同的写法,而不同的代…

遇见桂林山水画廊,深层互联自动讲解耳机走进漓江

遇见山水,听懂山水。由深层互联独家打造,桂林漓江山水画廊导览工程,于不久前正式启动,声情并茂的真人语音引导着游客,走进有声有色的山水画卷中。 桂林山水甲天下,得天独厚的自然景观,奇幻瑰丽…

matlab使用教程(92)—流线图、流带图和流管图

1.使用向量数据显示流线图 MATLAB 向量数据集 wind 代表北美地区的气流。本示例结合使用了几种方法: 利用流线跟踪风速 利用切片平面显示数据的横截面视图 利用切片平面上的等高线提高切片平面着色的可见性 1.1确定坐标的范围 加载数据并确定用来定位切片平面…

【WEEK15】 【DAY3】Scheduled Tasks【English Version】

2024.6.5 Wednesday Following 【WEEK15】 【DAY2】【DAY3】Email Tasks【English Version】 Contents 17. Asynchronous, Scheduled, and Email Tasks17.3. Scheduled Tasks17.3.1. Two Annotations:17.3.2. Cron Expression17.3.3. Modify Springboot09TestApplication.java …

html5实现端午节网站源码

文章目录 1.设计来源1.1 端午首页页面1.2 端午由来页面1.3 端午图集页面1.4 端午活动页面1.5 给我留言页面 2.效果和源码2.1 动态效果2.2 目录结构 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/139524377 ht…

工作备忘录软件有哪些 记事备忘提醒三合一的备忘录

在工作中,我总是觉得自己脑子好像不够用,一会儿是这个项目的进度要跟踪,一会儿是那个会议的要点要记录。每天都在忙碌与混乱中度过,我甚至开始怀疑自己是否适合这份工作。 备忘录不仅是一个简单的记事工具,像敬业签这…

【数据分享】中国高技术产业统计年鉴(2023年)

大家好!今天我要向大家介绍一份重要的高技术产业发展情况统计数据资源——《中国高技术产业统计年鉴》。这份年鉴涵盖了从2023年中国高技术产业发展情况的全面数据,并以多格式提供免费下载。(无需分享朋友圈即可获取) 数据介绍 …