不管是链表还是二叉树都是链式存储结构,用习惯的话说就是需要数据域+指针域
(如果对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
提一句,这题可以用递归来写。