【推荐收藏】这份图解算法数据结构的材料太良心

news/2024/4/28 22:33:45/文章来源:https://blog.csdn.net/m0_59596937/article/details/128427038

5年前发生的一件事,成为了我职业生涯的重要转折点。当时的我在交大读研,对互联网求职一无所知,但仍然硬着头皮申请了 Microsoft 实习生。面试官让我在白板上写出“快速排序”代码,我畏畏缩缩地写了一个“冒泡排序”,并且还写错了。从面试官的表情上,我知道失败了。

此次失利倒逼我开始刷算法题。我采用“扫雷游戏”式的学习方法,两眼一抹黑刷题,扫到不会的“雷”就通过查资料把它“排掉”,配合周期性总结,幸运地,我在秋招斩获了多家大厂的 Offer 。

当前的就业环境不好,找工作也卷的很,各种面试题也是千奇百怪。回想自己当初在“扫雷式”刷题中被炸的满头包的痛苦,思考良久,我意识到“前期刷题必看”的资料太有必要,可以使让我们在初入职场少走许多弯路。

文章目录

      • 内容结构
      • 完整版材料
      • 环境安装
      • 案例1
        • 数组
        • 数组常用操作
        • 数组典型应用
      • 案例2
        • 快速排序
        • 算法流程
        • 算法特性
        • 快排为什么快?
        • 基准数优化
        • 尾递归优化

内容结构

介绍的内容分为复杂度分析、数据结构、算法三个部分,限于篇幅原因,我下面会举2个案例,以 Python 讲解,当然也支持JAVA、C++、GO、C#等编程语言,完整版内容在下方

完整版材料

本文项目源码、数据、技术交流提升,均可加交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友

方式①、添加微信号:dkl88191,备注:来自CSDN +研究方向
方式②、微信搜索公众号:Python学习与数据挖掘,后台回复:图解算法数据结构

在本文中,重点和难点知识会主要以动画、图解的形式呈现,而文字的作用则是作为动画和图的解释与补充。

环境安装

推荐使用开源轻量的 VSCode 作为本地 IDE ,下载并安装 VSCode,在 VSCode 的插件市场中搜索 python ,安装 Python Extension Pack。

案例1

数组

数组 Array 是一种将 相同类型元素 存储在连续内存空间的数据结构,将元素在数组中的位置称为元素的索引 Index。

观察上图,我们发现数组首元素的索引为0。你可能会想,这并不符合日常习惯,首个元素的索引为什么不是1呢,这不是更加自然吗?我认同你的想法,但请先记住这个设定,后面讲内存地址计算时,我会尝试解答这个问题。

数组有多种初始化写法。根据实际需要,选代码最短的那一种就好。

""" 初始化数组 """
arr = [0] * 5  # [ 0, 0, 0, 0, 0 ]
nums = [1, 3, 2, 5, 4]  

优点:在数组中访问元素非常高效。这是因为在数组中,计算元素的内存地址非常容易。给定数组首个元素的地址、和一个元素的索引,利用以下公式可以直接计算得到该元素的内存地址,从而直接访问此元素。

为什么数组元素索引从 0 开始编号? 根据地址计算公式,索引本质上表示的是内存地址偏移量,首个元素的地址偏移量是0 ,那么索引是0 也就很自然了。

访问元素的高效性带来了许多便利。例如,我们可以在 时间内随机获取一个数组中的元素。

""" 随机访问元素 """
def randomAccess(nums):# 在区间 [0, len(nums)) 中随机抽取一个数字random_index = random.randint(0, len(nums))# 获取并返回随机元素random_num = nums[random_index]return random_num

缺点:数组在初始化后长度不可变。 由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗时的。

""" 扩展数组长度 """
# 请注意,Python 的 list 是动态数组,可以直接扩展
# 为了方便学习,本函数将 list 看作是长度不可变的数组
def extend(nums, enlarge):# 初始化一个扩展长度后的数组res = [0] * (len(nums) + enlarge)# 将原数组中的所有元素复制到新数组for i in range(len(nums)):res[i] = nums[i]# 返回扩展后的新数组return res

数组中插入或删除元素效率低下。假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点:

  • 时间复杂度高: 数组的插入和删除的平均时间复杂度均为 ,其中 为数组长度。
  • 丢失元素: 由于数组的长度不可变,因此在插入元素后,超出数组长度范围的元素会被丢失。
  • 内存浪费: 我们一般会初始化一个比较长的数组,只用前面一部分,这样在插入数据时,丢失的末尾元素都是我们不关心的,但这样做同时也会造成内存空间的浪费。

