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

news2024/11/20 14:21:32

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 i
    return -1

数组典型应用

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

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

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

案例2

快速排序

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

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

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

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

""" 哨兵划分 """
def partition(self, nums, left, right):
    # 以 nums[left] 作为基准数
    i, j = left, right
    while 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 = 1
    if (nums[left] > nums[mid]) ^ (nums[left] > nums[right]):
        return left
    elif (nums[mid] < nums[left]) ^ (nums[mid] > nums[right]):
        return mid
    return 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.coloradmin.cn/o/112111.html

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

相关文章

Unity 3D Inspector 视图 || Unity 3D Scene View 视图 || Unity 3D Game View 视图

Unity 3D Inspector 视图 Unity 3D 的 Inspector 视图用于显示当前选定的游戏对象的所有附加组件&#xff08;脚本属于组件&#xff09;及其属性的相关详细信息。 视图布局 以摄像机为例&#xff0c;在 Unity 3D 的 Inspector 视图中显示了当前游戏场景中的 MainCamera 对象所…

腾讯云轻量应用服务器使用 Cloudreve 应用镜像搭建个人私有云盘!

Cloudreve 是一款开源的网盘软件&#xff0c;支持服务器本机及腾讯云对象存储 COS 等多种存储方式&#xff0c;提供离线下载、拖拽上传、在线预览等功能&#xff0c;能够帮助您快速搭建个人使用或多人共享的云盘系统。该镜像基于 CentOS 8.2 64位操作系统&#xff0c;已集成宝塔…

OpenAI | GPT-3新模型Davinci,将AI写作提升到新水平!网友惊呼:GPT-4要来了?

文 | 王思若20年&#xff0c;OpenAI推出了1750亿参数量的屠榜‘杀器’GPT-3&#xff0c;但基于大模型至今悬而未决的伦理和社会风险以及商业盈利等因素的考量&#xff0c;OpenAI将GPT-3以付费API的形式向公众开放。通过调用GPT-3的API&#xff0c;问答、语义检索、翻译、数学推…

消息队列RabbitMQ学习笔记(四)死信队列和延迟队列

1. 死信的概念 先从概念解释上搞清楚这个定义&#xff0c;死信&#xff0c;顾名思义就是无法被消费的消息&#xff0c;字面意思可以这样理 解&#xff0c;一般来说&#xff0c;producer 将消息投递到 broker 或者直接到queue 里了&#xff0c;consumer 从 queue 取出消息 进行…

【Linux】调试器gdb的使用

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;什么是gdb…

2022-忙碌的一年

&#xff08;点击即可听音频&#xff09;前言花有重开日,人无再少年.每当这个时候,回头驻足,不是感慨万千,就是惜时如金,一年悄无声息的从指尖划过,星海横流,岁月如碑.那些被偷走的时光,发生了大大小小的事无论是平淡无奇,还是历久难忘,有惊喜,有遗憾,终将都会隐入尘烟。大到国…

自定义coco数据集

1、环境 anaconda环境安装配置 2、工具 安装labelme工具 3、安装软件 3.1、打开anaconda控制台 3.2、创建虚拟环境 conda create -n labelme python3.73.3、激活环境 conda activate labelme3.4、下载labelme pip install labelme3.5、输入labelme打开软件 以后打开跳…

微信小程序--P2P消息收发模式(MQTT)

目录 前言 js demo 参数 new Paho.Client 创建对象 onConnectionLost 连接丢失回调 onMessageArrived 监听数据 connect (connectOptions)将此消息客户端连接到其服务器。 mqtt 频繁断开和重连问题 小程序实践 前言 P2P&#xff0c;顾名思义&#xff0c;是一对一的消…

vTESTstudio入门到精通 - vTESTstudio工具栏介绍_Layout

到今天这一篇vTESTstudio工具栏介绍就将暂时告一段落了&#xff0c;后续如果大家有需求的话可以私信我&#xff0c;我就继续再往深的介绍&#xff0c;如果没有催更的话&#xff0c;就当这部分是给大家做个普及&#xff0c;作为一个扫盲篇吧&#xff0c;实际项目使用和编程的内容…

Nginx反向代理的一个算法API的接口调用超时:504,GateWay Timeout,怎么破?

