找回密码
 立即注册
查看: 1127|回复: 20

程序员必须掌握哪些算法?

[复制链接]
发表于 2021-7-10 14:26 | 显示全部楼层 |阅读模式
程序员必须掌握哪些算法?
发表于 2021-7-10 14:35 | 显示全部楼层
程序员必须掌握的常用算法正如 @力扣(LeetCode)所讲,主要包括以下内容:
算法:
1、排序算法:快速排序、归并排序、计数排序
2、搜索算法:回溯、递归、剪枝
3、图论:最短路径、最小生成树、网络流建模
4、动态规划:背包问题、最长子序列、计数问题
5、基础技巧:分治、倍增、二分法、贪心算法


数据结构:
1、数组和链表
2、栈与队列
3、树和图
4、哈希表
5、大/小跟堆,可并堆
6、字符串:字典树、后缀树


还可以在此基础上细分,例如单单排序算法就可以分为以下十种:
对于学习算法,我推荐在力扣上刷题:
力扣 (LeetCode) 官网 - 全球极客挚爱的技术成长平台此外,推荐一个用动画的形式演示 LeetCode 上的题目的项目:
https://github.com/MisterBooo/LeetCodeAnimation例如基础的冒泡排序法演示如下:
选择排序法:


插入排序法:


希尔排序法:


归并排序法:


快速排序法:


堆排序:


计数排序:


桶排序:


基数排序:


该项目正在完善中,已经用动画的形式演示出了一下题目:
因为知乎排版没有表格,所以我就截图了,以上内容详见:
MisterBooo/LeetCodeAnimation

此外,再推荐一些免费的学习资源:
在学习的时候,要想为什么要这样设计,优点在哪里,有什么改进方法,逐步通过这样的方式提升逻辑思维能力。不懂就查,找学习资料和相关解答,坚持学习。
1. 算法学习 LintCode:https://www.lintcode.com/
算法学习网站,上去每天刷两道算法题,走遍天下都不怕。
2. 算法学习 LeetCode:https://leetcode.com/
也是算法题网站,同上。
3. 算法学习 LeetCode 中文站:https://leetcode-cn.com/
这个是上面算法题网站的中文站点,英文不好的可以刷这个,英文好的推荐去刷英文网站的题目,还能提升英语能力。
4. 中国大学MOOC网:https://www.icourse163.org/
中国大学MOOC是由网易与高教社携手推出的在线教育平台,承接教育部国家精品开放课程任务,向大众提供中国知名高校的MOOC课程。在这里,每一个有意愿提升自己的人都可以免费获得更优质的高等教育。


我是程序员客栈,中国领先的程序员自由工作平台,技术新人力解决方案。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-7-10 14:39 | 显示全部楼层
大学四年,算法是我非常注重学习的一门知识。下面是我觉得值得学习的一些算法以及数据结构,当然,并且我也整理一些看过不错的文章给大家,大家也可以留言区补充。如果觉得不错,别忘了点个赞哦。先上图,后详细解说
一、算法最最基础

1、时间复杂度
2、空间复杂度
一般最先接触的就是时间复杂度和空间复杂度的学习了,这两个概念以及如何计算,是必须学的,也是必须最先学的,主要有最大复杂度、平均复杂度等,直接通过博客搜索学习即可。
文章推荐:
算法分析神器—时间复杂度
二、基础数据结构

1、线性表
    列表(必学)链表(必学)跳跃表(知道原理,应用,最后自己实现一遍)并查集(建议结合刷题学习)
不用说,链表、列表必须,不过重点是链表。
三分钟基础数据结构:如何轻松手写链表?
以后有面试官问你「跳跃表」,你就把这篇文章扔给他
2、栈与队列
    栈(必学)队列(必学)优先队列、堆(必学)多级反馈队列(原理与应用)
特别是优先队列,再刷题的时候,还是经常用到的,队列与栈,是最基本的数据结构,必学。可以通过博客来学习。相关文章:
三分钟基础知识:什么是栈?
二叉堆是什么鬼?
【算法与数据结构】堆排序是什么鬼?
3、哈希表(必学)
    碰撞解决方法:开放定址法、链地址法、再次哈希法、建立公共溢出区(必学)布隆过滤器(原理与应用)
哈希表相关的,推荐通过博客来学习,推荐文章:
Hash冲突之开放地址法
4、树
    二叉树:各种遍历(递归与非递归)(必学)哈夫曼树与编码(原理与应用)AVL树(必学)B 树与 B+ 树(原理与应用)前缀树(原理与应用)红黑树(原理与应用)线段树(原理与应用)
树相关是知识还是挺多的,建议看书,可以看《算法第四版》。相关文章:
高频面试题:什么是B树?为啥文件索引要用B树而不用二叉查找树?
【漫画】以后在有面试官问你AVL树,你就把这篇文章扔给他。
腾讯面试题:有了二叉查找树、平衡树为啥还需要红黑树?
【面试被虐】游戏中的敏感词过滤是如何实现的?
5、数组
    树状数组矩阵(必学)
树状数组其实我也没学过,,,,
这里给大家推荐一份刷题笔记,里面把各种算法题型以及经验都总结了,把这份笔记突击学习一下,很多算法考察,基本都稳了,给大家看一下目录




下载链接:
labuladong的算法小抄.zip - 蓝奏云三、各种常见算法

1、十大排序算法
    简单排序:插入排序、选择排序、冒泡排序(必学)分治排序:快速排序、归并排序(必学,快速排序还要关注中轴的选取方式)分配排序:桶排序、基数排序树状排序:堆排序(必学)其他:计数排序(必学)、希尔排序
对于十大算法的学习,假如你不大懂的话,那么我还是挺推荐你去看书的,因为看了书,你可能不仅仅知道这个算法怎么写,还能知道他是怎么来的。推荐书籍是《算法第四版》,这本书讲的很详细,而且配了很多图演示,还是挺好懂的。
推荐文章:
必学十大经典排序算法,看这篇就够了(附完整代码/动图/优质文章)(修订版)
2、图论算法
    图的表示:邻接矩阵和邻接表遍历算法:深度搜索和广度搜索(必学)最短路径算法:Floyd,Dijkstra(必学)最小生成树算法:Prim,Kruskal(必学)实际常用算法:关键路径、拓扑排序(原理与应用)二分图匹配:配对、匈牙利算法(原理与应用)拓展:中心性算法、社区发现算法(原理与应用)
