#Swift 递归枚举 VS Struct 实现自引用 数据结构 (链表 二叉树)

news2024/11/27 20:58:40

Enumeration 和 Struct 是 value type 实现 自引用 (self retain) 的数据类型,可以有效避免引用计数管理的问题(Class 是 reference type),递归枚举 因为在自引用类型的使用上和处理上不需要考虑 Struct 实现的空节点 Optional 类型的处理,更readable一些。

在 Swift 中,递归枚举(Recursive Enumerations) 是指一种枚举类型,其中一个或多个枚举 case 可能包含该枚举类型本身作为关联值。这允许你构建数据结构,如树或链表等,递归枚举是处理这些自引用结构的一种重要工具。

递归枚举的定义

由于 Swift 编译器需要知道枚举的内存布局,而递归枚举可能导致编译器无法直接推断它的大小,因此你需要使用 indirect 关键字来声明递归枚举。

indirect 可以用在整个枚举前,也可以只标记递归 case

递归枚举的常见用法场景

  1. 表达式求值:在编程语言中,表达式的结构通常是递归的,比如算术表达式,可以嵌套其他表达式。递归枚举可以用来表示和计算这样的嵌套表达式。
  2. 树结构:许多算法和数据结构,如二叉树或文件系统,都是递归定义的结构。每个节点可能有多个子节点,而子节点本身又是同样的类型。
  3. 链表:链表是一种递归的数据结构,每个节点指向下一个节点,直到链表结束。

让我们详细看一下递归枚举在这些场景中的应用。

1. 表达式求值的递归枚举

递归枚举非常适合用来表示像算术表达式这样的递归结构。比如表达式 (5 + 4) * 2,你可以使用递归枚举表示加法和乘法之间的嵌套关系:

indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case .number(let value):
        return value
    case .addition(let left, let right):
        return evaluate(left) + evaluate(right)
    case .multiplication(let left, let right):
        return evaluate(left) * evaluate(right)
    }
}

// 构造表达式 (5 + 4) * 2
let expression = ArithmeticExpression.multiplication(
    .addition(.number(5), .number(4)),
    .number(2)
)

// 计算表达式的值
print(evaluate(expression))  // 输出 18
这里递归枚举的好处
  • 枚举中的每个 case 可以递归地包含其他表达式。
  • 你可以用很简单的代码构建嵌套的表达式结构,并用递归函数 evaluate 来处理每个 case
  • 递归定义非常直观,表达式中的嵌套逻辑由递归结构自然表达。

2. 树结构的递归枚举

递归枚举非常适合表示树状结构,比如文件系统或组织架构。树状结构每个节点可能有多个子节点,而每个子节点本身也是一个子树。

indirect enum BinaryTree {
    case empty
    case node(Int, BinaryTree, BinaryTree)
}

// 创建一个简单的二叉树
let leftChild = BinaryTree.node(2, .empty, .empty)
let rightChild = BinaryTree.node(3, .empty, .empty)
let root = BinaryTree.node(1, leftChild, rightChild)

// 定义一个递归函数来遍历树
func traverseInOrder(_ tree: BinaryTree) {
    switch tree {
    case .empty:
        return
    case .node(let value, let left, let right):
        traverseInOrder(left)
        print(value)
        traverseInOrder(right)
    }
}

traverseInOrder(root)
// 输出:
// 2
// 1
// 3
递归枚举的好处
  • 二叉树的每个节点有两个子节点,递归定义树的结构非常自然。
  • 通过递归函数 traverseInOrder,你可以很方便地遍历每个节点,进行各种操作,比如打印、求和、查找等。

3. 链表的递归枚举

链表是一种简单的递归数据结构。每个链表节点包含数据,并指向下一个节点。

indirect enum LinkedList {
    case empty
    case node(Int, LinkedList)
}

// 创建一个链表:1 -> 2 -> 3 -> 空
let list = LinkedList.node(1, .node(2, .node(3, .empty)))

// 定义一个递归函数来遍历链表
func printList(_ list: LinkedList) {
    switch list {
    case .empty:
        return
    case .node(let value, let next):
        print(value)
        printList(next)
    }
}