数组常用操作

数组遍历

以下介绍两种常用的遍历方法。

""" 遍历数组 """
def traverse(nums):count = 0# 通过索引遍历数组for i in range(len(nums)):count += 1# 直接遍历数组for num in nums:count += 1

数组查找

通过遍历数组,查找数组内的指定元素,并输出对应索引。

""" 在数组中查找指定元素 """
def find(nums, target):for i in range(len(nums)):if nums[i] == target:return ireturn -1

数组典型应用

随机访问:如果我们想要随机抽取一些样本,那么可以用数组存储,并生成一个随机序列,根据索引实现样本的随机抽取。

二分查找:例如前文查字典的例子,我们可以将字典中的所有字按照拼音顺序存储在数组中,然后使用与日常查纸质字典相同的“翻开中间,排除一半”的方式,来实现一个查电子字典的算法。

深度学习:神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。数组是神经网络编程中最常使用的数据结构。

案例2

快速排序

快速排序 Quick Sort 是一种基于“分治思想”的排序算法,速度很快、应用很广。

快速排序的核心操作为哨兵划分,其目标为:选取数组某个元素为基准数,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。哨兵划分的实现流程为:

  • 以数组最左端元素作为基准数,初始化两个指针 i , j 指向数组两端;
  • 设置一个循环,每轮中使用 i / j 分别寻找首个比基准数大 / 小的元素,并交换此两元素;
  • 不断循环步骤 2. ,直至 i , j 相遇时跳出,最终把基准数交换至两个子数组的分界线;

哨兵划分执行完毕后,原数组被划分成两个部分,即左子数组和右子数组 ,且满足左子数组任意元素<基准数< 右子数组任意元素。因此,接下来我们只需要排序两个子数组即可。

""" 哨兵划分 """
def partition(self, nums, left, right):# 以 nums[left] 作为基准数i, j = left, rightwhile i < j:while i < j and nums[j] >= nums[left]:j -= 1  # 从右向左找首个小于基准数的元素while i < j and nums[i] <= nums[left]:i += 1  # 从左向右找首个大于基准数的元素# 元素交换nums[i], nums[j] = nums[j], nums[i]# 将基准数交换至两子数组的分界线nums[i], nums[left] = nums[left], nums[i]return i  # 返回基准数的索引

算法流程

  • 首先,对数组执行一次「哨兵划分」,得到待排序的 左子数组 和 右子数组 。
  • 接下来,对 左子数组 和 右子数组 分别 递归执行「哨兵划分」……
  • 直至子数组长度为 1 时 终止递归 ,即可完成对整个数组的排序。

观察发现,快速排序和「二分查找」的原理类似,都是以对数阶的时间复杂度来缩小处理区间。

算法特性

快排为什么快?

基准数优化

普通快速排序在某些输入下的时间效率变差。举个极端例子,假设输入数组是完全倒序的,由于我们选取最左端元素为基准数,那么在哨兵划分完成后,基准数被交换至数组最右端,从而左子数组长度为 n-1 、右子数组长度为0。这样进一步递归下去,每轮哨兵划分后的右子数组长度都为0,分治策略失效,快速排序退化为冒泡排序了。

为了尽量避免这种情况发生,我们可以优化一下基准数的选取策略。首先,在哨兵划分中,我们可以随机选取一个元素作为基准数 。但如果运气很差,每次都选择到比较差的基准数,那么效率依然不好。

进一步地,我们可以在数组中选取3个候选元素(一般为数组的首、尾、中点元素),并将三个候选元素的中位数作为基准数,这样基准数“既不大也不小”的概率就大大提升了。当然,如果数组很长的话,我们也可以选取更多候选元素,来进一步提升算法的稳健性。采取该方法后,时间复杂度劣化最差的概率极低。

""" 选取三个元素的中位数 """
def median_three(self, nums, left, mid, right):# 使用了异或操作来简化代码# 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1if (nums[left] > nums[mid]) ^ (nums[left] > nums[right]):return leftelif (nums[mid] < nums[left]) ^ (nums[mid] > nums[right]):return midreturn right""" 哨兵划分(三数取中值) """
def partition(self, nums, left, right):# 以 nums[left] 作为基准数med = self.median_three(nums, left, (left + right) // 2, right)# 将中位数交换至数组最左端nums[left], nums[med] = nums[med], nums[left]# 以 nums[left] 作为基准数# 下同省略...

尾递归优化