图还是比较难的,不过我觉得图涉及到的挺多算法都是挺实用的,例如最短路径的计算等,图相关的,我这里还是建议看书的,可以看《算法第四版》。
漫画:什么是 “图”?(修订版)
漫画:深度优先遍历 和 广度优先遍历
漫画:图的 “最短路径” 问题
漫画:Dijkstra 算法的优化
漫画:图的 “多源” 最短路径
3、搜索与回溯算法
    贪心算法(必学)启发式搜索算法:A*寻路算法(了解)地图着色算法、N 皇后问题、最优加工顺序旅行商问题
这方便的只是都是一些算法相关的,我觉得如果可以,都学一下。像贪心算法的思想,就必须学的了。建议通过刷题来学习,leetcode 直接专题刷。
4、动态规划
    树形DP:01背包问题线性DP:最长公共子序列、最长公共子串区间DP:矩阵最大值(和以及积)数位DP:数字游戏状态压缩DP:旅行商
我觉得动态规划是最难的一个算法思想了,记得当初第一次接触动态规划的时候,是看01背包问题的,看了好久都不大懂,懵懵懂懂,后面懂了基本思想,可是做题下不了手,但是看的懂答案。一气之下,再leetcdoe专题连续刷了几十道,才掌握了动态规划的套路,也有了自己的一套模板。不过说实话,动态规划,是考的真他妈多,学习算法、刷题,一定要掌握。这里建议先了解动态规划是什么,之后 leetcode 专题刷,反正就一般上面这几种题型。后面有时间,我也写一下我学到的套路,有点类似于我之前写的递归那样,算是一种经验。也就是我做题时的模板,不过感觉得写七八个小时,,,,,有时间就写。之前写的递归文章:为什么你学不会递归?告别递归,谈谈我的一些经验
5、字符匹配算法
    正则表达式模式匹配:KMP、Boyer-Moore
我写过两篇字符串匹配的文章,感觉还不错,看了这两篇文章,我觉得你就差不多懂 kmp 和 Boyer-Moore 了。
字符串匹配Boyer-Moore算法:文本编辑器中的查找功能是如何实现的?
6、流相关算法
    最大流:最短增广路、Dinic 算法最大流最小割:最大收益问题、方格取数问题最小费用最大流:最小费用路、消遣
这方面的一些算法,我也只了解过一些,感兴趣的可以学习下。
总结

对于上面设计到的算法,我都提供了感觉还不错的文章,建议大家收藏,然后可以利用零碎的时间进行阅读,有些人可能会觉得上面的算法太多,说实话,我觉得不多,特别是对于在校生的,上面涉及到的算法可以不用很懂,但至少得了解。至于书籍的话,如果你连基本数据结构都还不懂的,建议看《数据结构与算法》相关书籍,例如《大话数据结构》、《数据结构与算法分析》。如果你有一定的基础,例如知道链表,栈,队列,那么可以看《算法第四版》,不过这本书是用 Java 实现的,不过我觉得你只要学过 C,那么可以看的懂。
这些算法的学习,虽然你觉得学了没有什么用,但还是那些话,它对你的影响是潜意识的,它可以给你打下很深厚的基础内功,如果你想走的更远,那么我推荐学习,标注必学的,那么我觉得,你是真的需要抽时间来学习下,标注原理与应用的,代表你可以不知道怎么用代码实现,但是必得知道它的实现原理以及应用。
算法的学习没有太多捷径,离不开刷题,刷多了就会有感觉了,这里再给大家推荐一份某大佬的 leetcode 刷题笔记,汇聚了上千道 leetcode 题解,并且代码都是 beat 100%:下载链接:
cookbook.zip - 蓝奏云有收获?希望老铁们来个三连击,给更多的人看到这篇文章

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-7-10 14:43 | 显示全部楼层
不 BB,直接上干货,非科班出生,毕业工作后才开始学算法,到目前学了 4 年 !!!
为了让你对数据结构和算法能有个全面的认识,我画了一张图,里面几乎涵盖了所有数据结构和算法书籍中都会讲到的知识点。


这里面有10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树;10个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法。
掌握了这些基础的数据结构和算法,再学更加复杂的数据结构和算法,就会非常容易、非常快。
如果觉得不错,别忘了双击点个赞哦。
在这里也送大家一本帮助我拿到BAT 等一线大厂 offer 的算法笔记,是一位阿里大神写的,对于算法薄弱或者需要提高的同学都十分受用,算法一定是计算机学习的重中之重:
BAT大佬写的Leetcode刷题笔记,看完秒杀80%的算法题!




貌似手机端打开连接有的会出现问题,可以点击这个总结看看:
五分钟学算法:算法与数据结构文章详细分类与整理!-五分钟学算法1、复杂度分析

看动画轻松理解时间复杂度(一)
看动画轻松理解时间复杂度(二)
冰与火之歌:「时间」与「空间」复杂度
每个程序员都应该收藏的算法复杂度速查表
2、基本算法思想

五分钟了解一下什么是「贪心算法 」
有了四步解题法模板,再也不害怕动态规划!
(进阶版)有了四步解题法模板,再也不害怕动态规划!
(再进阶版)有了四步解题法模板,再也不害怕动态规划!
浅谈什么是分治算法
看动画轻松理解「递归」与「动态规划」
浅谈什么是动态规划以及相关的「股票」算法题
深度解析「正则表达式匹配」:从暴力解法到动态规划
3、排序算法

「多图警告」手撕排序算法 – iOS进阶必备
十大经典排序算法动画与解析,看我就够了!(配代码完全版)
这或许是东半球分析十大排序算法最好的一篇文章
4、搜索

几道和「广度优先搜索」有关的算法面试题
初识广度优先搜索与解题套路
从简单二叉树问题重新来看深度优先搜索
5、查找

二分查找算法详解
一网打尽!二分查找解题模版与题型全面解析
面试官,我会写二分查找法!对,没有 bug 的那种!
6、字符串匹配

动画:BM 算法中的坏字符规则与好后缀规则
动画:七分钟理解什么是KMP算法
动画:什么是 BF 算法 ?
动态规划之 KMP 算法详解(配代码版)
7、线性表