背景 服务端由第三方部署了一个基于 darknet &#xff08;一个较为轻型的完全基于C与CUDA的开源深度学习框架&#xff09;的识别算法服务&#xff0c;通过 Flask 的 Web 服务对业务服务暴露 API 接口。作为测试&#xff0c;一开始是直接通过 python3 app.py 的命令行启动的服务…

SAP Product Lifecycle Costing 里的 Costing Sheet 成本核算表

有朋友在我的知识星球里向我提问&#xff1a; 请您帮忙讲一下这个AP0100的costing sheet rows这里都表示什么意思吗&#xff1f;比如row10、base Z010、overhead啥、描述、from、to row、credit都说明了什么&#xff0c;能够实现上面&#x1f446;&#x1f3fb;的目标吗&#x…

【详细学习SpringBoot源码之属性配置文件加载原理(Bootstrap.properties|Bootstrap.yml)-8】

一.知识回顾 【0.SpringBoot专栏的相关文章都在这里哟&#xff0c;后续更多的文章内容可以点击查看】 【1.SpringBoot初识之Spring注解发展流程以及常用的Spring和SpringBoot注解】 【2.SpringBoot自动装配之SPI机制&SPI案例实操学习&SPI机制核心源码学习】 【3.详细学…

Transformer图解

Transformer正在席卷自然语言处理领域。 这些令人难以置信的模型正在打破多项 NLP 记录并推动最先进的技术发展。 它们被用于许多应用程序&#xff0c;如机器语言翻译、会话聊天机器人&#xff0c;甚至为更好的搜索引擎提供动力。 Transformer在当今深度学习领域风靡一时&…

POSTGRESQL 13.1 bug 与 逻辑复制槽参数调优

随着问问题的同学越来越多&#xff0c;公众号内部私信回答问题已经很困难了&#xff0c;所以建立了一个群&#xff0c;关于各种数据库的问题都可以&#xff0c;目前主要是 POSTGRESQL, MYSQL ,MONGODB ,POLARDB ,REDIS&#xff0c;SQL SERVER 等&#xff0c;期待你的加入&#…

pytorch 多卡运行详细教程

先说明一下背景&#xff0c;目前正在魔改以下这篇论文的代码&#xff1a; https://github.com/QipengGuo/GraphWriter-DGLgithub.com 由于每次完成实验需要5个小时&#xff08;baseline&#xff09;&#xff0c;自己的模型需要更久&#xff08;2倍&#xff09;&#xff0c;非…

战略和什么相关?

&#xff08;1&#xff09;战略和创新企业做到最大&#xff0c;就是业务多元化/一体化、区域全球化。想再折腾折腾&#xff0c;那就手里这几张牌搞重新排列组合&#xff0c;这就是&#xff1a;企业再造。就是中国人说的&#xff1a;天下大势分久必合合久必分。按照波士顿咨询来…

20221224英语学习

今日词汇 lash v.将&#xff08;物品&#xff09;系牢&#xff0c;捆绑&#xff1b;&#xff08;风、雨等&#xff09;猛烈打击&#xff1b;鞭打&#xff1b;猛烈抨击&#xff0c;严厉斥责 detective n.侦探; 警探; 发掘者; 发现者 division n.分开; 分隔; 分配; 分隔物; 刻…

深度学习SSD算法

目录1 SSD网络结构1.1 backbone1.2 extra部分1.3 loc和cls1.3.1 PriorBox层先验框的生成方法1.3.2 loc的预测结果2 模型训练2.1 正负样本标记2.2 损失函数2.3 困难样本挖掘3 模型预测4 总结1 SSD网络结构 SSD是YOLO V1出来后&#xff0c;YOLO V2出来前的一款One-stage目标检测…

Linux0.11 考古笔记

Linux0.11 考古笔记 最近读完《Linux 内核完全注释》和《品读 Linux0.11 核心代码》&#xff0c;大致理解下 Linux0.11 内核的全貌。在我理解这些属于计算机基础类的知识&#xff0c;所以在未来的工作场景不太可能会直接用到它们&#xff0c;如果用不到的话这些知识可能会随着…

从 2022 年优秀 Linux 发行版中挑选你喜欢的版本

导读如果你想从 2022 年最佳 Linux 发行版列表中挑选一个最喜欢的版本&#xff0c;那么今天你需要考虑以下几个选项。 2022 年是充满惊喜的一年&#xff0c;Linux 发行版的表现也不例外。从充满功能的新版本到各种桌面选项&#xff0c;总有一些值得期待的东西。 如果你想从 20…