printList(list)
// 输出:
// 1
// 2
// 3
递归枚举的好处
  • 链表本质上就是递归的结构,节点指向下一个节点,直到 empty
  • 使用递归枚举可以直观地表示链表结构,并通过递归函数轻松遍历链表。

总结

递归枚举非常适合处理以下场景:

  1. 表达式求值:可以用递归枚举构建表达式树,简化处理。
  2. 树结构:用递归枚举可以方便地表示二叉树或多叉树的结构,并通过递归遍历和操作树节点。
  3. 链表结构:链表的递归本质和递归枚举完美匹配,链表的操作变得非常自然。

使用 struct 实现链表

链表是一种递归结构,其中每个节点包含一个值和指向下一个节点的引用。我们可以用 struct 来实现链表的递归结构。通常情况下,链表会有一个表示空节点的 nil,我们可以通过 Optional 类型来表示这一点。

链表的定义
struct LinkedList<T> {
    var value: T
    var next: LinkedList<T>?
    
    init(value: T, next: LinkedList<T>? = nil) {
        self.value = value
        self.next = next
    }
}

在这个链表实现中:

  • LinkedList<T> 是一个泛型结构体,可以存储任何类型的数据。
  • 每个节点包含一个 value,表示当前节点的值。
  • next 是一个可选类型(LinkedList<T>?),表示下一个节点,如果链表结束,则 nextnil
创建一个链表

我们可以用这个结构体创建一个链表,比如 1 -> 2 -> 3 -> nil

let node3 = LinkedList(value: 3)
let node2 = LinkedList(value: 2, next: node3)
let node1 = LinkedList(value: 1, next: node2)

这个链表是:

1 -> 2 -> 3 -> nil
遍历链表

我们可以写一个函数来递归遍历链表:

func printList(_ list: LinkedList<Int>?) {
    guard let list = list else { return }
    print(list.value)
    printList(list.next)
}

printList(node1)
// 输出:
// 1
// 2
// 3

在这里,printList 函数通过递归遍历链表的每个节点,并打印出每个节点的值。

使用 struct 实现二叉树

二叉树是一种递归数据结构,其中每个节点最多有两个子节点:左子节点和右子节点。我们也可以用 struct 来实现二叉树的递归结构。

二叉树的定义
struct BinaryTree<T> {
    var value: T
    var left: BinaryTree<T>?
    var right: BinaryTree<T>?
    
    init(value: T, left: BinaryTree<T>? = nil, right: BinaryTree<T>? = nil) {
        self.value = value
        self.left = left
        self.right = right
    }
}

在这个二叉树实现中:

  • BinaryTree<T> 是一个泛型结构体,可以存储任何类型的数据。
  • 每个节点包含一个 value,表示当前节点的值。
  • leftright 是可选类型,表示左子树和右子树。如果子树不存在,则为 nil
创建一个二叉树

我们可以创建一个简单的二叉树,例如:

    1
   / \
  2   3
let leftChild = BinaryTree(value: 2)
let rightChild = BinaryTree(value: 3)
let root = BinaryTree(value: 1, left: leftChild, right: rightChild)
遍历二叉树

我们可以写一个递归函数来进行二叉树的遍历。例如,中序遍历(先左子树,后根,再右子树):

func inOrderTraversal<T>(_ tree: BinaryTree<T>?) {
    guard let tree = tree else { return }
    inOrderTraversal(tree.left)
    print(tree.value)
    inOrderTraversal(tree.right)
}

inOrderTraversal(root)
// 输出:
// 2
// 1
// 3

在这个递归函数中:

  • 递归遍历左子树。
  • 打印当前节点的值。
  • 递归遍历右子树。

总结

  • 链表:通过递归的 next 指针,每个节点指向下一个节点,直到链表结束。
  • 二叉树:通过递归的 leftright 指针,每个节点最多有两个子节点,分别指向左子树和右子树。