普通快速排序在某些输入下的空间效率变差。仍然以完全倒序的输入数组为例,由于每轮哨兵划分后右子数组长度为 0 ,那么将形成一个高度为 n-1 的递归树,此时使用的栈帧空间大小劣化至 O(n) 。

为了避免栈帧空间的累积,我们可以在每轮哨兵排序完成后,判断两个子数组的长度大小,仅递归排序较短的子数组。由于较短的子数组长度不会超过 n/2,因此这样做能保证递归深度不超过log(n) ,即最差空间复杂度被优化至O(log(n)) 。

""" 快速排序(尾递归优化) """
def quick_sort(self, nums, left, right):# 子数组长度为 1 时终止while left < right:# 哨兵划分操作pivot = self.partition(nums, left, right)# 对两个子数组中较短的那个执行快排if pivot - left < right - pivot:self.quick_sort(nums, left, pivot - 1)  # 递归排序左子数组left = pivot + 1     # 剩余待排序区间为 [pivot + 1, right]else:self.quick_sort(nums, pivot + 1, right)  # 递归排序右子数组right = pivot - 1    # 剩余待排序区间为 [left, pivot - 1]

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1754. 构造字典序最大的合并字符串

摘要 1754. 构造字典序最大的合并字符串 一 贪心算法分析 题目要求合并两个字符串 word1 与 word2&#xff0c;且要求合并后的字符串字典序最大。首先需要观察一下合并的选择规律&#xff0c;假设当前需要从 word1​ 的第 i 个字符和 word2​ 的第 j个字符选择一个字符加入到…

自制macOS安装镜像iso虚拟机用

在网上下载的用于在虚拟机中安装的镜像版本相对比较旧。安装完成后还要进行升级比较麻烦。于是我就想自己制作安装镜像了。 精华 #创建空白磁盘镜像 hdiutil create -o /tmp/ventura -size 13800m -volname ventura -layout SPUD -fs HFSJ #挂载上面创建的镜像 hdiutil attac…

内容资产管理11问

&#x1f447;点击一键关注主笔&#xff1a;邹小困、邝晴岚主持人&#xff1a;增长黑盒分析师Emma出品&#xff1a;增长黑盒研究组前言在这个信息爆炸的数据时代&#xff0c;各个行业正积极推进数字化转型&#xff0c;产业升级与技术赋能成为主题之一。在推进企业线上线下融合的…

最近面试遇到一个算法题,简单写一点。

第⼀题&#xff08;必答&#xff09; 请针对有重复数字的数组设计⼀个快排算法&#xff0c;⽐如&#xff1a;[34, 34, 89, 1, 1, 20, 12]&#xff0c;排序后结果为 [89,34,34,20,12,1,1] 第⼆题&#xff08;必答&#xff09; 请利⽤Redis 实现⼀个通⽤分布式锁&#xff0c;并…

B+树 [数据结构与算法][Java]

B树 B树是B树的一种变形 我们通过一颗四阶B树来理解认识一下B树:(如下:) 我们其实从图上就可以看出B树和B树是有很多不同之处的 比如我们的B树中将叶子结点层的所有结点使用一个链表串联了起来B树中对于非叶子结点都是只是存储的索引(指针), 并没有存储关键字, 所以我们最终查…

Linux系统基础——BIOS和Bootloader

BIOS和Bootloader 特此说明: 刘超的趣谈linux操作系统是比较重要的参考资料&#xff0c;本文大部分内容和所有图片来源于这个专栏。 1 了解背景 1.1 目的 操作系统不是在板子上电就直接运行的&#xff0c;上电到系统启动的中间过程要搞明白&#xff0c;比如了解linux系统启动…

火山引擎 DataTester 上线“流程画布”功能,支持组合型 A/B 实验分析

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 在精细化运营的时代&#xff0c;运营活动同样需要有精细化的策略&#xff0c;例如在年末大促活动中&#xff0c;设计 APP 弹窗提醒、满减、会员领券时&#xff0c;我…

TikTok 加速团结独立站,跨境电商的又一次红利期?

TikTok近年来在国际上非常流行。2021年8月&#xff0c;TikTok的全球下载量首次超过Facebook&#xff0c;成为全球最大的下载量。TikTok的诞生打破了海外社交媒体的垄断&#xff0c;TikTok营销成为许多跨境卖家的重点之一。 封号事件发生后&#xff0c;许多跨境卖家开始向独立站…

Python函数总结

