当前位置: 首页 > news >正文

常用算法实现【必会】:sort/bfs/dfs

文章目录

  • 常用排序算法实现(Go版本)
  • BFS 广度优先遍历,利用queue
  • DFS 深度优先遍历,利用stack
    • 前序遍历(根 左 右)
    • 中序遍历(左根右)
    • 后序遍历(左 右 根)
    • BFS/DFS 总结

常用排序算法实现(Go版本)

// 快排(分治)
// 时间复杂度:O(nlogn) 空间复杂度:O(logn)
func QuickSort(arr []int, left, right int) {var QuickSorting = func(arr []int, left, right int) int {tmp := arr[right] // 1、从右向左依次选基准值tmpfor left < right {for arr[left] <= tmp && left < right { // 2、每轮循环中先从左向右,遇到比基准值小的数则left++left++}if left < right {arr[right] = arr[left] // 3、直至遇到比基准值大的数x=array[left]为止,然后再交换基准值与较大值x的位置(保证基准值左侧的值都比基准值tmp小)}for arr[right] >= tmp && left < right { // 4、每轮循环中再从右向左,遇到比基准值大的数则right--right--}if left < right { // 5、直至遇到比基准值小的数y=array[right]为止,然后再交换基准值与较小值y的位置(保证基准值右侧的值都比基准值tmp大)arr[left] = arr[right]}}arr[left] = tmpreturn left}if left < right {mid := QuickSorting(arr, left, right)QuickSort(arr, left, mid-1)QuickSort(arr, mid+1, right)}
}// 快排(非递归)// todo:待补充
// https://www.bilibili.com/video/av758822583/?vd_source=2c268e25ffa1022b703ae0349e3659e4
func QuickSortNotByRecursion(arr []int) {
}// 堆排(大顶堆:每个节点的值都大于或等于其左右孩子节点的值)
// 时间复杂度:O(nlogn) 空间复杂度:O(1)
func HeapSort(arr []int) {var CreateHeap = func(arr []int, i, length int) {tmp := arr[i]// 注意for循环条件:是 j<length 而不是 j<len(arr)for j := 2*i + 1; j < length; j = j*2 + 1 { // j=2i+1:当前根节点的左孩子下标 j= 2*j + 1:以当前叶子节点为新根节点,该新根节点的下一层叶子节点左孩子下标if j+1 < length && arr[j] < arr[j+1] { // j+1<length:右孩子(j+1)不能超出len长度范围j++}if tmp > arr[j] { // 左右孩子节点中选较大的节点值,并与父节点比较大小break // 若父节点值满足"大于或等于其左右孩子节点的值"则break,否则与较大的孩子节点相互交换}arr[i] = arr[j]i = j}arr[i] = tmp // 将最终比较后较小值放到合适的位置}// 首次构建堆l := len(arr)for i := l / 2; i >= 0; i-- { // 从二叉树最后一个父节点从底向上遍历(最上面的父节点:i = 0;最后一个父节点下标:i = len(arr) / 2)CreateHeap(arr, i, l)}// 再次重建堆for i := l - 1; i > 0; i-- { // 从下往上不断在每轮循环中置换出当前最大值,arr长度i也逐渐减到0arr[0], arr[i] = arr[i], arr[0] // swap 把大顶堆根节点(下标为0)上的最大值交换到末尾,置换出来.CreateHeap(arr, 0, i)}
}// 归并
// 时间复杂度:O(nlogn) 空间复杂度:O(n)
func MergeSort(arr []int, length int) {var MergeSorting = func(arr1, arr2 []int, length1, length2 int) {i, j := 0, 0tempArr := make([]int, 0 /*, length1+length2*/)// 1.分别将两个子数组中较小一方的值按大小顺序移动到临时数组tempArr中for i < length1 && j < length2 {if arr1[i] < arr2[j] {tempArr = append(tempArr, arr1[i]) // 将较小值加入临时数组tempArri++// fmt.Println("i ", i)} else {tempArr = append(tempArr, arr2[j])j++// fmt.Println("j ", j)}}// 2.肯定存在一个子数组先移动完,所以需要将另一个未移动完的有序子数组剩下的元素继续移动到tempArr中if i < length1 {tmpArr = append(tmpArr, arr1[i:]...)}if j < length2 {tmpArr = append(tmpArr, arr2[j:]...)}// 3.将合并数组值赋给原始数组copy(arr, tmpArr) // arr = tempArr // 此赋值方式不会影响main()中原数组arr中的值,仅仅在该函数作用域内的结果是排好序的// for i := 0; i < length1+length2; i++ {// 	arr[i] = tmpArr[i]// }}// 注意:下面的l1和l2不能写成 "len(arr)/2" 和 "len(arr)-l1"if length > 1 { // 最后拆至每个子数组只有一个元素l1 := length / 2l2 := length - l1arr1, arr2 := arr, arr[l1:] // arr1原数组前半部分、arr2原数组后半部分MergeSort(arr1, l1)         // 不断拆分数组长度直至长度为1MergeSort(arr2, l2)         // 不断拆分数组长度直至长度为1MergeSorting(arr1, arr2, l1, l2)// mid := length / 2// arr1, arr2 := arr[:mid], arr[mid:] // arr1原数组前半部分、arr2原数组后半部分// MergeSort(arr1, len(arr1))         // 不断拆分数组长度直至长度为1// MergeSort(arr2, len(arr2))         // 不断拆分数组长度直至长度为1// MergeSorting(arr1, arr2, len(arr1), len(arr2))}
}// 冒泡
// 时间复杂度:O(n^2) 空间复杂度:O(1)
func MaoPaoSort(arr []int) {for i := 0; i < len(arr); i++ {for j := i + 1; j < len(arr); j++ {if arr[i] > arr[j] {arr[i], arr[j] = arr[j], arr[i] // swap}}}
}func main() {array := []int{5, 28, 73, 19, 6, 0, 5}// MaoPaoSort(array)// QuickSort(array, 0, len(array)-1)// QuickSortNotByRecursion(array)// HeapSort(array)MergeSort(array, len(array))fmt.Println(array)return
}

BFS 广度优先遍历,利用queue

// BFS(利用队列:尾进头出)
func levelOrder(root *TreeNode) []int {res := make([]int, 0)if  root == nil {   return res}queue := []*TreeNode{root} // 开始循环前,先塞入rootfor len(queue) > 0 {root = queue[0] // 获取即将出队的头节点res = append(res, root.Val)queue = queue[1:] // 头结点出队if root.Left != nil {queue = append(queue, root.Left)}if root.Right != nil {queue = append(queue, root.Right)}}return res
}

DFS 深度优先遍历,利用stack

前序遍历(根 左 右)

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
// 迭代
func preorderTraversal(root *TreeNode) []int {array := make([]int, 0)stack := make([]*TreeNode, 0)for root != nil || len(stack) > 0 {// 不断遍历左子树for root != nil {array = append(array, root.Val) // result, finally return stack = append(stack, root)     // pushroot = root.Left}// 左子树遍历完了,开始从下往上遍历右子树(每次找栈顶指针,然后pop出栈)if len(stack) > 0 {root = stack[len(stack) - 1]    // 获取栈顶元素root = root.Rightstack = stack[: len(stack) - 1] // pop}}return array
}// 递归
var array []int
func preorderTraversal(root *TreeNode) []int {array = make([]int, 0)dfs(root)return array
}func dfs(root *TreeNode) {if root == nil {return}array = append(array, root.Val)dfs(root.Left)dfs(root.Right)
} 

中序遍历(左根右)

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/// 迭代
func inorderTraversal(root *TreeNode) []int {res := make([]int, 0)stack := make([]*TreeNode, 0)for root != nil || len(stack) > 0 {for root != nil {stack = append(stack, root)root = root.Left}if len(stack) > 0 {top := stack[len(stack) - 1]res = append(res, top.Val)root = top.Rightstack = stack[:len(stack) - 1] // pop}}return res
}// 递归
func inorderTraversal(root *TreeNode) []int {res := make([]int, 0)var inorder func(root *TreeNode)inorder = func(root *TreeNode) {if root != nil {inorder(root.Left)res = append(res, root.Val)inorder(root.Right)}}inorder(root)return res
}

后序遍历(左 右 根)

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/// 迭代
// https://github.com/HelloWorld-666/C_Tree/blob/master/C_Tree/main.cpp
// 每个节点会经过两次栈,第一次不断遍历左子节点会经过,第二次遍历完右子节点后,获取栈顶节点时也会经过;
// 且当某个根节点root左右孩子节点都为空时,root出栈并置空
func postorderTraversal(root *TreeNode) []int {res := make([]int, 0)stack:= make([]*TreeNode, 0)flagMap := make(map[*TreeNode]int)          // 标记节点是第几次经过根节点 => 入栈for root != nil || len(stack) > 0 {for root != nil {                       // 遍历左子节点stack = append(stack, root)         // pushflagMap[root] = 1                   // 第1次经过该节点时,做标记:1root = root.Left}if len(stack) > 0 {root = stack[len(stack) - 1]        // 获取栈顶节点if flagMap[root] == 1 {flagMap[root] = 2               // 第2次经过该节点时,做标记:2root = root.Right} else {res = append(res, root.Val)stack = stack[:len(stack) - 1]  // pop stack top noderoot = nil // 当前root的左右子节点都为空时(叶子节点),将该root出栈且置空,避免该root因不等于空而再次进入上方内循环中逻辑.}}}return res
}// 递归
func postorderTraversal(root *TreeNode) []int {res := make([]int, 0)var recursion func(root *TreeNode)recursion = func(root *TreeNode) {if root != nil {recursion(root.Left)recursion(root.Right)res = append(res, root.Val)}}recursion(root)return res
}

BFS/DFS 总结

将所有结点都看作根结点,关键在于何时访问。
前序:入栈时访问;中序:第一次退栈时访问;后序:第二次退栈时访问。

深度优先遍历(借助栈stack结构来实现) = 前中后序遍历
dfs:一条路走的死,用栈实现,进栈、退栈,一搜到底!一般用 递归 实现
bfs: 辐射八方,用队实现,入队、出队,步步为营!一般用 迭代 实现
深度优先,就是 一条路走到底,广度优先,就是 每条路都同时派人走

另外:删除一棵二叉树,即释放一棵二叉树的内存,用后序遍历即可实现(这里的“访问”变成了delete 结点).

相关文章:

常用算法实现【必会】:sort/bfs/dfs

文章目录常用排序算法实现&#xff08;Go版本&#xff09;BFS 广度优先遍历&#xff0c;利用queueDFS 深度优先遍历&#xff0c;利用stack前序遍历&#xff08;根 左 右&#xff09;中序遍历&#xff08;左根右&#xff09;后序遍历&#xff08;左 右 根&#xff09;BFS/DFS 总…...

瑟瑟发抖吧——用了这款软件,我的开发效率提升了50%

一、前言 开发中&#xff0c;一直听到有人讨论是否需要重复造轮子&#xff0c;我觉得有能力的人&#xff0c;轮子得造。但是往往开发周期短&#xff0c;用轮子所节省的时间去更好的理解业务&#xff0c;应用到业务中&#xff0c;也能清晰发现轮子的利弊&#xff0c;一定意义上…...

笔记本只使用Linux是什么体验?

个人主页&#xff1a;董哥聊技术我是董哥&#xff0c;嵌入式领域新星创作者创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01;近期&#xff0c;也有朋友问我&#xff0c;笔记本只安装Linux怎么样&#xff0c;刚好我也借此来表达一下我的感受…...

pipeline业务发布

业务环境介绍公司当前业务上线流程首先是通过nginx灰度&#xff0c;dubbo-admin操作禁用&#xff0c;然后发布上线主机&#xff0c;发布成功后&#xff0c;dubbo-admin启用&#xff0c;nginx启用主机&#xff1b;之前是通过手动操作&#xff0c;很不方便&#xff0c;本次优化为…...

【巨人的肩膀】JAVA面试总结(七)

&#x1f4aa;MyBatis 1、谈谈你对MyBatis的理解 Mybatis是一个半ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了JDBC&#xff0c;加载驱动、创建连接、创建statement等繁杂的过程&#xff0c;开发者开发时只需要关注如何编写SQL语句&#xff0c;可以…...

Python满屏表白代码

目录 前言 爱心界面 无限弹窗 前言 人生苦短&#xff0c;我用Python&#xff01;又是新的一周啦&#xff0c;本期博主给大家带来了一个全新的作品&#xff1a;满屏表白代码&#xff0c;无限弹窗版&#xff01;快快收藏起来送给她吧~ 爱心界面 def Heart(): roottk.Tk…...

Spring学习流程介绍

Spring学习流程介绍 Spring技术是JavaEE开发必备技能&#xff0c;企业开发技术选型命中率>90%; Spring有下面两大优势: 简化开发: 降低企业级开发的复杂性 框架整合: 高效整合其他技术&#xff0c;提高企业级应用开发与运行效率 Spring官网: https://spring.io/ Spring发展…...

杭银消金基于 Apache Doris 的统一数据查询网关改造

导读&#xff1a; 随着业务量快速增长&#xff0c;数据规模的不断扩大&#xff0c;杭银消金早期的大数据平台在应对实时性更强、复杂度更高的的业务需求时存在瓶颈。为了更好的应对未来的数据规模增长&#xff0c;杭银消金于 2022 年 10 月正式引入 Apache Doris 1.2 对现有的风…...

Flink学习笔记(六)Time详解

一、Flink中Time的三种类型&#xff1a; Stream数据中的Time&#xff08;时间&#xff09;分为以下3种&#xff1a; 1.Event Time&#xff08;事件产生的时间&#xff09;&#xff1a; 事件的时间戳&#xff0c;通常是生成事件的时间。Event time 是事件本身的时间&#xff0c…...

「Vue面试题」在项目中你是如何解决跨域的?

文章目录一、跨域是什么二、如何解决CORSProxy一、跨域是什么 跨域本质是浏览器基于同源策略的一种安全手段 同源策略&#xff08;Sameoriginpolicy&#xff09;&#xff0c;是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能 所谓同源&#xff08;即指在同一个域&…...

java八股文--数据库

数据库1.索引的基本原理2.聚簇和非聚簇索引的区别3.mysql索引的数据结构以及各自的优劣4.索引的设计原则5.事务的基本特性和隔离级别6.mysql主从同步原理7.简述MyISAM和InnoDB的区别8.简述mysql中索引类型及对数据库性能的影响9.Explain语句结果中各个字段分别表示什么10.索引覆…...

vue中名词解释

No名称略写作用应用场景其他1 单页面应用 &#xff08;Single-page application&#xff09; SPA 1&#xff0c;控制整个页面 2&#xff0c;抓取更新数据 3&#xff0c;无需加载&#xff0c;进行页面切换 丰富的交互&#xff0c;复杂的业务逻辑的web前端一般要求后端提供api数据…...

基于Java+SSM+Vue的旅游资源网站设计与实现【源码(完整源码请私聊)+论文+演示视频+包运行成功】

博主介绍&#xff1a;专注于Java技术领域和毕业项目实战 &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例&#xff08;200套&#xff09; 目录 一、效果演示 二、…...

用于人工智能研究的开源Python微电网模拟器pymgrid(入门篇)

pymgrid是一个开源Python库&#xff0c;用于模拟微型电网的三级控制&#xff0c;允许用户创建或自行选择的微电网。并可以使用自定义的算法或pymgrid中包含的控制算法之一来控制这些微电网&#xff08;基于规则的控制和模型预测控制&#xff09;。 pymgrid还提供了与OpenAI Gy…...

运算放大器:电压比较器、电压跟随器、同相比例放大器

目录一、单限电压比较器二、滞回电压比较器三、窗口电压比较器四、正点原子直流电机驱动器电路分析实战1、电压采集电路2、电流采集电路3、过流检测电路Ⅰ、采用分压后的输入电压&#xff1a;Ⅱ、采用理想电压源的输入电压&#xff1a;Ⅲ、同相输入电压采用的是非理想电压源&am…...

Vector - CAPL - 实时时间on *(续2)

继续继续。。。四、键盘事件这个键盘事件是我个人起的名字&#xff0c;为了方便与其他事件进行区分&#xff0c;为什么要把这一个单独拉出来说呢&#xff0c;因为它的用处实在是太广泛了&#xff0c;基本只要是使用CANoe做一些基本的自动化测试小工具&#xff0c;都会用到它&am…...

数据质量管理的四个阶段

然而&#xff0c;我们需要按照什么流程来对数据质量进行有效的管控&#xff0c;从而提升数据质量&#xff0c;释放数据价值&#xff1f;一般来讲&#xff0c;数据质量控制流程分为4个阶段&#xff1a;启动、执行、检查、处理。在管控过程中这4个阶段需不断循环&#xff0c;螺旋…...

Spring源码面试最难问题——循环依赖

前言 问&#xff1a;Spring 如何解决循环依赖&#xff1f; 答&#xff1a;Spring 通过提前曝光机制&#xff0c;利用三级缓存解决循环依赖&#xff08;这原理还是挺简单的&#xff0c;参考&#xff1a;三级缓存、图解循环依赖原理&#xff09; 再问&#xff1a;Spring 通过提前…...

【计组】RAM的深入理解

一、存储机理 RAM的实现逻辑有种&#xff0c;分别是触发器和电容。 SRAM&#xff08;Static&#xff09;DRAM&#xff08;Dynamic&#xff09;存储方式触发器电容破坏性读出否&#xff08;触发器具有稳态&#xff0c;能够锁住0或1两种状态&#xff09;是&#xff08;电容需要…...

JavaScript 之数据交互

在前后端交互中&#xff0c;前端通常需要对接口返回的数据进行格式转换、遍历、循环等&#xff1b;通常会用到以下函数和方法&#xff1a; forEach()、map()遍历数组&#xff08;map返回新的数组&#xff09;&#xff1b;forEach()只能使用try catah终止循环&#xff1b;for in…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...