如何高效对有序数组/链表去重?
超详细!详解一道高频算法题:数组中的第 K 个最大元素
一道简单的数组遍历题,加上四个条件后感觉无从下手
数组特性的妙用!如何找到「缺失的第一个正数」
剑指 offer 第一题:二维数组中的查找
动画:什么是单调栈?
在数据结构中穿针引线:链表实现栈和队列
从简单的线性数据结构开始:栈与队列
五分钟学算法小知识:用栈实现队列/用队列实现栈
几道和「堆栈、队列」有关的面试算法题
超详细!图解「合并 K 个排序链表」
动画:面试如何轻松手写链表?
LeetCode 上最难的链表算法题,没有之一!
链表算法面试问题?看我就够了!
看动画轻松理解「链表」实现「LRU缓存淘汰算法」
从简单的线性数据结构开始:穿针引线的链表(一)
在数据结构中穿针引线:链表实现栈和队列
8、散列表

五分钟速读:什么是散列表(哈希表)?
什么是哈希洪水攻击(Hash-Flooding Attack)?
几道和散列(哈希)表有关的面试题
如何判断一个元素在亿级数据中是否存在?
9、树

面试前准备:二叉树高频面试题和答案
懵逼树上懵逼果:学习二分搜索树
LeetCode 二叉树问题小总结
从简单二叉树问题重新来看深度优先搜索
几道和「二叉树」有关的算法面试题
详解什么是平衡二叉树(AVL)(修订补充版)
【面试现场】为什么 MySQL 数据库要用B+树存储索引?
字典树概念与题型解析
面试官:为什么 MySQL 的索引要使用 B+ 树,而不是其它树?比如 B 树?
心里没点 B 树。。。
数据结构与算法——最小生成树
植树节,程序猿种的那些树
数据结构与算法——2-3-4树
数据结构与算法——2-3树
看动画轻松理解「Trie树」
10、图

浅谈什么是图拓扑排序
数据结构与算法——图论基础与图存储结构
数据结构与算法:三十张图弄懂「图的两种遍历方式」
数据结构与算法——图最短路径
总结

学习数据结构和算法的过程,是非常好的思维训练的过程,所以,千万不要被动地记忆,要多辩证地思考,多问为什么。
如果你一直这么坚持做,你会发现,等你学完之后,写代码的时候就会不由自主地考虑到很多性能方面的事情,时间复杂度、空间复杂度非常高的垃圾代码出现的次数就会越来越少。
你的编程内功就真正得到了修炼。
在这里向大家推荐一下我的微信公众号:五分钟学算法(ID:CXYxiaowu),专注算法技术分享,包括算法面试题、数据结构、LeetCode、图解算法、漫画算法等;每天推送优质技术文章,精彩视频教程以及项目源码下载,致力做一个实用的公众号。
<hr/>2020 年 01 月 13 日补充:
我再推荐一些算法书籍的选择给大家参考一下。

入门系列

入门的同学,我建议你不要过度追求上去就看经典书。
不要一来就拿着《算法导论》开始啃,初学就去啃这些书肯定会很费劲。你一旦啃不下来,挫败感就会很强。
然后就放弃学算法了。
所以,入门的同学,我建议你找一些比较容易看的书来看,比如《大话数据结构》和《算法图解》。
不要太在意书写得深浅,重要的是能不能坚持看完。
《大话数据结构》 这本书最大的特点是,它把理论讲得很有趣,不枯燥。而且每个数据结构和算法,作者都结合生活中的例子进行了讲解, 能让你有非常直观的感受。
虽然这本书有 400 多页,但是花两天时间读完,应该是没问题的。
如果你之前完全不懂数据结构和算法,可以先从这本书看起。
《算法图解》 跟《大话数据结构》走的是同样的路线,就像这本书副标题写的那样,“像小说一样有趣的算法入门书”,主打“图解”,通俗易懂。它只有不到 200 页,所以内容比较少。
作为入门,看看这本书,能让你对数据结构和算法有个大概的认识。
当然,这些入门书共同的问题是,缺少细节,不够系统,也不够严谨。
所以,如果你想要系统地学数据结构和算法,看这两本书肯定是不够的。
基础系列

通过基本入门算法书的调教,你已经逐渐体会到了算法的魅力,现在正是时候踏入基础系列算法的领域!!!
这些书籍需要你费点心思去阅读。
很多同学在学习的过程中,看到一篇算法科普文章经常会有这样的想法。
哎呀,要是文章的代码是 Java 语言就好了呀。
哎呀,要是文章的代码是 Python 语言就好了呀。
虽然代码并不会很严重影响阅读,但还是有很多强迫症的同学喜欢看到文章的解释代码是自己擅长的。
我这里推荐《数据结构和算法分析》,这本书非常系统、全面、严谨,而且又不是特别难,适合对数据结构和算法有些了解,并且掌握了至少一门编程语言的同学。而且,这个作者也很用心。
他用了三种语言,写了三个版本,分别是:《数据结构与算法分析 :C 语言描述》《数据结构与算法分析:C++ 描述》《数据结构与算法分析:Java 语言描述》。
面试实战系列

大家都知道,对于程序员来说很大程度上算法就是为了应付面试的。
所以,推荐三本有益于面试的书籍,分别是:《剑指 offer》《编程珠玑》《编程之美》。
《剑指 offer》这本书的目的非常明确,就是为了面试。
这本书几乎包含了所有常见的、经典的面试题。如果能搞懂这本书里的内容,应付一般公司的面试应该不成问题。
我做了一个 图解《剑指 offer》的小程序,应该能帮助你学习,感兴趣的可以在微信搜索 图解剑指offer。
我也在 B 站录制了一些图解剑指 offer 的免费视频课程,感兴趣的也可以看看,每个视频控制在5分钟以内。


图解剑指offer:二维数组的查找_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili「双指针」的魅力!图解 LeetCode 第 11 号问题:盛最多水的容器_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili剑指offer精讲图解面试题03. 数组中重复的数字_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliLeetCode 图解 | 739.每日温度_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili

《编程珠玑》这本书的豆瓣评分非常高,有 9 分。
这本书最大的特色就是讲了很多针对海量数据的处理技巧。这个可能是其他算法书籍很少涉及的。面试的时候,海量数据处理的问题也是经常会问的,特别是校招面试。不管是开拓眼界,还是应付面试,这本书都很值得一看。
《编程之美》这本书有多位作者,其中绝大部分是微软的工程师,所以书的质量很有保证。不过,这里面的算法题目稍微有点难,也不是很系统,这也是我把它归到面试这一部分的原因。如果你有一定基础,也喜欢钻研些算法问题,或者要面试 Google、Facebook 这样的公司,可以拿这本书里的题,先来自测一下。
<hr/>2020年05月31日补充:数据结构与算法在平时工作中的作用。

