Racket链表与二叉树

不管是链表还是二叉树都是链式存储结构,用习惯的话说就是需要数据域+指针域(如果对cpp等传统语言实现链表有所了解的话)

lisp中也需要使用结构体来实现它

结构体

struct在官方文档的页面:Racket - 5 Structures

可以通过以下示例程序了解它的用法

#lang racket

(struct point (x y) #:mutable)

(define p (point 3 4))

(displayln (point-x p)) ; 访问结构体字段 x 的值 输出3
(displayln (point-y p)) ; 访问结构体字段 y 的值 输出4

(define p2 (point 1 2))

(displayln (point? p2)) ; 检查变量是否为结构体 输出#t
(displayln (point? 'hello)) ; 检查变量是否为结构体 输出#f

(set-point-x! p 6)
(set-point-y! p 2)

(displayln (point-x p)) ; 输出6
(displayln (point-y p)) ; 输出2

结构体字段的访问器(getter)和修改器(setter)在Racket中是自动生成的,它们遵循了一致的命名规则,不难发现结构体原型名-字段名就是访问器,使用方法是(结构体原型名-字段名 结构体实例名),而修改器则是set-结构体原型名-字段名!,使用方法是(set-结构体原型名-字段名! 结构体实例名 修改为的值)

需要注意的是为了能够修改结构体字段的值,应当在定义的时候标记为#:mutable,该选项的意思见下方。

二叉树

#lang racket
; val : integer?
; left : (or/c tree-node? #f)
; right : (or/c tree-node? #f)
(struct tree-node
  (val left right) #:mutable #:transparent)

; 构造函数,本质也是对
(define (make-tree-node [val 0])
  (tree-node val #f #f))

(define left-node (make-tree-node 4)) ; 创建左子树节点,值为 4
(define right-node (make-tree-node 6)) ; 创建右子树节点,值为 6
(define root (tree-node 10 left-node right-node))	; 创建root节点,其值为10

; 尝试访问
(tree-node-left root)	; 输出(tree-node 4 #f #f)
(tree-node-val root)	; 输出10
  • #:mutable 选项表示创建的结构体实例是可变的(mutable)。这意味着可以使用 set! 等操作来修改结构体实例的字段值。如果不使用 #:mutable 选项,默认情况下结构体实例是不可变的。
  • #:transparent 选项表示创建的结构体是透明的(transparent)。透明结构体指的是在访问结构体字段时,可以直接使用字段名而不需要通过访问器函数。这样可以使代码更简洁。如果不使用 #:transparent 选项,默认情况下访问结构体字段需要使用访问器函数。

再来一个三大遍历的例子:

#lang racket
; 定义 tree-node 结构体
(struct tree-node
  (val left right) #:mutable #:transparent)

; 创建二叉树的函数
(define (create-binary-tree)
  ; 创建节点
  (define node1 (tree-node 1 #f #f))
  (define node2 (tree-node 2 #f #f))
  (define node3 (tree-node 3 #f #f))
  (define node4 (tree-node 4 #f #f))
  (define node5 (tree-node 5 #f #f))

  ; 构建二叉树
  (set-tree-node-left! node1 node2)
  (set-tree-node-right! node1 node3)
  (set-tree-node-left! node2 node4)
  (set-tree-node-right! node2 node5)

  ; 返回根节点
  node1)

; 遍历二叉树(前序遍历)
(define (preorder-traversal node)
  (cond
    [(tree-node? node)
     (displayln (tree-node-val node))
     (preorder-traversal (tree-node-left node))
     (preorder-traversal (tree-node-right node))]
    [else #f]))

; 中序遍历二叉树
(define (inorder-traversal node)
  (cond
    [(tree-node? node)
     (inorder-traversal (tree-node-left node))
     (displayln (tree-node-val node))
     (inorder-traversal (tree-node-right node))]
    [else #f]))

; 后序遍历二叉树
(define (postorder-traversal node)
  (cond
    [(tree-node? node)
     (postorder-traversal (tree-node-left node))
     (postorder-traversal (tree-node-right node))
     (displayln (tree-node-val node))]
    [else #f]))

; 创建二叉树
(define root (create-binary-tree))

; 遍历二叉树(前序遍历)
(displayln "Preorder Traversal:")
(preorder-traversal root)

; 遍历二叉树(中序遍历)
(displayln "Inorder Traversal:")
(inorder-traversal root)

; 遍历二叉树(后序遍历)
(displayln "Postorder Traversal:")
(postorder-traversal root)

输出结果是:

Preorder Traversal:
1
2
4
5
3
#f
Inorder Traversal:
4
2
5
1
3
#f
Postorder Traversal:
4
5
2
3
1

练习:LeetCode - 2236. 判断根结点是否等于子结点之和

链表

#lang racket

; val : integer?
; next : (or/c list-node? #f)
(struct list-node
  (val next) #:mutable #:transparent)

; constructor
(define (make-list-node [val 0])
  (list-node val #f))

; 创建链表
(define node1 (make-list-node 2))
(define node2 (make-list-node 4))
(define node3 (make-list-node 1))
(define node4 (make-list-node 5))
(define node5 (make-list-node 6))

(set-list-node-next! node1 node2)
(set-list-node-next! node2 node3)
(set-list-node-next! node3 node4)
(set-list-node-next! node4 node5)

; 遍历链表并输出值
(define (traverse-list node)
  (cond
    [(list-node? node)
     (displayln (list-node-val node))
     (traverse-list (list-node-next node))]
    [else #f]))

; 遍历输出链表
(traverse-list node1)

输出是:

2
4
1
5
6
#f

练习:LeetCode - 21. 合并两个有序链表

提一句,这题可以用递归来写。

猜你喜欢

转载自blog.csdn.net/qq_39377889/article/details/130928707