对比分析

  • struct 在实现递归结构时更像是传统的数据结构定义,适合简单的递归情况,尤其是在不需要表示多种状态时,struct 提供了更直观的语法。

  • enum 更适合表达有明确状态区分的递归结构,如树结构中的叶子节点、分支节点、空节点等。通过 enumcase,你可以更清晰地表达这些状态,并使用模式匹配来处理递归逻辑。

选择 struct 还是 enum 取决于你的数据结构的复杂性和状态的需求:

  • 如果只是简单地描述一组数据,struct 更简洁。
  • 如果数据结构有多个状态,且不同状态需要不同的处理逻辑,enum 是更好的选择。

这两者在递归结构上的区别主要在于状态的表达方式和处理方式上的差异。

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

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

相关文章

Koa学习

Koa 安装与配置 1. 初始化项目 在终端中执行以下命令&#xff1a; # 创建项目文件夹 mkdir koa cd koa# 初始化并安装依赖 npm init -y npm install koa npm install nodemon --save-dev2. 修改 package.json 在 package.json 文件中进行如下修改&#xff1a; {"type…

LeetCode讲解篇之1143. 最长公共子序列

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 这题我们可以采用动态规划求解&#xff0c;用一个二维数组记录text1的0 ~ i区间子串和text2的0 ~ j区间子串的最长公共子序列的长度&#xff0c;我们假设该二维数组是f 这个数组有一个特性&#xff0c;如果a <…

ssm服装店销售管理系统

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 摘 要 I Abstract II 第1章 绪论 1 1.1研究背景 1 1.2研究意义 1 1.3国内外研究现状 2 1.3.1国外研…

R语言中的plumber介绍

R语言中的plumber介绍 基本用法常用 API 方法1. GET 方法2. POST 方法3. 带路径参数的 GET 方法 使用 R 对数据进行操作处理 JSON 输入和输出运行 API 的其他选项其他功能 plumber 是个强大的 R 包&#xff0c;用于将 R 代码转换为 Web API&#xff0c;通过使用 plumber&#x…

启动hadoop后没有 NodeManager和 ResourceManager

跟着黑马网课学下去时发现我的hadoop启动后没有NodeManager和ResourceManager 找到日志的路径 我在/export/server/hadoop/etc/hadoop/hadoop-env.sh文件里配置了日志存放的路径 这里找到你的日志路径&#xff0c;每个人的习惯和看的教程不同&#xff0c;日志放的地方大概率也…

MATLAB中lsqminnorm函数用法