正如 N.Wirth 教授所说的: 数据结构+ 算法=程序
遇到一个实际问题,充分利用所学的数据结构,将数据及其之间的关系有效地存储在计算机中,然后选择合适的算法策略,并用程序高效实现。
这句话可能有点抽象,我举个例子给你们解释一下
在工作过程中,我们多多少少都接触过 OAuth2 ,在使用 OAuth2 授权的时候,通常应用会弹出一个类似这样的信息:
1) 获取用户基本信息接口


2) 获取用户列表接口


3) 用户分组管理接口


。。。
微信获取授权
思考一下,如果让你设计数据库,应该怎么设计信息存储权限?
如何你熟练掌握了各种数据结构的特点的话,那自然而然想到使用 bitmap 来存储权限。
我们把权限划分成最小粒度之后,每一个 bit 都它的含义, 例如我们把权限划分为以下几种:
    获取你的头像、性别、昵称等基本用户信息以你的身份发布微博获取你的好友列表获取你的朋友圈信息
每勾选一个选项,就代表着这个权限被授权,为了保证可扩展性,我们使用一个 uint64 来保存这些 bit ,也就是说,我们一共可以划分 64 种细分权限,然后对这些权限进行组合。
例如,第一个 bit 如果设置了,那么就代表可以获取你的昵称、头像、地区、性别等基本用户信息, 第二个 bit 如果设置了,就可以用你的身份发状态。
数据结构的实际作用还有挺多,感兴趣的可以搜索以下知识点:
    二叉树搜索用于中断处理、登记缓存查找等哈希表,用于实现索引节点、文件系统完整性检查等红黑树用于调度、虚拟内存管理、跟踪文件描述符和目录条目等Radix树,用于内存管理、NFS相关查找和网络相关的功能......
上面这些例子是关于数据结构的,我再举一个算法的例子,如果有帮助,不妨点个赞收藏一下,好的内容值得肯定。
同样的也来思考一个问题:计算机的缓存容量无论再大,缓存满了还是要删除一些内容,给新内容腾位置。
那么删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留在缓存里,方便之后继续使用。那么,什么样的数据,我们判定为「有用的」的数据呢?
这个时候采取的策略就是 LRU 缓存淘汰算法
LRU 的全称是 Least Recently Used,也就是说我们认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。
具体的关于 LRU 缓存淘汰算法 的介绍可以看我之前写的一篇文章。


补充

再推荐一个阿里朋友的算法刷题的开源项目。
截至 2020 年 11 月,该开源项目配套的网站已经有一百二十万的访问量,在 GitHub 上收获了 8500 颗小星星。
这个开源项目是@halfrost(中文名一缕殇流化隐半边冰,简称霜神)去年刷算法题时整理出的 520 题,每道题都写了解题思路,全部都是 GO 实现的,并且每题都 runtime beats 100% 了。






至于为什么要求每题都 runtime beats 100%。  
霜神是这样回复的:优化到 beats 100% 才算是把这题做出感觉了。有好几道 Hard 题,可以用暴力解法 AC 了,但只 beats 了 5%,这题就如同没做一样;而且面试中如果给了这样的答案,面试官也不会满意,“还有没有更优解?”。如果通过自己的思考能给出更优解,面试官会更满意一些。
如果你把这些题解都摸透,相信在面试环节你可以从容的回答“还有没有更优解”。


作者介绍:霜神是前阿里巴巴资深后端工程师,业余时间酷爱写博客,目前他的博客已经有 300W+ 的浏览量,是 iOS 开发届的大佬级别人物,霜神为人谦和,上周六我说能不能提供一份离线电子书,方便读者阅读,他立马熬夜研究,修改了好几个版本。
离线版笔记下载地址(已获授权)链接: https://pan.baidu.com/s/1prMLkrf7MqANVyrqZjzPhw 密码: gjht
离线版笔记下载地址(已获授权): LeetCode - Go 电子书下载



我的专栏:
和程序员小吴一起学算法我的其它高赞回答:
程序员必须掌握哪些算法?字节跳动面试难吗,应该如何应对?大家都是如何刷 LeetCode 的?算法网站推荐?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-7-10 14:48 | 显示全部楼层
力扣 (LeetCode) 作为全球极客挚爱的技术成长平台,致力于为同学们面试、求职提供帮助。
向下阅读的同学一定要注意,下方 图片可能会带来不适,有密集恐惧症的小伙伴们请建议迅速划过。
在这里,力扣君也为大家整理了一些程序员在 面试中 需要掌握的算法,熟练掌握它们可以帮你在面试中如虎添翼,百战百胜。
算法 - Algorithms
    排序算法:快速排序、归并排序、计数排序搜索算法:回溯、递归、剪枝技巧图论:最短路、最小生成树、网络流建模动态规划:背包问题、最长子序列、计数问题基础技巧:分治、倍增、二分、贪心
数据结构 - Data Structures
    数组与链表:单 / 双向链表、跳舞链栈与队列树与图:最近公共祖先、并查集哈希表堆:大 / 小根堆、可并堆字符串:字典树、后缀树
在 互联网公司最常见的面试算法题有哪些? 问题下的回答中,力扣君更加详细地整理了一些面试常见的算法并且对每种算法罗列了很多题目,在此就不赘述了,感兴趣的同学可以点击链接了解更多内容。
互联网公司最常见的面试算法题有哪些?如果不谈面试的需求,对于程序员来说上面提到的那些算法依然非常重要,可以说上述内容都是作为一个程序员必须掌握的算法
有人可能会觉得,这些基础的算法在工作中完全用不到,安安静静地做一个 CRUD Boy 多好。
其实不然,虽然同是程序员,程序员之间也是可以分出个三六九等的。一名出色的程序员一定是熟练掌握各种算法的。扎实地理解与掌握这些基础算法,能帮助你收获更强的竞争力,在自己的岗位上快速晋升。
那熟练掌握这些算法,到底可以为身为程序员的我们带来什么呢?
提升代码效率

比如,现在让你实现这样一个功能:给你一些有序的数字,动态地查找目标数字。实现这一功能的方法有很多种,当面临不同情况的时候,我们需要使用不同的方法。
    查找频率很低时,对于每一次查询,暴力从前向后遍历,每次查询的复杂度为 O(N),能解决问题。当查找频率很高时,对有序数字使用二分查找,每次查询复杂度为 O(logN)。或者使用哈希表,每次查询的复杂度为 O(1)。如果数字非常多存不进内存里,可以使用 B树 的思路来优化查询。当引入密集的插入操作,查询不太密集的时候,可以使用 LSM树 的思想完成这一功能。
