数据结构和算法基础(一)
文章目录
- 链表反转
- 链表合并
- 删除链表倒数第 n 个结点
- 找链表的中间结点
- 链表中环的检测
- 排序算法
- 递归
趁空闲时间刷一遍极客时间上王争的《数据结构与算法之美》课程,个人觉得写的很好,每章节由浅入深且从基础到引入设计类问题,如果写过很多代码想要进行架构设计转型时再回头看这些基础知识还蛮有趣的,以下纪录下随着课程走的部分实现代码和思考;
内容主要是笔记和代码,注:手写一遍代码是有必要的;
链表反转
单链表反转
class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; this.next = null; }
public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode nextTemp = curr.next; // 临时保存下一个节点 curr.next = prev; // 反转当前节点的指针 prev = curr; // 将前一个节点移动到当前节点 curr = nextTemp; // 将当前节点移动到下一个节点 } return prev; // prev 最后会指向新的头节点 }
}
链表合并
两个有序的链表合并,用到了哨兵dummy这个指针记录
class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; this.next = null; } public ListNode mergeTwoLists(ListNode l1, ListNode l2) { // 创建一个哨兵节点,方便处理边界情况 ListNode dummy = new ListNode(0); ListNode curr = dummy; // 使用两个指针分别遍历两个链表 while (l1 != null && l2 != null) { if (l1.val <= l2.val) { curr.next = l1; l1 = l1.next; } else { curr.next = l2; l2 = l2.next; } curr = curr.next; } // 处理剩余节点(只能有一个链表还有剩余节点) if (l1 != null) { curr.next = l1; } else { curr.next = l2; } }
}
删除链表倒数第 n 个结点
使用快慢指针,快慢指针在解很多链表题目中都有体现
class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; this.next = null; } public ListNode removeNthFromEnd(ListNode head, int n) { // 创建一个哨兵节点,简化头节点被删除的情况 ListNode dummy = new ListNode(0); dummy.next = head;// 初始化快慢指针 ListNode fast = dummy; ListNode slow = dummy; // 先将快指针向前移动 n+1 步 for (int i = 0; i <= n; i++) { fast = fast.next; } // 然后同时移动快慢指针,直到快指针到达链表末尾 while (fast != null) { fast = fast.next; slow = slow.next; } // 此时慢指针指向的节点的下一个节点就是要删除的节点 slow.next = slow.next.next; // 返回头节点(注意是哨兵节点的下一个节点) return dummy.next; }
}
找链表的中间结点
使用快慢指针来实现,快指针每次移动2步,而慢指针每次移动1步。当快指针到达链表末尾时,慢指针将恰好位于链表的中间。
class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; this.next = null; } public ListNode findMiddle(ListNode head) { // 初始化快慢指针 ListNode slow = head; ListNode fast = head; // 快指针每次移动两步,慢指针每次移动一步 while (fast != null && fast.next != null) { slow = slow.next; // 慢指针移动一步 fast = fast.next.next; // 快指针移动两步 } // 当快指针到达链表末尾时,慢指针指向中间节点 return slow; }
}
链表中环的检测
快慢指针进行遍历,如果快慢指针不相遇说明没有环
class ListNode { int val; ListNode next;ListNode(int val) { this.val = val; this.next = null; } public boolean hasCycle(ListNode head) { if (head == null || head.next == null) { // 如果链表为空或只有一个节点,则不可能有环 return false; } ListNode slow = head; ListNode fast = head;// 快慢指针开始移动,直到它们相遇或快指针到达链表末尾 while (fast != null && fast.next != null) { slow = slow.next; // 慢指针每次移动一步 fast = fast.next.next; // 快指针每次移动两步 // 如果快慢指针相遇,说明链表中存在环 if (slow == fast) { return true; } }// 快指针到达链表末尾,说明链表中没有环 return false; }
}
排序算法
常用的冒泡、选择、插入、归并、快速算法,手写很重要,写出来会发现即使是一个小的改动对于程序的消耗来说都有所差别;
关于排序的算法还可以参照:https://mp.weixin.qq.com/s/HQg3BzzQfJXcWyltsgOfCQ
在要求高效的很多基础框架代码中都是用了快速排序(递归思路)
// 冒泡排序
void bubbleSort(int[] arr) { int n = arr.length; for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { // 交换arr[j]和arr[j + 1] int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } }
} // 选择排序
void selectionSort(int[] arr) { int n = arr.length; for (int i = 0; i < n - 1; i++) { int minIdx = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIdx]) { minIdx = j; } } // 交换arr[i]和arr[minIdx] int temp = arr[minIdx]; arr[minIdx] = arr[i]; arr[i] = temp; }
} // 插入排序
void insertionSort(int[] arr) { int n = arr.length; for (int i = 1; i < n; i++) { int key = arr[i]; int j = i - 1; // 将arr[i]插入到已排序部分arr[0..i-1] while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j = j - 1; } arr[j + 1] = key; }
}
// 归并排序
void mergeSort(int[] arr, int left, int right) { if (left < right) { int mid = left + (right - left) / 2; // 递归排序两个子数组 mergeSort(arr, left, mid); mergeSort(arr, mid + 1, right); // 合并两个已排序的子数组 merge(arr, left, mid, right); }
}
void merge(int[] arr, int left, int mid, int right) { int n1 = mid - left + 1; int n2 = right - mid; int[] L = new int[n1]; int[] R = new int[n2]; for (int i = 0; i < n1; i++) L[i] = arr[left + i]; for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j]; int i = 0, j = 0; int k = left; while (i < n1 && j < n2) { if (L[i] <= R[j]) { arr[k] = L[i]; i++; } else { arr[k] = R[j]; j++; } k++; } while (i < n1) { arr[k] = L[i]; i++; k++; } while (j < n2) { arr[k] = R[j]; j++; k++; }
}
// 快速排序
void quickSort(int[] arr, int low, int high) { if (low < high) { int pi = partition(arr, low, high); // 递归排序两个子数组 quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); }
}
int partition(int[] arr, int low, int high) { int pivot = arr[high]; int i = (low - 1); for (int j = low; j < high; j++) { if (arr[j] < pivot) { i++; // 交换arr[i]和arr[j] int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } // 交换arr[i + 1]和arr[high] (或pivot) int temp = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = temp; return i + 1;
}
递归
递归是一种分治的思维,不适合人类大脑但天然是计算机的处理方式,人类大脑总是想把事情的步骤想的很清晰,12345每一步骤做什么,但是计算机不是这样的;
递归也存在堆栈溢出和重复计算的问题,专栏中也给了对应的方式,重复计算可以通过缓存来解决;
// 上楼梯问题中可以适当增加缓存来消除重复计算
public int f(int n) {if (n == 1) return 1;if (n == 2) return 2;// hasSolvedList 可以理解成一个 Map,key 是 n,value 是 f(n)if (hasSolvedList.containsKey(n)) {return hasSovledList.get(n);}int ret = f(n-1) + f(n-2);hasSovledList.put(n, ret);return ret;
}
相关文章:
数据结构和算法基础(一)
文章目录 链表反转链表合并删除链表倒数第 n 个结点找链表的中间结点链表中环的检测排序算法递归 趁空闲时间刷一遍极客时间上王争的《数据结构与算法之美》课程,个人觉得写的很好,每章节由浅入深且从基础到引入设计类问题,如果写过很多代码想…...

【超长好文】网络安全从业者面试指南
文章为笔者偶然看到的github项目《网络安全面试指南》,作者FeeiCN,读完内容深感作者的用心,尽管一些观点因为时间原因与当下行情存在差异,但仍旧值得大家参考,希望能给大家在这行业寒冬带来一些启发,愿正在…...

基于大数据的高校新生数据可视化分析系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...

【cache】浅析四种常用的缓存淘汰算法 FIFO/LRU/LFU/W-TinyLFU
本文浅析淘汰策略与工作中结合使用、选取,并非针对算法本身如何实现的 文章目录 FIFOLFULRUW-TinyLFU实践与优化监控与调整 FIFO first input first output , 先进先出,即最早存入的元素最先取出, 典型数据结构代表:…...
STM32的DMA技术介绍
DMA(Direct Memory Access,直接内存访问) 是一种允许外设直接与系统内存进行数据传输,而无需经过CPU的技术。在STM32微控制器中,DMA技术极大地提高了数据传输效率,降低了CPU的负担,从而提升系统…...
C++11 多线程编程-小白零基础到手撕线程池
提示:文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问: 本文目标: 一、背景 来源于b站视频 C11 多线程编程-小白零基础到手撕线程池 学习来源:https://www.bilibili.com/video/BV1d841117SH/?p2&spm_id_f…...

智源研究院与百度达成战略合作 共建AI产研协同生态
2024年9月24日,北京智源人工智能研究院(简称“智源研究院”)与北京百度网讯科技有限公司(简称“百度”)正式签署战略合作协议,双方将充分发挥互补优势,在大模型等领域展开深度合作,共…...
Flask-SQLAlchemy:在Flask应用中优雅地操作数据库
在Python的Web开发领域,Flask是一个备受欢迎的轻量级Web框架,它以简洁、灵活而著称。而当我们需要在Flask应用中与数据库进行交互时,Flask-SQLAlchemy就成为了一个强大而便捷的工具。它将Flask的简洁性与SQLAlchemy的强大数据库抽象能力完美结…...

智能巡检机器人 数据库
智能巡检机器人AI智能识别。无需人工。只需后台监控结果即可!...
Spring AOP异步操作实现
在Spring框架中,AOP(面向切面编程)提供了一种非常灵活的方式来增强应用程序的功能。异步操作是现代应用程序中常见的需求,尤其是在处理耗时任务时,它可以帮助我们提高应用程序的响应性和吞吐量。Spring提供了一种简单的…...

【2006.07】UMLS工具——MetaMap原理深度解析
文献:《MetaMap: Mapping Text to the UMLS Metathesaurus》2006 年 7 月 14 日 https://lhncbc.nlm.nih.gov/ii/information/Papers/metamap06.pdf MetaMap:将文本映射到 UMLS 元数据库 总结 解决的问题 自动概念映射问题:解决如何将文本…...
ros2 colcon build 构建后,install中的local_setup.bash 和setup.bash有什么区别
功能概述 在 ROS2 中,colcon build是用于构建软件包的工具。构建完成后会生成install文件夹,其中的setup.bash和local_setup.bash文件都与环境设置相关,但存在一些区别。setup.bash 作用范围 setup.bash文件用于设置整个工作空间的环境变量。…...
Thymeleaf基础语法
Thymeleaf 是一种用于 Web 和非 Web 环境的现代服务器端 Java 模板引擎。它能够处理 HTML、XML、JavaScript、CSS 甚至纯文本。以下是 Thymeleaf 的一些基础语法: 1. 变量表达式 <!-- 显示变量的值 --> <p th:text"${name}">Default Name&l…...
spring cloud alibaba学习路线
以下是一条学习Spring Cloud Alibaba的路线: 一、基础前置知识 1. Java基础 熟练掌握Java语言特性,包括面向对象编程、集合框架、多线程等知识。 2. Spring和Spring Boot基础深入理解Spring框架,如依赖注入(DI)、控…...
基于 Seq2Seq 的中英文翻译项目(pytorch)
项目简介 本项目旨在使用 PyTorch 构建一个基于 Seq2Seq(编码器-解码器架构)的中英文翻译模型。我们将使用双语句子对的数据进行训练,最终实现一个能够将英文句子翻译为中文的模型。项目的主要步骤包括: 数据预处理:从数据集中提取英文和中文句子,并进行初步清洗和保存。…...

部标主动安全(ADAS+DMS)对接说明
1.前言 上一篇介绍了部标(JT/T1078)流媒体对接说明,这里说一下如何对接主动安全附件服务器。 流媒体的对接主要牵扯到4个方面: (1)平台端:业务端系统,包含前端呈现界面。 &#x…...
C++ STL(1)迭代器
文章目录 一、迭代器详解1、迭代器的定义与功能2、迭代器类型3、示例4、迭代器失效4.1、vector 迭代器失效分析4.2、list 迭代器失效分析4.3、set 与 map 迭代器失效分析 5、总结 前言: 在C标准模板库(STL)中,迭代器是一个核心概念…...
uview表单校验不生效问题
最近几次使用发现有时候会不生效,具体还没排查出来什么原因,先记录一下解决使用方法 <u--formlabelPosition"top"labelWidth"auto":model"form":rules"rules"ref"uForm" ><view class"…...
前端开发设计模式——单例模式
目录 一、单例模式的定义和特点: 1.定义: 2.特点: 二、单例模式的实现方式: 1.立即执行函数结合闭包实现: 2.ES6类实现: 三、单例模式的应用场景 1.全局状态管理: 2.日志记录器: …...
行情叠加量化,占据市场先机!
A股久违的3000点,最近都没有更新,现在终于对我们的市场又来点信息。相信在座的朋友这几天都是喜笑颜开,对A股又充满信心。当前行情好起来了,很多朋友又开始重回市场,研究股票学习量化,今天我们给大家重温下…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...