目录 语法 说明 示例 求解具有无限个解的线性系统 指定容差以减少含噪数据的影响 切换显示低秩矩阵警告 lsqminnorm函数的功能是线性方程的最小范数最小二乘解。 语法 X lsqminnorm(A,B) X lsqminnorm(A,B,tol) X lsqminnorm(___,rankWarn) 说明 X lsqminnorm(A,B…

【大语言模型-论文精读】用于医疗领域摘要任务的大型语言模型评估综述

【大语言模型-论文精读】用于医疗领域摘要任务的大型语言模型评估综述 论文信息&#xff1a; 用于医疗领域摘要任务的大型语言模型评估&#xff1a;一篇叙述性综述&#xff0c; 文章是由 Emma Croxford , Yanjun Gao 博士 , Nicholas Pellegrino , Karen K. Wong 等人近期合作…

【Arduino IDE安装】Arduino IDE的简介和安装详情

目录 &#x1f31e;1. Arduino IDE概述 &#x1f31e;2. Arduino IDE安装详情 &#x1f30d;2.1 获取安装包 &#x1f30d;2.2 安装详情 &#x1f30d;2.3 配置中文 &#x1f30d;2.4 其他配置 &#x1f31e;1. Arduino IDE概述 Arduino IDE&#xff08;Integrated Deve…

Spring Boot医院管理系统:提升患者体验

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

python pass的作用

class Phone: IMEI None # 序列号 producer “ITCAST” # 厂商 def call_by_4g(self):print("4g通话")class Phone2022(Phone): face_id “10001” # 面部识别ID def call_by_5g(self):print("2022年新功能&#xff1a;5g通话")class NFCReader: nfc_ty…

​​​​​​​如何使用Hugging Face上的FacePoke工具调整照片中人的头部位置

在照片处理中&#xff0c;调整人物的头部位置可以为你带来创意无限的效果。借助Hugging Face上的FacePoke工具&#xff0c;这一操作变得前所未有的简单和高效。以下是详细步骤&#xff0c;教你如何使用FacePoke来调整照片中人的头部位置。 第一步&#xff1a;访问FacePoke工具…

二、Python(项目创建、常见的设置、print函数)

一、项目创建 二、常见设置 1.字体大小 2.插件 3.主题设置 4.配置解释器 三、print函数 在 Python 中&#xff0c;print()函数是一个非常重要的用于输出内容的函数。 作用&#xff1a;print函数在控制台中输出(显示,打印)括号中的内容&#xff0c;可以用于验证代码的输出结果…

【Kubernetes】常见面试题汇总(五十八)

目录 127.创建 PV 失败&#xff1f; 128. pod 无法挂载 PVC&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-113 属于【Kubernetes】…

SpringBoot开发——SpringSecurity安全框架17个业务场景案例(一)

文章目录 一、Spring Security 常用应用场景介绍二、Spring Security场景案例1、认证(Authentication)1.1. Spring Security 配置1.2 业务逻辑代码1.3 登录控制器1.4 登录页面 (login.html)2、授权(Authorization)2.1 Spring Security 配置2.2 业务逻辑代码2.3 控制器3、表…

如何判断静态代理IP地址是否被污染?

在网络使用中&#xff0c;静态IP代理是一种常见的工具&#xff0c;用于维持稳定的连接和保护个人隐私。然而&#xff0c;有时这些IP地址可能会被污染&#xff0c;导致用户遭受各种问题&#xff0c;如连接延迟、数据泄露等。因此&#xff0c;了解如何判断址是否被污染至关重要。…

EPC User Manual Introduction

Overview 您提供的链接是指向srsRAN 4G项目的官方文档&#xff0c;具体是关于srsEPC的介绍部分。以下是该页面的核心内容概要&#xff1a; ### 概述 srsEPC是一个轻量级的完整LTE核心网络&#xff08;EPC&#xff09;实现。srsEPC应用程序作为一个单一的二进制文件运行&#…

【高阶数据结构】二叉树进阶探秘:AVL树的平衡机制与实现详解

高阶数据结构相关知识点可以通过点击以下链接进行学习一起加油&#xff01;二叉搜索树 大家好&#xff0c;这里是店小二&#xff01;今天我们将深入探讨高阶数据结构中的AVL树。AVL树是一种自平衡的二叉搜索树&#xff0c;可以看作是对传统二叉搜索树的优化版本。如果你对数据结…

828华为云征文|华为云Flexus云服务器X实例部署 即时通讯IM聊天交友软件——高性能服务器实现120W并发连接

营运版的即时通讯IM聊天交友系统&#xff1a;特点可发红包&#xff0c;可添加多条链接到用户网站和应用&#xff0c;安卓苹果APPPC端H5四合一 后端开发语言&#xff1a;PHP&#xff0c; 前端开发语言&#xff1a;uniapp混合开发。 集安卓苹果APPPC端H5四合一APP源码&#xff0…

AI学习记录 - L2正则化详细解释(权重衰减)

大白话&#xff1a; 通过让反向传播的损失值变得比原来更大&#xff0c;并且加入的损失值和权重的大小有关&#xff0c;当出现权重的平方变大的时候&#xff0c;也就是权重往更加负或者更加正的方向走的时候&#xff0c;损失就越大&#xff0c;从而控制极大正或者极大负的情况…

在宝塔面板中部署 Express + MongoDB + Uniapp h5 项目(超详细!!!)

文章目录 一、打包 uniapp h5 项目(1) 打开 manifest.json 文件&#xff0c;修改相关配置(2) 开始项目打包 二、修改 express 相关配置(1) 添加打包后的前端资源文件(2) 修改 app.js 文件(3) 修改项目启动命令 三、使用宝塔面板部署项目(1) 宝塔面板安装(2) 项目环境搭建 四、添…