如果你熟知各种基础算法,那么你就可以很容易地针对不同的场景找到合适的解决方案,并且将它们变成代码,以提升程序的效率。而不是遇事不决,先上暴力,虽然解决了问题,但是在时间与空间上还有很多不足。
提升能力、借鉴思路、获得启发

通过学习这些算法,可以提升我们在计算机方面的能力:抽象建模能力、逻辑思维能力等,并且积累一些解决问题的基本思路:折半、倍增、贪心、分治等。
现实中的问题都大相径庭,但是我们通过将其抽象并建模之后,会发现问题的本质是相似的,我们往往可能从某一个基础算法中获得启发,从而高效地解决问题。而达到这一境界,就要求我们首先对基础算法能非常了解,并达到熟练运用,融会贯通的地步。
所以,即使过了公司面试这一关,算法对于程序员来说依然是非常重要的。熟练掌握算法,将是你职场晋升路上的一把利刃。还是那句话,奔着求职、面试、晋升的小伙伴,刷 力扣 拿到你的 Dream Offer,走向人生巅峰!
下面给大家推荐几个算法学习的网站:
对于算法的学习的平台,只要关注以下三个维度来进行选择:
    第一、能够学习算法的原理,在动态规划之后的很多算法,都需要花时间去理解,需要有一个学习的过程。所以,学习算法的过程无论是边学边做还是简单的学习,最好可以附带一定的教学功能。第二、可以对算法进行练习的在线测评系统 (OnlineJudge)。如何选择一个适合自己的 OJ 去练习?力扣君认为首先要看其支持的语言种类与检索功能,另外是否有定期的原创题目,原创比赛。是的,原创非常重要!(敲小黑板咯!)第三、针对第二点,明确进行算法练习的目的,单纯为了提升算法能力还是在提升的同时也想为自己的技术面试做准备。