在Python中&#xff0c;函数是一个带有名字的代码块&#xff0c;可以被反复调用。函数可以帮助你组织和重用代码&#xff0c;使你的程序更整洁&#xff0c;更易于维护。本文将会深入探索Python的秘密 目录 定义函数 自定义函数 内置函数 函数式方程 高阶函数 函数标注 …

读研2年,我选择从中科院退学转行做码农

从入学天坑材料专业到退学 先自我介绍一下&#xff1a;我天坑材料专业&#xff0c;本科某211&#xff0c;保研到中科院&#xff0c;但是我真是菜的抠脚&#xff0c;还懒&#xff0c;也不喜欢科研&#xff0c;论文达不到毕业要求&#xff0c;纠结之下研三退学转码农了。 读了2…

JVM【垃圾回收相关概念和垃圾回收器】

垃圾回收相关概念 System.gc()的理解 在默认情况下&#xff0c;通过**system.gc&#xff08;&#xff09;**者Runtime.getRuntime().gc() 的调用&#xff0c;会显式触发FullGC&#xff0c;同时对老年代和新生代进行回收&#xff0c;尝试释放被丢弃对象占用的内存。 然而syste…

做跨境电商,如何从同类产品中脱颖而出?

随便打开一个跨境电商平台&#xff0c;你会发现自己售卖的产品有那么多类似的选择&#xff0c;如何确保你的产品能被客户选择&#xff1f;怎样在一系列产品中脱颖而出&#xff1f; 不少卖家提到了&#xff0c;搞差异化竞争&#xff0c;这是跨境电商卖家常挂在嘴边的一个词&…

k8s使用glusterfs(静态供给、动态供给)、glusterfs的安装与使用

目录前言主机准备配置主机名、关闭防火墙、关闭selinux挂载磁盘安装glusterfs服务端glusterfs的端口分布式集群的结构组成glusterfs集群创建存储卷启动卷k8s使用glusterfs作为后端存储&#xff08;静态供给glusterfs存储&#xff09;恢复初始化环境安装Heketi 服务&#xff08;…

fpga实操训练(signal tap调试)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 编写软件的同学都知道&#xff0c;如果需要调试软件的话&#xff0c;除了要学会打印信息日志之外&#xff0c;另外一个很重要的方面&#xff0c;就…

maven的插件(命令)install介绍

maven的插件&#xff08;命令&#xff09;install介绍背景关于构建时使用的maven命令installmaven其他插件/命令的使用背景 今天在引入SpringCloudAlibaba时&#xff0c;pom.xml中的dependency报错了 到本地仓库去验证 验证无误&#xff0c;找原因 现象&#xff1a; 在maven…

数据挖掘期末-图注意力模型

PyGAT图注意力模型 ​  PyGAT实现的分类器&#xff1a; https://www.aliyundrive.com/s/vfK8ndntpyc 还在发烧&#xff0c;不是特别清醒&#xff0c;就简单写了写。用GAT进行关系预测&#xff0c;GAT可能是只做中间层&#xff0c;不过本来在GAT这一层就为了能懂就简化了很多…

Linux-系统随你玩之--用户及用户组管理

一、用户基本介绍 Linux 系统是一个多用户多任务的操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向系统 管理员申请一个账号&#xff0c;然后才可以以这个用户登陆系统。 二、Linux中用户和组 2.1、用户和组介绍 用户&#xff1a; 每一个用户都…

如何不改一行代码,让Hippy启动速度提升50%?

导读&#xff5c;Hippy使用JS引擎进行异步渲染&#xff0c;在用户从点击到打开首屏可交互过程中会有一定的耗时&#xff0c;影响用户体验。如何优化这段耗时&#xff1f;腾讯客户端开发工程师李鹏&#xff0c;将介绍QQ浏览器通过切换JS引擎来优化耗时的探索过程和效果收益。在分…

微导纳米科创板上市:市值125亿 无锡首富王燕清再敲钟

雷递网 雷建平 12月23日江苏微导纳米科技股份有限公司&#xff08;简称&#xff1a;“微导纳米”&#xff0c;股票代码为&#xff1a;“688147”&#xff09;今日在科创板上市。微导纳米此次发行4544.55万股&#xff0c;发行价为24.21元&#xff0c;募资总额为11亿元。微导纳米…

对Python的学习【如何查看路径和安装包】

1&#xff1a;怎么查看本地电脑的Python版本号及安装路径&#xff1a; 对于Windows平台&#xff0c;打开cmd 使用命令py -0p 【其中0是零】 显示已安装的 python 版本且带路径的列表&#xff0c;参见下图&#xff1a; 其中带星号*的为默认版本。 2:怎么查看python pip…