<hr/>一、力扣 Leetcode(http://leetcode-cn.com)

    集教学与和练习与一体
力扣的 探索卡片 针对每一种算法都有详细的教学和习题,非常适合初学者来进行练习。
    技术面试必备刷题平台
拥有上千道原创算法题的 力扣题库,国内外不少知名 IT 公司技术面试时的首选题库。如果你正在准备技术面试,来力扣刷题肯定没错。
题库 - 力扣 (LeetCode)
    活跃的社区
在力扣 题解 版块发起对一道题的讨论,如果你没有好的解题思路,可以和其他小伙伴一起学习交流。
    每周一次的原创竞赛题
参加每周一次的 力扣周赛,你可以通过周赛来赢取力扣积分兑换相应奖励,如果时间没有那么充裕,也可以参加力扣的虚拟竞赛。力扣的竞赛题更偏求职风格,比赛过程中错误的数据会显示出来以方便选手调式,对多数程序员来说可以说相当友好啦!
二、Github (https://github.com)

Github 除了开源项目以外,也有一些大型的学习算法的项目。比如:
    著名的算法可视化项目
https://github.com/algorithm-visualizer/algorithm-visualizer
其中将许多著名的算法都做了可编辑的动画,对于后期理解图论等相关算法有很大帮助。
    基于 Python 的算法合集
https://github.com/qiwsir/algorithm
亮点是这个项目是中文的,算法由浅入深,相对适合从零开始学算法的。
三、Topcoder(http://topcoder.com)

想挑战自我?可以试下 TopCoder
参加过 TopCoder 比赛的童鞋可能会对它印象深刻,它有它独特的魅力。TopCoder 没有测试用例,在比赛中,完成代码后可以去直接阅读别人的算法,并构造错误用例来为对方扣分。
可以说 TopCoder 很适合学有所成的人去寻找下刺激,不过建议有一定的刷题基础再进行尝试。
四、Coursera(Coursera | Online Courses & Credentials by Top Educators. Join for Free)

Coursera 上有各所大学的算法课程。有很多都是世界顶尖的算法课,有兴趣学习的程序员也可以不妨前去一试。
<hr/>欢迎各位知友关注力扣官方微信公众号:「LeetCode力扣」,更多关于程序员面试、技术干货的内容等你来啃!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-7-10 14:53 | 显示全部楼层
注:本篇回答主要针对程序员校招求职。
常考面试算法题类型总结

结合2019春招和秋招真题,以下几类算法题最常考,汇总了一下:

一、暴力枚举
    好多鱼!DNA合成连续整数序列和01翻转最长公共连续子串组装三角形最小的矩形字符串分类优美的回文串赶去公司调整队形集合涂棋盘小易记单词分饼干买帽子度度熊回家寻找三角形有趣的排序神奇数添加字符数组变换
二、动态规划
    页码统计创造新世界双核处理堆砖块不等式数列牛牛的数列暗黑的字符串数字和为sum的方法数
三、DFS/BFS
    推箱子工作安排幸运的袋子饥饿的小易跳石板地下迷宫
四、数学
    超级素数幂找整除魔力手环混合颜料最大的奇约数末尾0的个数
五、模拟实现
    平衡数消除重复元素奇怪的表达式求值变换次数
六、贪心算法
    排序子序列组队竞赛训练部队
七、字符串算法
    循环单词
练习题库推荐

推荐两个题库,面试算法题基本上都是从里面出的或者变形而来:
1、剑指Offer
2、leetcode在线编程
算法视频推荐

1、如何学习算法:十年算法刷题大牛手把手传授算法学习经验
2、算法基础入门
3、算法基础提升
4、求职算法真题精讲-中级
5、求职算法真题精讲-高级
6、直通BAT算法精讲
算法专栏推荐

《程序员代码面试指南》左程云
互联网名企面经精选

因为每家公司的侧重点不同,所以他们面试时考的题目类型也不同。如果能提前知道每家公司考题的风格,临到自己上考场就会轻松很多。整理了一些前辈们的面试经验分享给大家:
2020秋招面经大汇总!(岗位划分)_笔经面经_牛客网最后,祝大家都能拿到自己最想要的那份offer,加油~
发表于 2021-7-10 15:02 | 显示全部楼层
每种程序员的需求很不一样,与其谈具体的算法,不如说最基本应掌握复杂度、穷举、分治、回溯、贪心、动态规划等算法基础理论。

P.S. 如果我当面试官问 游戏编程里面有哪些经典或者很酷的算法? - Milo Yip 的回答 里类似的算法,估计很难请人。实际工作需要不断学习(甚至研究)领域相关的算法去解决问题,没有什么必须掌握的。
发表于 2021-7-10 15:11 | 显示全部楼层
十大经典排序算法最强总结(含Java代码实现)

0、排序算法说明

0.1 排序的定义
对一序列对象根据某个关键字进行排序。
0.2 术语说明
    稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;内排序:所有排序操作都在内存中完成;外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;时间复杂度: 一个算法执行所耗费的时间。空间复杂度:运行完一个程序所需内存的大小。
0.3 算法总结
图片名词解释:
    n: 数据规模k: “桶”的个数In-place: 占用常数内存,不占用额外内存Out-place: 占用额外内存
0.4 算法分类
0.5 比较和非比较的区别
常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。
冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为O(n)。在归并排序、快速排序之类的排序中,问题规模通过分治法消减为logN次,所以时间复杂度平均O(nlogn)
比较排序的优势是,适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。
计数排序、基数排序、桶排序则属于非比较排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组arr,计算arr之前有多少个元素,则唯一确定了arr在排序后数组中的位置。
非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度O(n)
非比较排序时间复杂度底,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求。
1、冒泡排序(Bubble Sort)

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
1.1 算法描述
    比较相邻的元素。如果第一个比第二个大,就交换它们两个;对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;针对所有的元素重复以上的步骤,除了最后一个;重复步骤1~3,直到排序完成。
1.2 动图演示
1.3 代码实现
/**
     * 冒泡排序
     *
     * @param array
     * @return
     */
    public static int[] bubbleSort(int[] array) {
        if (array.length == 0)
            return array;
        for (int i = 0; i < array.length; i++)
            for (int j = 0; j < array.length - 1 - i; j++)
                if (array[j + 1] < array[j]) {
                    int temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                }
        return array;
    }1.4 算法分析
最佳情况:T(n) = O(n)   最差情况:T(n) = O(n2)   平均情况:T(n) = O(n2)
2、选择排序(Selection Sort)

表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
2.1 算法描述
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
    初始状态:无序区为R[1..n],有序区为空;第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;n-1趟结束,数组有序化了。
2.2 动图演示
2.3 代码实现
/**
     * 选择排序
     * @param array
     * @return
     */
    public static int[] selectionSort(int[] array) {
        if (array.length == 0)
            return array;
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;
            for (int j = i; j < array.length; j++) {
                if (array[j] < array[minIndex]) //找到最小的数
                    minIndex = j; //将最小数的索引保存
            }
            int temp = array[minIndex];
            array[minIndex] = array;
            array = temp;
        }
        return array;
    }2.4 算法分析
最佳情况:T(n) = O(n2)  最差情况:T(n) = O(n2)  平均情况:T(n) = O(n2)
3、插入排序(Insertion Sort)

插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
3.1 算法描述
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
    从第一个元素开始,该元素可以认为已经被排序;取出下一个元素,在已经排序的元素序列中从后向前扫描;如果该元素(已排序)大于新元素,将该元素移到下一位置;重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;将新元素插入到该位置后;重复步骤2~5。
3.2 动图演示
3.3 代码实现
/**
     * 插入排序
     * @param array
     * @return
     */
    public static int[] insertionSort(int[] array) {
        if (array.length == 0)
            return array;
        int current;
        for (int i = 0; i < array.length - 1; i++) {
            current = array[i + 1];
            int preIndex = i;
            while (preIndex >= 0 && current < array[preIndex]) {
                array[preIndex + 1] = array[preIndex];
                preIndex--;
            }
            array[preIndex + 1] = current;
        }
        return array;
    }3.4 算法分析
最佳情况:T(n) = O(n)   最坏情况:T(n) = O(n2)   平均情况:T(n) = O(n2)
4、希尔排序(Shell Sort)

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
4.1 算法描述
我们来看下希尔排序的基本步骤,在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2...1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
    选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;按增量序列个数k,对序列进行k 趟排序;每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
4.2 过程演示
4.3 代码实现
/**
     * 希尔排序
     *
     * @param array
     * @return
     */
    public static int[] ShellSort(int[] array) {
        int len = array.length;
        int temp, gap = len / 2;
        while (gap > 0) {
            for (int i = gap; i < len; i++) {
                temp = array;
                int preIndex = i - gap;
                while (preIndex >= 0 && array[preIndex] > temp) {
                    array[preIndex + gap] = array[preIndex];
                    preIndex -= gap;
                }
                array[preIndex + gap] = temp;
            }
            gap /= 2;
        }
        return array;
    }4.4 算法分析
最佳情况:T(n) = O(nlog2 n)  最坏情况:T(n) = O(nlog2 n)  平均情况:T(n) =O(nlog2n) 
5、归并排序(Merge Sort)

和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
5.1 算法描述
    把长度为n的输入序列分成两个长度为n/2的子序列;对这两个子序列分别采用归并排序;将两个排序好的子序列合并成一个最终的排序序列。
5.2 动图演示
5.3 代码实现
/**
     * 归并排序
     *
     * @param array
     * @return
     */
    public static int[] MergeSort(int[] array) {
        if (array.length < 2) return array;
        int mid = array.length / 2;
        int[] left = Arrays.copyOfRange(array, 0, mid);
        int[] right = Arrays.copyOfRange(array, mid, array.length);
        return merge(MergeSort(left), MergeSort(right));
    }
    /**
     * 归并排序——将两段排序好的数组结合成一个排序数组
     *
     * @param left
     * @param right
     * @return
     */
    public static int[] merge(int[] left, int[] right) {
        int[] result = new int[left.length + right.length];
        for (int index = 0, i = 0, j = 0; index < result.length; index++) {
            if (i >= left.length)
                result[index] = right[j++];
            else if (j >= right.length)
                result[index] = left[i++];
            else if (left > right[j])
                result[index] = right[j++];
            else
                result[index] = left[i++];
        }
        return result;
    }5. 4 算法分析
最佳情况:T(n) = O(n)  最差情况:T(n) = O(nlogn)  平均情况:T(n) = O(nlogn)
6、快速排序(Quick Sort)

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
6.1 算法描述
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
    从数列中挑出一个元素,称为 “基准”(pivot);重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
6.2 动图演示
6.3 代码实现
/**
     * 快速排序方法
     * @param array
     * @param start
     * @param end
     * @return
     */
    public static int[] QuickSort(int[] array, int start, int end) {
        if (array.length < 1 || start < 0 || end >= array.length || start > end) return null;
        int smallIndex = partition(array, start, end);
        if (smallIndex > start)
            QuickSort(array, start, smallIndex - 1);
        if (smallIndex < end)
            QuickSort(array, smallIndex + 1, end);
        return array;
    }
    /**
     * 快速排序算法——partition
     * @param array
     * @param start
     * @param end
     * @return
     */
    public static int partition(int[] array, int start, int end) {
        int pivot = (int) (start + Math.random() * (end - start + 1));
        int smallIndex = start - 1;
        swap(array, pivot, end);
        for (int i = start; i <= end; i++)
            if (array <= array[end]) {
                smallIndex++;
                if (i > smallIndex)
                    swap(array, i, smallIndex);
            }
        return smallIndex;
    }

    /**
     * 交换数组内两个元素
     * @param array
     * @param i
     * @param j
     */
    public static void swap(int[] array, int i, int j) {
        int temp = array;
        array = array[j];
        array[j] = temp;
    }6.4 算法分析
最佳情况:T(n) = O(nlogn)   最差情况:T(n) = O(n2)   平均情况:T(n) = O(nlogn) 
7、堆排序(Heap Sort)

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
7.1 算法描述
    将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
7.2 动图演示
7.3 代码实现
注意:这里用到了完全二叉树的部分性质:详情见《数据结构二叉树知识点总结》
//声明全局变量,用于记录数组array的长度;
static int len;
    /**
     * 堆排序算法
     *
     * @param array
     * @return
     */
    public static int[] HeapSort(int[] array) {
        len = array.length;
        if (len < 1) return array;
        //1.构建一个最大堆
        buildMaxHeap(array);
        //2.循环将堆首位(最大值)与末位交换,然后在重新调整最大堆
        while (len > 0) {
            swap(array, 0, len - 1);
            len--;
            adjustHeap(array, 0);
        }
        return array;
    }
    /**
     * 建立最大堆
     *
     * @param array
     */
    public static void buildMaxHeap(int[] array) {
        //从最后一个非叶子节点开始向上构造最大堆
        for (int i = (len/2 - 1); i >= 0; i--) { //感谢 @让我发会呆 网友的提醒,此处应该为 i = (len/2 - 1)
            adjustHeap(array, i);
        }
    }
    /**
     * 调整使之成为最大堆
     *
     * @param array
     * @param i
     */
    public static void adjustHeap(int[] array, int i) {
        int maxIndex = i;
        //如果有左子树,且左子树大于父节点,则将最大指针指向左子树
        if (i * 2 < len && array[i * 2] > array[maxIndex])
            maxIndex = i * 2;
        //如果有右子树,且右子树大于父节点,则将最大指针指向右子树
        if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex])
            maxIndex = i * 2 + 1;
        //如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
        if (maxIndex != i) {
            swap(array, maxIndex, i);
            adjustHeap(array, maxIndex);
        }
    }7.4 算法分析
最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(nlogn) 平均情况:T(n) = O(nlogn)
8、计数排序(Counting Sort)

计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。
8.1 算法描述
    找出待排序的数组中最大和最小的元素;统计数组中每个值为i的元素出现的次数,存入数组C的第i项;对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
8.2 动图演示
8.3 代码实现
/**
     * 计数排序
     *
     * @param array
     * @return
     */
    public static int[] CountingSort(int[] array) {
        if (array.length == 0) return array;
        int bias, min = array[0], max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array > max)
                max = array;
            if (array < min)
                min = array;
        }
        bias = 0 - min;
        int[] bucket = new int[max - min + 1];
        Arrays.fill(bucket, 0);
        for (int i = 0; i < array.length; i++) {
            bucket[array + bias]++;
        }
        int index = 0, i = 0;
        while (index < array.length) {
            if (bucket != 0) {
                array[index] = i - bias;
                bucket--;
                index++;
            } else
                i++;
        }
        return array;
    }8.4 算法分析
当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。
最佳情况:T(n) = O(n+k)  最差情况:T(n) = O(n+k)  平均情况:T(n) = O(n+k)
9、桶排序(Bucket Sort)

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排
9.1 算法描述
    人为设置一个BucketSize,作为每个桶所能放置多少个不同数值(例如当BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3);遍历输入数据,并且把数据一个一个放到对应的桶里去;对每个不是空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序;从不是空的桶里把排好序的数据拼接起来。
注意,如果递归使用桶排序为各个桶排序,则当桶数量为1时要手动减小BucketSize增加下一循环桶的数量,否则会陷入死循环,导致内存溢出。
9.2 图片演示
9.3 代码实现
/**
     * 桶排序
     *
     * @param array
     * @param bucketSize
     * @return
     */
    public static ArrayList<Integer> BucketSort(ArrayList<Integer> array, int bucketSize) {
        if (array == null || array.size() < 2)
            return array;
        int max = array.get(0), min = array.get(0);
        // 找到最大值最小值
        for (int i = 0; i < array.size(); i++) {
            if (array.get(i) > max)
                max = array.get(i);
            if (array.get(i) < min)
                min = array.get(i);
        }
        int bucketCount = (max - min) / bucketSize + 1;
        ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount);
        ArrayList<Integer> resultArr = new ArrayList<>();
        for (int i = 0; i < bucketCount; i++) {
            bucketArr.add(new ArrayList<Integer>());
        }
        for (int i = 0; i < array.size(); i++) {
            bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));
        }
        for (int i = 0; i < bucketCount; i++) {
            if (bucketSize == 1) { // 如果带排序数组中有重复数字时  感谢 @见风任然是风 朋友指出错误
                for (int j = 0; j < bucketArr.get(i).size(); j++)
                    resultArr.add(bucketArr.get(i).get(j));
            } else {
                if (bucketCount == 1)
                    bucketSize--;
                ArrayList<Integer> temp = BucketSort(bucketArr.get(i), bucketSize);
                for (int j = 0; j < temp.size(); j++)
                    resultArr.add(temp.get(j));
            }
        }
        return resultArr;
    }9.4 算法分析
桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。
最佳情况:T(n) = O(n+k)   最差情况:T(n) = O(n+k)   平均情况:T(n) = O(n2)  
10、基数排序(Radix Sort)

基数排序也是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),为数组长度,k为数组中的数的最大的位数;
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。
10.1 算法描述
    取得数组中的最大数,并取得位数;arr为原始数组,从最低位开始取每个位组成radix数组;对radix进行计数排序(利用计数排序适用于小范围数的特点);
10.2 动图演示
10.3 代码实现
/**
     * 基数排序
     * @param array
     * @return
     */
    public static int[] RadixSort(int[] array) {
        if (array == null || array.length < 2)
            return array;
        // 1.先算出最大数的位数;
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            max = Math.max(max, array);
        }
        int maxDigit = 0;
        while (max != 0) {
            max /= 10;
            maxDigit++;
        }
        int mod = 10, div = 1;
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();
        for (int i = 0; i < 10; i++)
            bucketList.add(new ArrayList<Integer>());
        for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
            for (int j = 0; j < array.length; j++) {
                int num = (array[j] % mod) / div;
                bucketList.get(num).add(array[j]);
            }
            int index = 0;
            for (int j = 0; j < bucketList.size(); j++) {
                for (int k = 0; k < bucketList.get(j).size(); k++)
                    array[index++] = bucketList.get(j).get(k);
                bucketList.get(j).clear();
            }
        }
        return array;
    }10.4 算法分析
最佳情况:T(n) = O(n * k)   最差情况:T(n) = O(n * k)   平均情况:T(n) = O(n * k)
基数排序有两种方法:
MSD 从高位开始进行排序 LSD 从低位开始进行排序
基数排序 vs 计数排序 vs 桶排序

这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
    基数排序:根据键值的每位数字来分配桶计数排序:每个桶只存储单一键值桶排序:每个桶存储一定范围的数值
我有一个微信公众号,经常会分享一些Java技术相关的干货;如果你喜欢我的分享,可以用微信搜索“Java团长”或者“javatuanzhang”关注。
<hr/>
作者:郭耀华
链接:https://www.cnblogs.com/guoyaohua/p/8600214.html
来源:博客园

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-7-10 15:14 | 显示全部楼层
我觉得两个算法是必须要掌握的。
第一个算法是:怎么算时薪。
第二个算法是:钱以外的东西能带来的幸福度的量化算法
发表于 2021-7-10 15:15 | 显示全部楼层
温酒的答案说的好极了,我也发挥下:
第一个算法是计算终身年薪

终生范围的年薪。以下数字都是假的,只是说明算法,无实际意义。


比如26岁可以开始工作,如果40岁就下岗,实际上一年100万,也只有1400万,要除以整个工作年龄(65-26 = 39),平均年薪大概只有30多万。而且累积税率下,高收入年份集中在26-40岁非常吃亏。


但,这没有考虑到房价上涨因素,因为房价上涨时期,前14年的收入会因为房产而大量增值,使得算法更加复杂。


还有比如假设能做到50岁,那么晚工作5年(比如多读个火坑博士)损失的钱是多少呢?假设起薪是100万,50岁下岗时是200万一年,实际上损失的是最后5年的收入,大概200X5 = 1000万。所以写码要趁早。


其次要算地点年薪。比如工作30年同样年薪,在一个房价上涨的地区,会在退休时能提出更多的钱,尤其是50多岁把一线城市或是加州的房子一卖回乡下养老或是环游世界,美滋滋。


第三要算成长年薪,同样100万的工作,有些技能会保证10年后不仅不失业,反而会上涨到150万,而有些技能会在衰退中,10年后可能只有50万甚至失业。
第二个算法是LeetCode一道题的价格

我当年做过粗略计算,很多LeetCode题做一道可以提升年薪300-500美元。假设400道Medium、Hard题可以拿到大厂Offer,那么很可能不刷题的人上限也就是不到20万,而大厂senior上限可以轻松35-40万。差不多20万的差距,除以400 等于 500美元。


所以一道Medium或是Hard题的价值大概是500美元年薪每年。而且这个可怕在于是累积的,每年500, 20年下来就是1万块一道题。这还不算大厂背景对个人的加分、对失业的强抵抗力等,只是单单年薪上的收入(当然,税后会少很多)。


这个“算法”掌握了,你才有动力去学习LeetCode的算法,你就不会觉得它折磨人了。LeetCode这么一看,简直跟金山一样,还不去挖?
第三个算法是算时薪和效率

Google和微软的时薪就比较高,FB亚麻就相对低。这种时薪不光是用hours计算,还用体力计算。比如微软上班干两个小时活,扯六个小时蛋,然后回家精神抖擞,可以去卖房子、创业、炒股票等,相当于一天多出来4个小时有效时间;FB上班干七个小时活,被扯两个小时蛋,通勤再耗去一个半小时,回家就瘫痪了。


千万别看什么华为他们996效率高。我算过,他们效率很低:中午吃饭吃一个小时,还要午休一个小时,加上重新进入状态的时间,中国的12小时一天,实际上也就相当于美国的9小时一天左右,因为我们这里9小时是真的9个小时。


第四个算福利

公司免费三餐大概省多少钱?早饭就算0(因为可以不吃),午饭算10美元,晚饭算15美元,一年工作220天,25X220 = 5500美元。但这是税后的,所以5500 要乘以1.5(税率按33%近似) = 8250美元税前。


还要算时间账:午餐和晚餐大概各省半个小时的话(不需要开车出去或是下楼吃等),一年会省220小时,相当于多出来220/8 = 27天。


还要算健康账:因为公司有大量的蔬菜水果等。食物种类多变更有利于身体。


还要算士气账:免费三餐大概能提升10%-30%的员工士气(我是吃货,所以+30%)。


结论:三餐免费每年带来很大的收益,无论公司还是员工。
第五个算面试大厂的成功概率

假设一个人面一家大厂的成功率是25%,大厂有8家,冷冻期均是一年,连续坚持不懈面5年,一个offer都拿不到的概率是:
(1-0.25)^ (8*5) = 0.75 ^ 40 = 0.00001 = 0.001%


假设这人只有5%的成功率,且一年只面了4家,连续面5年,一个offer都拿不到的概率是:
(1-0.05)^ (4*5) = 0.95 ^ 20 = 0.3584 = 35.84%


可见,一个只有5%成功率的人,坚持面5年也有大概2/3的概率能进大厂。


第二个假设跟我在现实中的观察很相似,也解释了为什么很多看似不强的人也进了大厂。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-21 05:37 , Processed in 0.078849 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表