├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── others.md │ └── translate.md ├── .gitignore ├── README.md ├── pictures ├── 4keyboard │ ├── 1.jpg │ └── title.png ├── BST │ ├── BST_example.png │ ├── bst_deletion_case_1.png │ ├── bst_deletion_case_2.png │ ├── bst_deletion_case_3.png │ └── 假BST.png ├── Chrome插件 │ ├── baidumonkey.png │ ├── baidu广告.png │ ├── csdnBlock.png │ ├── githubzip.png │ ├── listen1.png │ ├── monkey.png │ ├── oneTab.png │ ├── pin.png │ ├── tree.png │ └── youhou优化.png ├── LCS │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── dp.png │ └── lcs.png ├── LRU算法 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ └── 4.jpg ├── algo4 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ └── title.png ├── backtrack │ ├── ink-image (1).png │ ├── ink-image (2).png │ ├── ink-image (3).png │ ├── ink-image (4).png │ ├── ink-image (5).png │ ├── ink-image (6).png │ ├── ink-image.png │ ├── nqueens.png │ ├── permutation.png │ ├── 代码.png │ ├── 代码1.png │ ├── 代码2.png │ ├── 代码3.png │ └── 全排列.png ├── backtracking │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ └── 7.jpg ├── calculator │ ├── 1.1.jpg │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ └── 6.jpg ├── dupmissing │ ├── 1.gif │ ├── 2.jpg │ └── 3.jpg ├── editDistance │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── delete.gif │ ├── dp.jpg │ ├── edit.gif │ ├── insert.gif │ ├── replace.gif │ └── title.png ├── floodfill │ ├── floodfill.gif │ ├── floodfill.png │ ├── leetcode.png │ ├── ppt1.PNG │ ├── ppt2.PNG │ ├── ppt3.PNG │ ├── ppt4.PNG │ ├── ppt5.PNG │ ├── xiaoxiaole.jpg │ ├── 扫雷.png │ ├── 抠图.jpeg │ └── 抠图.jpg ├── group.jpg ├── heap │ ├── 1.png │ ├── delete.gif │ ├── insert.gif │ ├── sink.gif │ └── swim.gif ├── intersection │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.gif │ └── title.png ├── interval │ ├── 1.gif │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── title1.png │ └── title2.png ├── kgroup │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ ├── 8.gif │ └── title.png ├── kmp │ ├── 1.gif │ ├── 2.gif │ ├── 3.gif │ ├── A.gif │ ├── allstate.jpg │ ├── back.jpg │ ├── dfa.gif │ ├── exp1.jpg │ ├── exp2.jpg │ ├── exp3.jpg │ ├── exp4.jpg │ ├── exp5.jpg │ ├── exp6.jpg │ ├── exp7.jpg │ ├── forward.jpg │ ├── kmp.gif │ ├── shadow.jpg │ ├── shadow1.jpg │ ├── shadow2.jpg │ ├── state.jpg │ ├── state2.jpg │ ├── state4.jpg │ ├── txt1.jpg │ ├── txt2.jpg │ ├── txt3.jpg │ ├── txt4.jpg │ └── z.jpg ├── labuladong.jpg ├── labuladong.png ├── linux-fs │ ├── application.png │ ├── apt.png │ ├── bin.png │ ├── boot.png │ ├── cpu.png │ ├── desktop.png │ ├── dev.png │ ├── etc.png │ ├── home.png │ ├── linux-filesystem.png │ ├── log.png │ ├── opt.png │ ├── proc.png │ ├── root.png │ ├── sbin.png │ ├── tmp.png │ ├── usr.png │ └── usrbin.png ├── linuxProcess │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ └── 8.jpg ├── linuxshell │ └── 1.png ├── mergeInterval │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.gif │ └── title.png ├── online │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── others │ └── leetcode.jpeg ├── pancakeSort │ ├── 1.jpg │ ├── 2.png │ ├── 3.jpg │ ├── 4.jpg │ └── title.png ├── pay.jpg ├── prime │ └── 1.gif ├── qrcode.jpg ├── redis入侵 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png ├── robber │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── title.png │ └── title1.png ├── session │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.jpg ├── unionfind │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ ├── 8.jpg │ └── 9.gif ├── unionfind应用 │ ├── 1.jpg │ ├── 2.jpg │ └── 3.jpg ├── youtube │ ├── 1.png │ ├── 1573133096614.jpeg │ ├── 1573133131308.jpeg │ ├── 2.jpg │ ├── 3.jpg │ └── 4.jpg ├── 二分应用 │ ├── title1.png │ └── title2.png ├── 二分查找 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── binarySearch1.png │ ├── binarySearch2.png │ └── poem.png ├── 位操作 │ ├── 1.png │ └── title.png ├── 信封嵌套 │ ├── 0.jpg │ ├── 1.jpg │ ├── 2.jpg │ └── title.png ├── 前缀和 │ ├── 1.jpg │ ├── 2.jpg │ └── title.png ├── 动态规划详解 │ ├── coindp.png │ ├── coinfunc.png │ ├── cointree.png │ ├── fibdp.png │ ├── fibfunc.png │ ├── fibmemo.png │ ├── fibtree.png │ ├── img_20190514_013033.441.png │ ├── img_20190514_013830.397.png │ ├── ink-image (1).png │ ├── ink-image (2).png │ ├── ink-image (3).png │ ├── ink-image (4).png │ └── ink-image.png ├── 动态规划详解进阶 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── coin.png │ └── fib.png ├── 单调栈 │ ├── 1.png │ ├── 2.png │ └── 3.png ├── 单调队列 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── title.png ├── 博弈问题 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── 双指针 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── center.png │ └── title.png ├── 反转链表 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ └── title.png ├── 回文 │ └── title.png ├── 回文链表 │ ├── 1.gif │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ └── 4.jpg ├── 子序列 │ ├── 1.gif │ ├── 1.jpg │ ├── 2.gif │ ├── 2.jpg │ └── 3.jpg ├── 子集 │ ├── 1.jpg │ ├── 2.jpg │ └── 3.jpg ├── 字符串乘法 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.gif │ ├── 6.jpg │ └── title.png ├── 密码技术 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ └── 7.jpg ├── 座位调度 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ └── 7.jpg ├── 扔鸡蛋 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ └── dp.png ├── 接雨水 │ ├── 0.jpg │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ └── title.png ├── 数组交换 │ ├── ink-image (1).png │ ├── ink-image (2).png │ ├── ink-image (3).png │ └── ink-image.png ├── 最优子结构 │ └── 1.jpg ├── 最长回文子序列 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ └── 5.jpg ├── 最长递增子序列 │ ├── 1.jpeg │ ├── 2.jpeg │ ├── 3.jpeg │ ├── gif1.gif │ ├── gif2.gif │ ├── poker1.jpeg │ ├── poker2.jpeg │ ├── poker3.jpeg │ ├── poker4.jpeg │ └── title.png ├── 有序数组去重 │ ├── 1.gif │ ├── 2.gif │ └── title.png ├── 栈队列 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ └── 6.jpg ├── 概率问题 │ ├── p.png │ ├── p.svg │ ├── sanmen.png │ ├── sanmen.svg │ └── tree.png ├── 正则 │ ├── example.png │ └── title.png ├── 洗牌算法 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.jpg │ └── 6.png ├── 滑动窗口 │ ├── 0.png │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── title1.png │ ├── title2.png │ └── title3.png ├── 缺失元素 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── title.png │ └── xor.png ├── 股票问题 │ ├── 1.png │ └── title.png └── 设计Twitter │ ├── design.png │ ├── merge.gif │ ├── tweet.jpg │ └── user.jpg ├── 动态规划系列 ├── README.md ├── 动态规划之KMP字符匹配算法.md ├── 动态规划之博弈问题.md ├── 动态规划之四键键盘.md ├── 动态规划之正则表达.md ├── 动态规划设计:最长递增子序列.md ├── 动态规划详解进阶.md ├── 团灭股票问题.md ├── 子序列问题模板.md ├── 抢房子.md ├── 最优子结构.md ├── 最长公共子序列.md ├── 编辑距离.md ├── 贪心算法之区间调度问题.md ├── 高楼扔鸡蛋进阶.md └── 高楼扔鸡蛋问题.md ├── 技术 ├── linuxshell.md ├── linux进程.md ├── redis入侵.md ├── session和cookie.md ├── 在线练习平台.md └── 密码技术.md ├── 数据结构系列 ├── README.md ├── 二叉堆详解实现优先级队列.md ├── 二叉搜索树操作集锦.md ├── 单调栈.md ├── 单调队列.md ├── 实现计算器.md ├── 设计Twitter.md ├── 递归反转链表的一部分.md └── 队列实现栈栈实现队列.md ├── 算法思维系列 ├── FloodFill算法详解及应用.md ├── README.md ├── UnionFind算法应用.md ├── UnionFind算法详解.md ├── twoSum问题的核心思想.md ├── 为什么推荐算法4.md ├── 二分查找详解.md ├── 信封嵌套问题.md ├── 几个反直觉的概率问题.md ├── 前缀和技巧.md ├── 区间交集问题.md ├── 区间调度问题之区间合并.md ├── 双指针技巧.md ├── 回溯算法详解修订版.md ├── 字符串乘法.md ├── 学习数据结构和算法的高效方法.md ├── 常用的位操作.md ├── 洗牌算法.md ├── 滑动窗口技巧.md ├── 烧饼排序.md ├── 算法学习之路.md └── 递归详解.md └── 高频面试系列 ├── LRU算法.md ├── README.md ├── koko偷香蕉.md ├── k个一组反转链表.md ├── 一行代码解决的智力题.md ├── 二分查找判定子序列.md ├── 判断回文链表.md ├── 合法括号判定.md ├── 如何去除有序数组的重复元素.md ├── 子集排列组合.md ├── 座位调度.md ├── 打印素数.md ├── 接雨水.md ├── 最长回文子串.md ├── 水塘抽样.md ├── 消失的元素.md └── 缺失和重复的元素.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 发现问题 3 | about: 我发现了某处链接或者知识点的错误 4 | title: 'bug ' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | 15 | 你好,我发现如下文章有 bug(点击文字可跳转到相关文章): 16 | 17 | [动态规划系列/抢房子.md](https://github.com/labuladong/fucking-algorithm/blob/master/动态规划系列/抢房子.md) 18 | 19 | 问题描述: 20 | 21 | 某章图片链接失效/其中的 XXX 内容有误/等等。 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/others.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 其他issue 3 | about: 我还有一些其他的建议/问题 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/translate.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 参与翻译 3 | about: 我想参与仓库中文章的翻译工作 4 | title: 'translate ' 5 | labels: translate 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 我已阅读过[翻译组工作流程](https://github.com/labuladong/fucking-algorithm/issues/9),我已阅读过[翻译要求](https://github.com/labuladong/fucking-algorithm/blob/english/README.md),我已查看 [已完成列表](https://github.com/labuladong/fucking-algorithm/pulls?q=is%3Apr+is%3Aclosed),确保我要翻译的文章还没有被翻译。 19 | 20 | 我将开始翻译如下文章(点击可查看目标文章): 21 | 22 | 23 | [动态规划系列/抢房子.md](https://github.com/labuladong/fucking-algorithm/blob/master/动态规划系列/抢房子.md) 24 | 25 | 我对如何翻译此文章已经心中有数,我准备将它翻译成:**英文** 26 | 27 | 28 | **预计 3 天内翻译完成**,我会尽可能快地完成翻译,主仓库会对第一个完成的 pull request 添加翻译者昵称/姓名及个人链接。 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_store 2 | -------------------------------------------------------------------------------- /pictures/4keyboard/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/4keyboard/1.jpg -------------------------------------------------------------------------------- /pictures/4keyboard/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/4keyboard/title.png -------------------------------------------------------------------------------- /pictures/BST/BST_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/BST/BST_example.png -------------------------------------------------------------------------------- /pictures/BST/bst_deletion_case_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/BST/bst_deletion_case_1.png -------------------------------------------------------------------------------- /pictures/BST/bst_deletion_case_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/BST/bst_deletion_case_2.png -------------------------------------------------------------------------------- /pictures/BST/bst_deletion_case_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/BST/bst_deletion_case_3.png -------------------------------------------------------------------------------- /pictures/BST/假BST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/BST/假BST.png -------------------------------------------------------------------------------- /pictures/Chrome插件/baidumonkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/baidumonkey.png -------------------------------------------------------------------------------- /pictures/Chrome插件/baidu广告.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/baidu广告.png -------------------------------------------------------------------------------- /pictures/Chrome插件/csdnBlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/csdnBlock.png -------------------------------------------------------------------------------- /pictures/Chrome插件/githubzip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/githubzip.png -------------------------------------------------------------------------------- /pictures/Chrome插件/listen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/listen1.png -------------------------------------------------------------------------------- /pictures/Chrome插件/monkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/monkey.png -------------------------------------------------------------------------------- /pictures/Chrome插件/oneTab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/oneTab.png -------------------------------------------------------------------------------- /pictures/Chrome插件/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/pin.png -------------------------------------------------------------------------------- /pictures/Chrome插件/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/tree.png -------------------------------------------------------------------------------- /pictures/Chrome插件/youhou优化.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/Chrome插件/youhou优化.png -------------------------------------------------------------------------------- /pictures/LCS/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LCS/1.png -------------------------------------------------------------------------------- /pictures/LCS/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LCS/2.png -------------------------------------------------------------------------------- /pictures/LCS/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LCS/3.png -------------------------------------------------------------------------------- /pictures/LCS/dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LCS/dp.png -------------------------------------------------------------------------------- /pictures/LCS/lcs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LCS/lcs.png -------------------------------------------------------------------------------- /pictures/LRU算法/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LRU算法/1.jpg -------------------------------------------------------------------------------- /pictures/LRU算法/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LRU算法/2.jpg -------------------------------------------------------------------------------- /pictures/LRU算法/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LRU算法/3.jpg -------------------------------------------------------------------------------- /pictures/LRU算法/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/LRU算法/4.jpg -------------------------------------------------------------------------------- /pictures/algo4/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/algo4/1.jpg -------------------------------------------------------------------------------- /pictures/algo4/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/algo4/2.jpg -------------------------------------------------------------------------------- /pictures/algo4/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/algo4/3.jpg -------------------------------------------------------------------------------- /pictures/algo4/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/algo4/title.png -------------------------------------------------------------------------------- /pictures/backtrack/ink-image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/ink-image (1).png -------------------------------------------------------------------------------- /pictures/backtrack/ink-image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/ink-image (2).png -------------------------------------------------------------------------------- /pictures/backtrack/ink-image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/ink-image (3).png -------------------------------------------------------------------------------- /pictures/backtrack/ink-image (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/ink-image (4).png -------------------------------------------------------------------------------- /pictures/backtrack/ink-image (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/ink-image (5).png -------------------------------------------------------------------------------- /pictures/backtrack/ink-image (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/ink-image (6).png -------------------------------------------------------------------------------- /pictures/backtrack/ink-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/ink-image.png -------------------------------------------------------------------------------- /pictures/backtrack/nqueens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/nqueens.png -------------------------------------------------------------------------------- /pictures/backtrack/permutation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/permutation.png -------------------------------------------------------------------------------- /pictures/backtrack/代码.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/代码.png -------------------------------------------------------------------------------- /pictures/backtrack/代码1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/代码1.png -------------------------------------------------------------------------------- /pictures/backtrack/代码2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/代码2.png -------------------------------------------------------------------------------- /pictures/backtrack/代码3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/代码3.png -------------------------------------------------------------------------------- /pictures/backtrack/全排列.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtrack/全排列.png -------------------------------------------------------------------------------- /pictures/backtracking/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtracking/1.jpg -------------------------------------------------------------------------------- /pictures/backtracking/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtracking/2.jpg -------------------------------------------------------------------------------- /pictures/backtracking/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtracking/3.jpg -------------------------------------------------------------------------------- /pictures/backtracking/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtracking/4.jpg -------------------------------------------------------------------------------- /pictures/backtracking/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtracking/5.jpg -------------------------------------------------------------------------------- /pictures/backtracking/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtracking/6.jpg -------------------------------------------------------------------------------- /pictures/backtracking/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/backtracking/7.jpg -------------------------------------------------------------------------------- /pictures/calculator/1.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/calculator/1.1.jpg -------------------------------------------------------------------------------- /pictures/calculator/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/calculator/1.jpg -------------------------------------------------------------------------------- /pictures/calculator/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/calculator/2.jpg -------------------------------------------------------------------------------- /pictures/calculator/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/calculator/3.jpg -------------------------------------------------------------------------------- /pictures/calculator/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/calculator/4.jpg -------------------------------------------------------------------------------- /pictures/calculator/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/calculator/5.jpg -------------------------------------------------------------------------------- /pictures/calculator/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/calculator/6.jpg -------------------------------------------------------------------------------- /pictures/dupmissing/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/dupmissing/1.gif -------------------------------------------------------------------------------- /pictures/dupmissing/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/dupmissing/2.jpg -------------------------------------------------------------------------------- /pictures/dupmissing/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/dupmissing/3.jpg -------------------------------------------------------------------------------- /pictures/editDistance/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/1.jpg -------------------------------------------------------------------------------- /pictures/editDistance/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/2.jpg -------------------------------------------------------------------------------- /pictures/editDistance/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/3.jpg -------------------------------------------------------------------------------- /pictures/editDistance/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/4.jpg -------------------------------------------------------------------------------- /pictures/editDistance/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/5.jpg -------------------------------------------------------------------------------- /pictures/editDistance/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/6.jpg -------------------------------------------------------------------------------- /pictures/editDistance/delete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/delete.gif -------------------------------------------------------------------------------- /pictures/editDistance/dp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/dp.jpg -------------------------------------------------------------------------------- /pictures/editDistance/edit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/edit.gif -------------------------------------------------------------------------------- /pictures/editDistance/insert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/insert.gif -------------------------------------------------------------------------------- /pictures/editDistance/replace.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/replace.gif -------------------------------------------------------------------------------- /pictures/editDistance/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/editDistance/title.png -------------------------------------------------------------------------------- /pictures/floodfill/floodfill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/floodfill.gif -------------------------------------------------------------------------------- /pictures/floodfill/floodfill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/floodfill.png -------------------------------------------------------------------------------- /pictures/floodfill/leetcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/leetcode.png -------------------------------------------------------------------------------- /pictures/floodfill/ppt1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/ppt1.PNG -------------------------------------------------------------------------------- /pictures/floodfill/ppt2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/ppt2.PNG -------------------------------------------------------------------------------- /pictures/floodfill/ppt3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/ppt3.PNG -------------------------------------------------------------------------------- /pictures/floodfill/ppt4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/ppt4.PNG -------------------------------------------------------------------------------- /pictures/floodfill/ppt5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/ppt5.PNG -------------------------------------------------------------------------------- /pictures/floodfill/xiaoxiaole.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/xiaoxiaole.jpg -------------------------------------------------------------------------------- /pictures/floodfill/扫雷.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/扫雷.png -------------------------------------------------------------------------------- /pictures/floodfill/抠图.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/抠图.jpeg -------------------------------------------------------------------------------- /pictures/floodfill/抠图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/floodfill/抠图.jpg -------------------------------------------------------------------------------- /pictures/group.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/group.jpg -------------------------------------------------------------------------------- /pictures/heap/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/heap/1.png -------------------------------------------------------------------------------- /pictures/heap/delete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/heap/delete.gif -------------------------------------------------------------------------------- /pictures/heap/insert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/heap/insert.gif -------------------------------------------------------------------------------- /pictures/heap/sink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/heap/sink.gif -------------------------------------------------------------------------------- /pictures/heap/swim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/heap/swim.gif -------------------------------------------------------------------------------- /pictures/intersection/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/intersection/1.jpg -------------------------------------------------------------------------------- /pictures/intersection/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/intersection/2.jpg -------------------------------------------------------------------------------- /pictures/intersection/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/intersection/3.jpg -------------------------------------------------------------------------------- /pictures/intersection/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/intersection/4.gif -------------------------------------------------------------------------------- /pictures/intersection/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/intersection/title.png -------------------------------------------------------------------------------- /pictures/interval/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/interval/1.gif -------------------------------------------------------------------------------- /pictures/interval/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/interval/2.jpg -------------------------------------------------------------------------------- /pictures/interval/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/interval/3.jpg -------------------------------------------------------------------------------- /pictures/interval/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/interval/4.jpg -------------------------------------------------------------------------------- /pictures/interval/title1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/interval/title1.png -------------------------------------------------------------------------------- /pictures/interval/title2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/interval/title2.png -------------------------------------------------------------------------------- /pictures/kgroup/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/1.jpg -------------------------------------------------------------------------------- /pictures/kgroup/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/2.jpg -------------------------------------------------------------------------------- /pictures/kgroup/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/3.jpg -------------------------------------------------------------------------------- /pictures/kgroup/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/4.jpg -------------------------------------------------------------------------------- /pictures/kgroup/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/5.jpg -------------------------------------------------------------------------------- /pictures/kgroup/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/6.jpg -------------------------------------------------------------------------------- /pictures/kgroup/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/7.jpg -------------------------------------------------------------------------------- /pictures/kgroup/8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/8.gif -------------------------------------------------------------------------------- /pictures/kgroup/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kgroup/title.png -------------------------------------------------------------------------------- /pictures/kmp/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/1.gif -------------------------------------------------------------------------------- /pictures/kmp/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/2.gif -------------------------------------------------------------------------------- /pictures/kmp/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/3.gif -------------------------------------------------------------------------------- /pictures/kmp/A.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/A.gif -------------------------------------------------------------------------------- /pictures/kmp/allstate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/allstate.jpg -------------------------------------------------------------------------------- /pictures/kmp/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/back.jpg -------------------------------------------------------------------------------- /pictures/kmp/dfa.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/dfa.gif -------------------------------------------------------------------------------- /pictures/kmp/exp1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/exp1.jpg -------------------------------------------------------------------------------- /pictures/kmp/exp2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/exp2.jpg -------------------------------------------------------------------------------- /pictures/kmp/exp3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/exp3.jpg -------------------------------------------------------------------------------- /pictures/kmp/exp4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/exp4.jpg -------------------------------------------------------------------------------- /pictures/kmp/exp5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/exp5.jpg -------------------------------------------------------------------------------- /pictures/kmp/exp6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/exp6.jpg -------------------------------------------------------------------------------- /pictures/kmp/exp7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/exp7.jpg -------------------------------------------------------------------------------- /pictures/kmp/forward.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/forward.jpg -------------------------------------------------------------------------------- /pictures/kmp/kmp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/kmp.gif -------------------------------------------------------------------------------- /pictures/kmp/shadow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/shadow.jpg -------------------------------------------------------------------------------- /pictures/kmp/shadow1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/shadow1.jpg -------------------------------------------------------------------------------- /pictures/kmp/shadow2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/shadow2.jpg -------------------------------------------------------------------------------- /pictures/kmp/state.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/state.jpg -------------------------------------------------------------------------------- /pictures/kmp/state2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/state2.jpg -------------------------------------------------------------------------------- /pictures/kmp/state4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/state4.jpg -------------------------------------------------------------------------------- /pictures/kmp/txt1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/txt1.jpg -------------------------------------------------------------------------------- /pictures/kmp/txt2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/txt2.jpg -------------------------------------------------------------------------------- /pictures/kmp/txt3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/txt3.jpg -------------------------------------------------------------------------------- /pictures/kmp/txt4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/txt4.jpg -------------------------------------------------------------------------------- /pictures/kmp/z.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/kmp/z.jpg -------------------------------------------------------------------------------- /pictures/labuladong.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/labuladong.jpg -------------------------------------------------------------------------------- /pictures/labuladong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/labuladong.png -------------------------------------------------------------------------------- /pictures/linux-fs/application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/application.png -------------------------------------------------------------------------------- /pictures/linux-fs/apt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/apt.png -------------------------------------------------------------------------------- /pictures/linux-fs/bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/bin.png -------------------------------------------------------------------------------- /pictures/linux-fs/boot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/boot.png -------------------------------------------------------------------------------- /pictures/linux-fs/cpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/cpu.png -------------------------------------------------------------------------------- /pictures/linux-fs/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/desktop.png -------------------------------------------------------------------------------- /pictures/linux-fs/dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/dev.png -------------------------------------------------------------------------------- /pictures/linux-fs/etc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/etc.png -------------------------------------------------------------------------------- /pictures/linux-fs/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/home.png -------------------------------------------------------------------------------- /pictures/linux-fs/linux-filesystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/linux-filesystem.png -------------------------------------------------------------------------------- /pictures/linux-fs/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/log.png -------------------------------------------------------------------------------- /pictures/linux-fs/opt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/opt.png -------------------------------------------------------------------------------- /pictures/linux-fs/proc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/proc.png -------------------------------------------------------------------------------- /pictures/linux-fs/root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/root.png -------------------------------------------------------------------------------- /pictures/linux-fs/sbin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/sbin.png -------------------------------------------------------------------------------- /pictures/linux-fs/tmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/tmp.png -------------------------------------------------------------------------------- /pictures/linux-fs/usr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/usr.png -------------------------------------------------------------------------------- /pictures/linux-fs/usrbin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linux-fs/usrbin.png -------------------------------------------------------------------------------- /pictures/linuxProcess/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/1.jpg -------------------------------------------------------------------------------- /pictures/linuxProcess/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/2.jpg -------------------------------------------------------------------------------- /pictures/linuxProcess/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/3.jpg -------------------------------------------------------------------------------- /pictures/linuxProcess/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/4.jpg -------------------------------------------------------------------------------- /pictures/linuxProcess/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/5.jpg -------------------------------------------------------------------------------- /pictures/linuxProcess/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/6.jpg -------------------------------------------------------------------------------- /pictures/linuxProcess/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/7.jpg -------------------------------------------------------------------------------- /pictures/linuxProcess/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxProcess/8.jpg -------------------------------------------------------------------------------- /pictures/linuxshell/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/linuxshell/1.png -------------------------------------------------------------------------------- /pictures/mergeInterval/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/mergeInterval/1.jpg -------------------------------------------------------------------------------- /pictures/mergeInterval/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/mergeInterval/2.jpg -------------------------------------------------------------------------------- /pictures/mergeInterval/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/mergeInterval/3.gif -------------------------------------------------------------------------------- /pictures/mergeInterval/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/mergeInterval/title.png -------------------------------------------------------------------------------- /pictures/online/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/1.png -------------------------------------------------------------------------------- /pictures/online/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/10.png -------------------------------------------------------------------------------- /pictures/online/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/11.png -------------------------------------------------------------------------------- /pictures/online/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/2.png -------------------------------------------------------------------------------- /pictures/online/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/3.png -------------------------------------------------------------------------------- /pictures/online/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/4.png -------------------------------------------------------------------------------- /pictures/online/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/5.png -------------------------------------------------------------------------------- /pictures/online/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/6.png -------------------------------------------------------------------------------- /pictures/online/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/7.png -------------------------------------------------------------------------------- /pictures/online/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/8.png -------------------------------------------------------------------------------- /pictures/online/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/online/9.png -------------------------------------------------------------------------------- /pictures/others/leetcode.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/others/leetcode.jpeg -------------------------------------------------------------------------------- /pictures/pancakeSort/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/pancakeSort/1.jpg -------------------------------------------------------------------------------- /pictures/pancakeSort/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/pancakeSort/2.png -------------------------------------------------------------------------------- /pictures/pancakeSort/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/pancakeSort/3.jpg -------------------------------------------------------------------------------- /pictures/pancakeSort/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/pancakeSort/4.jpg -------------------------------------------------------------------------------- /pictures/pancakeSort/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/pancakeSort/title.png -------------------------------------------------------------------------------- /pictures/pay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/pay.jpg -------------------------------------------------------------------------------- /pictures/prime/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/prime/1.gif -------------------------------------------------------------------------------- /pictures/qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/qrcode.jpg -------------------------------------------------------------------------------- /pictures/redis入侵/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/redis入侵/1.png -------------------------------------------------------------------------------- /pictures/redis入侵/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/redis入侵/2.png -------------------------------------------------------------------------------- /pictures/redis入侵/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/redis入侵/3.png -------------------------------------------------------------------------------- /pictures/redis入侵/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/redis入侵/4.png -------------------------------------------------------------------------------- /pictures/redis入侵/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/redis入侵/5.png -------------------------------------------------------------------------------- /pictures/redis入侵/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/redis入侵/6.png -------------------------------------------------------------------------------- /pictures/robber/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/robber/1.jpg -------------------------------------------------------------------------------- /pictures/robber/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/robber/2.jpg -------------------------------------------------------------------------------- /pictures/robber/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/robber/3.jpg -------------------------------------------------------------------------------- /pictures/robber/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/robber/title.png -------------------------------------------------------------------------------- /pictures/robber/title1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/robber/title1.png -------------------------------------------------------------------------------- /pictures/session/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/session/1.png -------------------------------------------------------------------------------- /pictures/session/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/session/2.png -------------------------------------------------------------------------------- /pictures/session/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/session/3.png -------------------------------------------------------------------------------- /pictures/session/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/session/4.jpg -------------------------------------------------------------------------------- /pictures/unionfind/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/1.jpg -------------------------------------------------------------------------------- /pictures/unionfind/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/2.jpg -------------------------------------------------------------------------------- /pictures/unionfind/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/3.jpg -------------------------------------------------------------------------------- /pictures/unionfind/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/4.jpg -------------------------------------------------------------------------------- /pictures/unionfind/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/5.jpg -------------------------------------------------------------------------------- /pictures/unionfind/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/6.jpg -------------------------------------------------------------------------------- /pictures/unionfind/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/7.jpg -------------------------------------------------------------------------------- /pictures/unionfind/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/8.jpg -------------------------------------------------------------------------------- /pictures/unionfind/9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind/9.gif -------------------------------------------------------------------------------- /pictures/unionfind应用/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind应用/1.jpg -------------------------------------------------------------------------------- /pictures/unionfind应用/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind应用/2.jpg -------------------------------------------------------------------------------- /pictures/unionfind应用/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/unionfind应用/3.jpg -------------------------------------------------------------------------------- /pictures/youtube/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/youtube/1.png -------------------------------------------------------------------------------- /pictures/youtube/1573133096614.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/youtube/1573133096614.jpeg -------------------------------------------------------------------------------- /pictures/youtube/1573133131308.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/youtube/1573133131308.jpeg -------------------------------------------------------------------------------- /pictures/youtube/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/youtube/2.jpg -------------------------------------------------------------------------------- /pictures/youtube/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/youtube/3.jpg -------------------------------------------------------------------------------- /pictures/youtube/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/youtube/4.jpg -------------------------------------------------------------------------------- /pictures/二分应用/title1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分应用/title1.png -------------------------------------------------------------------------------- /pictures/二分应用/title2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分应用/title2.png -------------------------------------------------------------------------------- /pictures/二分查找/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分查找/1.jpg -------------------------------------------------------------------------------- /pictures/二分查找/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分查找/2.jpg -------------------------------------------------------------------------------- /pictures/二分查找/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分查找/3.jpg -------------------------------------------------------------------------------- /pictures/二分查找/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分查找/4.jpg -------------------------------------------------------------------------------- /pictures/二分查找/binarySearch1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分查找/binarySearch1.png -------------------------------------------------------------------------------- /pictures/二分查找/binarySearch2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分查找/binarySearch2.png -------------------------------------------------------------------------------- /pictures/二分查找/poem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/二分查找/poem.png -------------------------------------------------------------------------------- /pictures/位操作/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/位操作/1.png -------------------------------------------------------------------------------- /pictures/位操作/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/位操作/title.png -------------------------------------------------------------------------------- /pictures/信封嵌套/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/信封嵌套/0.jpg -------------------------------------------------------------------------------- /pictures/信封嵌套/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/信封嵌套/1.jpg -------------------------------------------------------------------------------- /pictures/信封嵌套/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/信封嵌套/2.jpg -------------------------------------------------------------------------------- /pictures/信封嵌套/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/信封嵌套/title.png -------------------------------------------------------------------------------- /pictures/前缀和/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/前缀和/1.jpg -------------------------------------------------------------------------------- /pictures/前缀和/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/前缀和/2.jpg -------------------------------------------------------------------------------- /pictures/前缀和/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/前缀和/title.png -------------------------------------------------------------------------------- /pictures/动态规划详解/coindp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/coindp.png -------------------------------------------------------------------------------- /pictures/动态规划详解/coinfunc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/coinfunc.png -------------------------------------------------------------------------------- /pictures/动态规划详解/cointree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/cointree.png -------------------------------------------------------------------------------- /pictures/动态规划详解/fibdp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/fibdp.png -------------------------------------------------------------------------------- /pictures/动态规划详解/fibfunc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/fibfunc.png -------------------------------------------------------------------------------- /pictures/动态规划详解/fibmemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/fibmemo.png -------------------------------------------------------------------------------- /pictures/动态规划详解/fibtree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/fibtree.png -------------------------------------------------------------------------------- /pictures/动态规划详解/img_20190514_013033.441.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/img_20190514_013033.441.png -------------------------------------------------------------------------------- /pictures/动态规划详解/img_20190514_013830.397.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/img_20190514_013830.397.png -------------------------------------------------------------------------------- /pictures/动态规划详解/ink-image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/ink-image (1).png -------------------------------------------------------------------------------- /pictures/动态规划详解/ink-image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/ink-image (2).png -------------------------------------------------------------------------------- /pictures/动态规划详解/ink-image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/ink-image (3).png -------------------------------------------------------------------------------- /pictures/动态规划详解/ink-image (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/ink-image (4).png -------------------------------------------------------------------------------- /pictures/动态规划详解/ink-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解/ink-image.png -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/1.jpg -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/2.jpg -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/3.jpg -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/4.jpg -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/5.jpg -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/6.jpg -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/coin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/coin.png -------------------------------------------------------------------------------- /pictures/动态规划详解进阶/fib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/动态规划详解进阶/fib.png -------------------------------------------------------------------------------- /pictures/单调栈/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/单调栈/1.png -------------------------------------------------------------------------------- /pictures/单调栈/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/单调栈/2.png -------------------------------------------------------------------------------- /pictures/单调栈/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/单调栈/3.png -------------------------------------------------------------------------------- /pictures/单调队列/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/单调队列/1.png -------------------------------------------------------------------------------- /pictures/单调队列/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/单调队列/2.png -------------------------------------------------------------------------------- /pictures/单调队列/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/单调队列/3.png -------------------------------------------------------------------------------- /pictures/单调队列/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/单调队列/title.png -------------------------------------------------------------------------------- /pictures/博弈问题/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/博弈问题/1.png -------------------------------------------------------------------------------- /pictures/博弈问题/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/博弈问题/2.png -------------------------------------------------------------------------------- /pictures/博弈问题/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/博弈问题/3.png -------------------------------------------------------------------------------- /pictures/博弈问题/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/博弈问题/4.png -------------------------------------------------------------------------------- /pictures/双指针/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/双指针/1.png -------------------------------------------------------------------------------- /pictures/双指针/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/双指针/2.png -------------------------------------------------------------------------------- /pictures/双指针/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/双指针/3.png -------------------------------------------------------------------------------- /pictures/双指针/center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/双指针/center.png -------------------------------------------------------------------------------- /pictures/双指针/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/双指针/title.png -------------------------------------------------------------------------------- /pictures/反转链表/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/1.jpg -------------------------------------------------------------------------------- /pictures/反转链表/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/2.jpg -------------------------------------------------------------------------------- /pictures/反转链表/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/3.jpg -------------------------------------------------------------------------------- /pictures/反转链表/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/4.jpg -------------------------------------------------------------------------------- /pictures/反转链表/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/5.jpg -------------------------------------------------------------------------------- /pictures/反转链表/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/6.jpg -------------------------------------------------------------------------------- /pictures/反转链表/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/7.jpg -------------------------------------------------------------------------------- /pictures/反转链表/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/反转链表/title.png -------------------------------------------------------------------------------- /pictures/回文/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/回文/title.png -------------------------------------------------------------------------------- /pictures/回文链表/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/回文链表/1.gif -------------------------------------------------------------------------------- /pictures/回文链表/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/回文链表/1.jpg -------------------------------------------------------------------------------- /pictures/回文链表/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/回文链表/2.jpg -------------------------------------------------------------------------------- /pictures/回文链表/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/回文链表/3.jpg -------------------------------------------------------------------------------- /pictures/回文链表/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/回文链表/4.jpg -------------------------------------------------------------------------------- /pictures/子序列/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子序列/1.gif -------------------------------------------------------------------------------- /pictures/子序列/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子序列/1.jpg -------------------------------------------------------------------------------- /pictures/子序列/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子序列/2.gif -------------------------------------------------------------------------------- /pictures/子序列/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子序列/2.jpg -------------------------------------------------------------------------------- /pictures/子序列/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子序列/3.jpg -------------------------------------------------------------------------------- /pictures/子集/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子集/1.jpg -------------------------------------------------------------------------------- /pictures/子集/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子集/2.jpg -------------------------------------------------------------------------------- /pictures/子集/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/子集/3.jpg -------------------------------------------------------------------------------- /pictures/字符串乘法/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/字符串乘法/1.jpg -------------------------------------------------------------------------------- /pictures/字符串乘法/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/字符串乘法/2.jpg -------------------------------------------------------------------------------- /pictures/字符串乘法/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/字符串乘法/3.jpg -------------------------------------------------------------------------------- /pictures/字符串乘法/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/字符串乘法/4.gif -------------------------------------------------------------------------------- /pictures/字符串乘法/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/字符串乘法/6.jpg -------------------------------------------------------------------------------- /pictures/字符串乘法/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/字符串乘法/title.png -------------------------------------------------------------------------------- /pictures/密码技术/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/密码技术/1.jpg -------------------------------------------------------------------------------- /pictures/密码技术/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/密码技术/2.jpg -------------------------------------------------------------------------------- /pictures/密码技术/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/密码技术/3.jpg -------------------------------------------------------------------------------- /pictures/密码技术/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/密码技术/4.jpg -------------------------------------------------------------------------------- /pictures/密码技术/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/密码技术/5.jpg -------------------------------------------------------------------------------- /pictures/密码技术/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/密码技术/6.jpg -------------------------------------------------------------------------------- /pictures/密码技术/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/密码技术/7.jpg -------------------------------------------------------------------------------- /pictures/座位调度/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/座位调度/1.jpg -------------------------------------------------------------------------------- /pictures/座位调度/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/座位调度/2.jpg -------------------------------------------------------------------------------- /pictures/座位调度/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/座位调度/3.jpg -------------------------------------------------------------------------------- /pictures/座位调度/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/座位调度/4.jpg -------------------------------------------------------------------------------- /pictures/座位调度/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/座位调度/5.jpg -------------------------------------------------------------------------------- /pictures/座位调度/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/座位调度/6.jpg -------------------------------------------------------------------------------- /pictures/座位调度/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/座位调度/7.jpg -------------------------------------------------------------------------------- /pictures/扔鸡蛋/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/扔鸡蛋/1.jpg -------------------------------------------------------------------------------- /pictures/扔鸡蛋/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/扔鸡蛋/2.jpg -------------------------------------------------------------------------------- /pictures/扔鸡蛋/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/扔鸡蛋/3.jpg -------------------------------------------------------------------------------- /pictures/扔鸡蛋/dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/扔鸡蛋/dp.png -------------------------------------------------------------------------------- /pictures/接雨水/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/接雨水/0.jpg -------------------------------------------------------------------------------- /pictures/接雨水/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/接雨水/1.jpg -------------------------------------------------------------------------------- /pictures/接雨水/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/接雨水/2.jpg -------------------------------------------------------------------------------- /pictures/接雨水/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/接雨水/3.jpg -------------------------------------------------------------------------------- /pictures/接雨水/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/接雨水/4.jpg -------------------------------------------------------------------------------- /pictures/接雨水/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/接雨水/5.jpg -------------------------------------------------------------------------------- /pictures/接雨水/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/接雨水/title.png -------------------------------------------------------------------------------- /pictures/数组交换/ink-image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/数组交换/ink-image (1).png -------------------------------------------------------------------------------- /pictures/数组交换/ink-image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/数组交换/ink-image (2).png -------------------------------------------------------------------------------- /pictures/数组交换/ink-image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/数组交换/ink-image (3).png -------------------------------------------------------------------------------- /pictures/数组交换/ink-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/数组交换/ink-image.png -------------------------------------------------------------------------------- /pictures/最优子结构/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最优子结构/1.jpg -------------------------------------------------------------------------------- /pictures/最长回文子序列/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长回文子序列/1.jpg -------------------------------------------------------------------------------- /pictures/最长回文子序列/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长回文子序列/2.jpg -------------------------------------------------------------------------------- /pictures/最长回文子序列/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长回文子序列/3.jpg -------------------------------------------------------------------------------- /pictures/最长回文子序列/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长回文子序列/4.jpg -------------------------------------------------------------------------------- /pictures/最长回文子序列/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长回文子序列/5.jpg -------------------------------------------------------------------------------- /pictures/最长递增子序列/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/1.jpeg -------------------------------------------------------------------------------- /pictures/最长递增子序列/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/2.jpeg -------------------------------------------------------------------------------- /pictures/最长递增子序列/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/3.jpeg -------------------------------------------------------------------------------- /pictures/最长递增子序列/gif1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/gif1.gif -------------------------------------------------------------------------------- /pictures/最长递增子序列/gif2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/gif2.gif -------------------------------------------------------------------------------- /pictures/最长递增子序列/poker1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/poker1.jpeg -------------------------------------------------------------------------------- /pictures/最长递增子序列/poker2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/poker2.jpeg -------------------------------------------------------------------------------- /pictures/最长递增子序列/poker3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/poker3.jpeg -------------------------------------------------------------------------------- /pictures/最长递增子序列/poker4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/poker4.jpeg -------------------------------------------------------------------------------- /pictures/最长递增子序列/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/最长递增子序列/title.png -------------------------------------------------------------------------------- /pictures/有序数组去重/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/有序数组去重/1.gif -------------------------------------------------------------------------------- /pictures/有序数组去重/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/有序数组去重/2.gif -------------------------------------------------------------------------------- /pictures/有序数组去重/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/有序数组去重/title.png -------------------------------------------------------------------------------- /pictures/栈队列/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/栈队列/1.jpg -------------------------------------------------------------------------------- /pictures/栈队列/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/栈队列/2.jpg -------------------------------------------------------------------------------- /pictures/栈队列/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/栈队列/3.jpg -------------------------------------------------------------------------------- /pictures/栈队列/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/栈队列/4.jpg -------------------------------------------------------------------------------- /pictures/栈队列/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/栈队列/5.jpg -------------------------------------------------------------------------------- /pictures/栈队列/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/栈队列/6.jpg -------------------------------------------------------------------------------- /pictures/概率问题/p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/概率问题/p.png -------------------------------------------------------------------------------- /pictures/概率问题/sanmen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/概率问题/sanmen.png -------------------------------------------------------------------------------- /pictures/概率问题/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/概率问题/tree.png -------------------------------------------------------------------------------- /pictures/正则/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/正则/example.png -------------------------------------------------------------------------------- /pictures/正则/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/正则/title.png -------------------------------------------------------------------------------- /pictures/洗牌算法/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/洗牌算法/1.png -------------------------------------------------------------------------------- /pictures/洗牌算法/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/洗牌算法/2.png -------------------------------------------------------------------------------- /pictures/洗牌算法/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/洗牌算法/3.png -------------------------------------------------------------------------------- /pictures/洗牌算法/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/洗牌算法/4.png -------------------------------------------------------------------------------- /pictures/洗牌算法/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/洗牌算法/5.jpg -------------------------------------------------------------------------------- /pictures/洗牌算法/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/洗牌算法/6.png -------------------------------------------------------------------------------- /pictures/滑动窗口/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/滑动窗口/0.png -------------------------------------------------------------------------------- /pictures/滑动窗口/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/滑动窗口/1.png -------------------------------------------------------------------------------- /pictures/滑动窗口/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/滑动窗口/2.png -------------------------------------------------------------------------------- /pictures/滑动窗口/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/滑动窗口/3.png -------------------------------------------------------------------------------- /pictures/滑动窗口/title1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/滑动窗口/title1.png -------------------------------------------------------------------------------- /pictures/滑动窗口/title2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/滑动窗口/title2.png -------------------------------------------------------------------------------- /pictures/滑动窗口/title3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/滑动窗口/title3.png -------------------------------------------------------------------------------- /pictures/缺失元素/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/缺失元素/1.jpg -------------------------------------------------------------------------------- /pictures/缺失元素/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/缺失元素/2.jpg -------------------------------------------------------------------------------- /pictures/缺失元素/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/缺失元素/3.jpg -------------------------------------------------------------------------------- /pictures/缺失元素/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/缺失元素/title.png -------------------------------------------------------------------------------- /pictures/缺失元素/xor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/缺失元素/xor.png -------------------------------------------------------------------------------- /pictures/股票问题/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/股票问题/1.png -------------------------------------------------------------------------------- /pictures/股票问题/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/股票问题/title.png -------------------------------------------------------------------------------- /pictures/设计Twitter/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/设计Twitter/design.png -------------------------------------------------------------------------------- /pictures/设计Twitter/merge.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/设计Twitter/merge.gif -------------------------------------------------------------------------------- /pictures/设计Twitter/tweet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/设计Twitter/tweet.jpg -------------------------------------------------------------------------------- /pictures/设计Twitter/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfrued/fucking-algorithm/1c82f0135cff127df638c00083879f0b9e2aecae/pictures/设计Twitter/user.jpg -------------------------------------------------------------------------------- /动态规划系列/README.md: -------------------------------------------------------------------------------- 1 | # 动态规划系列 2 | 3 | 我们公众号最火的就是动态规划系列的文章,也许是动态规划问题有难度而且有意思,也许因为它是面试常考题型。不管你之前是否害怕动态规划系列的问题,相信这一章的内容足以帮助你消除对动态规划算法的恐惧。 4 | 5 | 具体来说,动态规划的一般流程就是三步:**暴力的递归解法 -> 带备忘录的递归解法 -> 迭代的动态规划解法**。 6 | 7 | 就思考流程来说,就分为一下几步:**找到状态和选择 -> 明确 dp 数组/函数的定义 -> 寻找状态之间的关系**。 8 | 9 | 这就是思维模式的框架,**本章都会按照以上的模式来解决问题,辅助读者养成这种模式思维**,有了方向遇到问题就不会抓瞎,足以解决一般的动态规划问题。 10 | 11 | 欢迎关注我的公众号 labuladong,方便获得最新的优质文章: 12 | 13 | ![labuladong二维码](../pictures/qrcode.jpg) -------------------------------------------------------------------------------- /动态规划系列/子序列问题模板.md: -------------------------------------------------------------------------------- 1 | # 动态规划之子序列问题解题模板 2 | 3 | 子序列问题是常见的算法问题,而且并不好解决。 4 | 5 | 首先,子序列问题本身就相对子串、子数组更困难一些,因为前者是不连续的序列,而后两者是连续的,就算穷举你都不一定会,更别说求解相关的算法问题了。 6 | 7 | 而且,子序列问题很可能涉及到两个字符串,比如前文「最长公共子序列」,如果没有一定的处理经验,真的不容易想出来。所以本文就来扒一扒子序列问题的套路,其实就有两种模板,相关问题只要往这两种思路上想,十拿九稳。 8 | 9 | 一般来说,这类问题都是让你求一个**最长子序列**,因为最短子序列就是一个字符嘛,没啥可问的。一旦涉及到子序列和最值,那几乎可以肯定,**考察的是动态规划技巧,时间复杂度一般都是 O(n^2)**。 10 | 11 | 原因很简单,你想想一个字符串,它的子序列有多少种可能?起码是指数级的吧,这种情况下,不用动态规划技巧,还想怎么着? 12 | 13 | 既然要用动态规划,那就要定义 dp 数组,找状态转移关系。我们说的两种思路模板,就是 dp 数组的定义思路。不同的问题可能需要不同的 dp 数组定义来解决。 14 | 15 | ### 一、两种思路 16 | 17 | **1、第一种思路模板是一个一维的 dp 数组**: 18 | 19 | ```java 20 | int n = array.length; 21 | int[] dp = new int[n]; 22 | 23 | for (int i = 1; i < n; i++) { 24 | for (int j = 0; j < i; j++) { 25 | dp[i] = 最值(dp[i], dp[j] + ...) 26 | } 27 | } 28 | ``` 29 | 30 | 举个我们写过的例子「最长递增子序列」,在这个思路中 dp 数组的定义是: 31 | 32 | **在子数组 `array[0..i]` 中,我们要求的子序列(最长递增子序列)的长度是 `dp[i]`**。 33 | 34 | 为啥最长递增子序列需要这种思路呢?前文说得很清楚了,因为这样符合归纳法,可以找到状态转移的关系,这里就不具体展开了。 35 | 36 | **2、第二种思路模板是一个二维的 dp 数组**: 37 | 38 | ```java 39 | int n = arr.length; 40 | int[][] dp = new dp[n][n]; 41 | 42 | for (int i = 0; i < n; i++) { 43 | for (int j = 0; j < n; j++) { 44 | if (arr[i] == arr[j]) 45 | dp[i][j] = dp[i][j] + ... 46 | else 47 | dp[i][j] = 最值(...) 48 | } 49 | } 50 | ``` 51 | 52 | 这种思路运用相对更多一些,尤其是涉及两个字符串/数组的子序列,比如前文讲的「最长公共子序列」。本思路中 dp 数组含义又分为「只涉及一个字符串」和「涉及两个字符串」两种情况。 53 | 54 | **2.1 涉及两个字符串/数组时**(比如最长公共子序列),dp 数组的含义如下: 55 | 56 | **在子数组 `arr1[0..i]` 和子数组 `arr2[0..j]` 中,我们要求的子序列(最长公共子序列)长度为 `dp[i][j]`**。 57 | 58 | **2.2 只涉及一个字符串/数组时**(比如本文要讲的最长回文子序列),dp 数组的含义如下: 59 | 60 | **在子数组 `array[i..j]` 中,我们要求的子序列(最长回文子序列)的长度为 `dp[i][j]`**。 61 | 62 | 第一种情况可以参考这两篇旧文:「编辑距离」「公共子序列」 63 | 64 | 下面就借最长回文子序列这个问题,详解一下第二种情况下如何使用动态规划。 65 | 66 | ### 二、最长回文子序列 67 | 68 | 之前解决了「最长回文子串」的问题,这次提升难度,求最长回文子序列的长度: 69 | 70 | ![](../pictures/最长回文子序列/1.jpg) 71 | 72 | 我们说这个问题对 dp 数组的定义是:**在子串 `s[i..j]` 中,最长回文子序列的长度为 `dp[i][j]`**。一定要记住这个定义才能理解算法。 73 | 74 | 为啥这个问题要这样定义二维的 dp 数组呢?我们前文多次提到,**找状态转移需要归纳思维,说白了就是如何从已知的结果推出未知的部分**,这样定义容易归纳,容易发现状态转移关系。 75 | 76 | 具体来说,如果我们想求 `dp[i][j]`,假设你知道了子问题 `dp[i+1][j-1]` 的结果(`s[i+1..j-1]` 中最长回文子序列的长度),你是否能想办法算出 `dp[i][j]` 的值(`s[i..j]` 中,最长回文子序列的长度)呢? 77 | 78 | ![](../pictures/最长回文子序列/1.jpg) 79 | 80 | 可以!这取决于 `s[i]` 和 `s[j]` 的字符: 81 | 82 | **如果它俩相等**,那么它俩加上 `s[i+1..j-1]` 中的最长回文子序列就是 `s[i..j]` 的最长回文子序列: 83 | 84 | ![](../pictures/最长回文子序列/2.jpg) 85 | 86 | **如果它俩不相等**,说明它俩**不可能同时**出现在 `s[i..j]` 的最长回文子序列中,那么把它俩**分别**加入 `s[i+1..j-1]` 中,看看哪个子串产生的回文子序列更长即可: 87 | 88 | ![](../pictures/最长回文子序列/3.jpg) 89 | 90 | 以上两种情况写成代码就是这样: 91 | 92 | ```java 93 | if (s[i] == s[j]) 94 | // 它俩一定在最长回文子序列中 95 | dp[i][j] = dp[i + 1][j - 1] + 2; 96 | else 97 | // s[i+1..j] 和 s[i..j-1] 谁的回文子序列更长? 98 | dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 99 | ``` 100 | 101 | 至此,状态转移方程就写出来了,根据 dp 数组的定义,我们要求的就是 `dp[0][n - 1]`,也就是整个 `s` 的最长回文子序列的长度。 102 | 103 | ### 三、代码实现 104 | 105 | 首先明确一下 base case,如果只有一个字符,显然最长回文子序列长度是 1,也就是 `dp[i][j] = 1 (i == j)`。 106 | 107 | 因为 `i` 肯定小于等于 `j`,所以对于那些 `i > j` 的位置,根本不存在什么子序列,应该初始化为 0。 108 | 109 | 另外,看看刚才写的状态转移方程,想求 `dp[i][j]` 需要知道 `dp[i+1][j-1]`,`dp[i+1][j]`,`dp[i][j-1]` 这三个位置;再看看我们确定的 base case,填入 dp 数组之后是这样: 110 | 111 | ![](../pictures/最长回文子序列/4.jpg) 112 | 113 | **为了保证每次计算 `dp[i][j]`,左下右方向的位置已经被计算出来,只能斜着遍历或者反着遍历**: 114 | 115 | ![](../pictures/最长回文子序列/5.jpg) 116 | 117 | 我选择反着遍历,代码如下: 118 | 119 | ```cpp 120 | int longestPalindromeSubseq(string s) { 121 | int n = s.size(); 122 | // dp 数组全部初始化为 0 123 | vector> dp(n, vector(n, 0)); 124 | // base case 125 | for (int i = 0; i < n; i++) 126 | dp[i][i] = 1; 127 | // 反着遍历保证正确的状态转移 128 | for (int i = n - 1; i >= 0; i--) { 129 | for (int j = i + 1; j < n; j++) { 130 | // 状态转移方程 131 | if (s[i] == s[j]) 132 | dp[i][j] = dp[i + 1][j - 1] + 2; 133 | else 134 | dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 135 | } 136 | } 137 | // 整个 s 的最长回文子串长度 138 | return dp[0][n - 1]; 139 | } 140 | ``` 141 | 142 | 至此,最长回文子序列的问题就解决了。 143 | 144 | **致力于把算法讲清楚!欢迎关注我的微信公众号 labuladong,查看更多通俗易懂的文章**: 145 | 146 | ![labuladong](../pictures/labuladong.png) 147 | 148 | [上一篇:经典动态规划问题:高楼扔鸡蛋(进阶)](../动态规划系列/高楼扔鸡蛋进阶.md) 149 | 150 | [下一篇:动态规划之博弈问题](../动态规划系列/动态规划之博弈问题.md) 151 | 152 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /动态规划系列/最优子结构.md: -------------------------------------------------------------------------------- 1 | # 动态规划答疑篇 2 | 3 | 这篇文章就给你讲明白两个问题: 4 | 5 | 1、到底什么才叫「最优子结构」,和动态规划什么关系。 6 | 7 | 2、为什么动态规划遍历 `dp` 数组的方式五花八门,有的正着遍历,有的倒着遍历,有的斜着遍历。 8 | 9 | ### 一、最优子结构详解 10 | 11 | 「最优子结构」是某些问题的一种特定性质,并不是动态规划问题专有的。也就是说,很多问题其实都具有最优子结构,只是其中大部分不具有重叠子问题,所以我们不把它们归为动态规划系列问题而已。 12 | 13 | 我先举个很容易理解的例子:假设你们学校有 10 个班,你已经计算出了每个班的最高考试成绩。那么现在我要求你计算全校最高的成绩,你会不会算?当然会,而且你不用重新遍历全校学生的分数进行比较,而是只要在这 10 个最高成绩中取最大的就是全校的最高成绩。 14 | 15 | 我给你提出的这个问题就**符合最优子结构**:可以从子问题的最优结果推出更大规模问题的最优结果。让你算**每个班**的最优成绩就是子问题,你知道所有子问题的答案后,就可以借此推出**全校**学生的最优成绩这个规模更大的问题的答案。 16 | 17 | 你看,这么简单的问题都有最优子结构性质,只是因为显然没有重叠子问题,所以我们简单地求最值肯定用不出动态规划。 18 | 19 | 再举个例子:假设你们学校有 10 个班,你已知每个班的最大分数差(最高分和最低分的差值)。那么现在我让你计算全校学生中的最大分数差,你会不会算?可以想办法算,但是肯定不能通过已知的这 10 个班的最大分数差推到出来。因为这 10 个班的最大分数差不一定就包含全校学生的最大分数差,比如全校的最大分数差可能是 3 班的最高分和 6 班的最低分之差。 20 | 21 | 这次我给你提出的问题就**不符合最优子结构**,因为你没办通过每个班的最优值推出全校的最优值,没办法通过子问题的最优值推出规模更大的问题的最优值。前文「动态规划详解」说过,想满足最优子结,子问题之间必须互相独立。全校的最大分数差可能出现在两个班之间,显然子问题不独立,所以这个问题本身不符合最优子结构。 22 | 23 | **那么遇到这种最优子结构失效情况,怎么办?策略是:改造问题**。对于最大分数差这个问题,我们不是没办法利用已知的每个班的分数差吗,那我只能这样写一段暴力代码: 24 | 25 | ```java 26 | int result = 0; 27 | for (Student a : school) { 28 | for (Student b : school) { 29 | if (a is b) continue; 30 | result = max(result, |a.score - b.score|); 31 | } 32 | } 33 | return result; 34 | ``` 35 | 36 | 改造问题,也就是把问题等价转化:最大分数差,不就等价于最高分数和最低分数的差么,那不就是要求最高和最低分数么,不就是我们讨论的第一个问题么,不就具有最优子结构了么?那现在改变思路,借助最优子结构解决最值问题,再回过头解决最大分数差问题,是不是就高效多了? 37 | 38 | 当然,上面这个例子太简单了,不过请读者回顾一下,我们做动态规划问题,是不是一直在求各种最值,本质跟我们举的例子没啥区别,无非需要处理一下重叠子问题。 39 | 40 | 前文「不同定义不同解法」和「高楼扔鸡蛋进阶」就展示了如何改造问题,不同的最优子结构,可能导致不同的解法和效率。 41 | 42 | 再举个常见但也十分简单的例子,求一棵二叉树的最大值,不难吧(简单起见,假设节点中的值都是非负数): 43 | 44 | ```java 45 | int maxVal(TreeNode root) { 46 | if (root == null) 47 | return -1; 48 | int left = maxVal(root.left); 49 | int right = maxVal(root.right); 50 | return max(root.val, left, right); 51 | } 52 | ``` 53 | 54 | 你看这个问题也符合最优子结构,以 `root` 为根的树的最大值,可以通过两边子树(子问题)的最大值推导出来,结合刚才学校和班级的例子,很容易理解吧。 55 | 56 | 当然这也不是动态规划问题,旨在说明,最优子结构并不是动态规划独有的一种性质,能求最值的问题大部分都具有这个性质;**但反过来,最优子结构性质作为动态规划问题的必要条件,一定是让你求最值的**,以后碰到那种恶心人的最值题,思路往动态规划想就对了,这就是套路。 57 | 58 | 动态规划不就是从最简单的 base case 往后推导吗,可以想象成一个链式反应,以小博大。但只有符合最优子结构的问题,才有发生这种链式反应的性质。 59 | 60 | 找最优子结构的过程,其实就是证明状态转移方程正确性的过程,方程符合最优子结构就可以写暴力解了,写出暴力解就可以看出有没有重叠子问题了,有则优化,无则 OK。这也是套路,经常刷题的朋友应该能体会。 61 | 62 | 这里就不举那些正宗动态规划的例子了,读者可以翻翻历史文章,看看状态转移是如何遵循最优子结构的,这个话题就聊到这,下面再来看另外个动态规划迷惑行为。 63 | 64 | ### 二、dp 数组的遍历方向 65 | 66 | 我相信读者做动态规问题时,肯定会对 `dp` 数组的遍历顺序有些头疼。我们拿二维 `dp` 数组来举例,有时候我们是正向遍历: 67 | 68 | ```java 69 | int[][] dp = new int[m][n]; 70 | for (int i = 0; i < m; i++) 71 | for (int j = 0; j < n; j++) 72 | // 计算 dp[i][j] 73 | ``` 74 | 75 | 有时候我们反向遍历: 76 | 77 | ```java 78 | for (int i = m - 1; i >= 0; i--) 79 | for (int j = n - 1; j >= 0; j--) 80 | // 计算 dp[i][j] 81 | ``` 82 | 83 | 有时候可能会斜向遍历: 84 | 85 | ```java 86 | // 斜着遍历数组 87 | for (int l = 2; l <= n; l++) { 88 | for (int i = 0; i <= n - l; i++) { 89 | int j = l + i - 1; 90 | // 计算 dp[i][j] 91 | } 92 | } 93 | ``` 94 | 95 | 甚至更让人迷惑的是,有时候发现正向反向遍历都可以得到正确答案,比如我们在「团灭股票问题」中有的地方就正反皆可。 96 | 97 | 那么,如果仔细观察的话可以发现其中的原因的。你只要把住两点就行了: 98 | 99 | **1、遍历的过程中,所需的状态必须是已经计算出来的**。 100 | 101 | **2、遍历的终点必须是存储结果的那个位置**。 102 | 103 | 下面来距离解释上面两个原则是什么意思。 104 | 105 | 比如编辑距离这个经典的问题,详解见前文「编辑距离详解」,我们通过对 `dp` 数组的定义,确定了 base case 是 `dp[..][0]` 和 `dp[0][..]`,最终答案是 `dp[m][n]`;而且我们通过状态转移方程知道 `dp[i][j]` 需要从 `dp[i-1][j]`, `dp[i][j-1]`, `dp[i-1][j-1]` 转移而来,如下图: 106 | 107 | ![](../pictures/最优子结构/1.jpg) 108 | 109 | 那么,参考刚才说的两条原则,你该怎么遍历 `dp` 数组?肯定是正向遍历: 110 | 111 | ```java 112 | for (int i = 1; i < m; i++) 113 | for (int j = 1; j < n; j++) 114 | // 通过 dp[i-1][j], dp[i][j - 1], dp[i-1][j-1] 115 | // 计算 dp[i][j] 116 | ``` 117 | 118 | 因为,这样每一步迭代的左边、上边、左上边的位置都是 base case 或者之前计算过的,而且最终结束在我们想要的答案 `dp[m][n]`。 119 | 120 | 再举一例,回文子序列问题,详见前文「子序列问题模板」,我们通过过对 `dp` 数组的定义,确定了 base case 处在中间的对角线,`dp[i][j]` 需要从 `dp[i+1][j]`, `dp[i][j-1]`, `dp[i+1][j-1]` 转移而来,想要求的最终答案是 `dp[0][n-1]`,如下图: 121 | 122 | ![](../pictures/最长回文子序列/4.jpg) 123 | 124 | 这种情况根据刚才的两个原则,就可以有两种正确的遍历方式: 125 | 126 | ![](../pictures/最长回文子序列/5.jpg) 127 | 128 | 要么从左至右斜着遍历,要么从下向上从左到右遍历,这样才能保证每次 `dp[i][j]` 的左边、下边、左下边已经计算完毕,得到正确结果。 129 | 130 | 现在,你应该理解了这两个原则,主要就是看 base case 和最终结果的存储位置,保证遍历过程中使用的数据都是计算完毕的就行,有时候确实存在多种方法可以得到正确答案,可根据个人口味自行选择。 131 | 132 | **致力于把算法讲清楚!欢迎关注我的微信公众号 labuladong,查看更多通俗易懂的文章**: 133 | 134 | ![labuladong](../pictures/labuladong.png) 135 | 136 | [上一篇:动态规划解题框架](../动态规划系列/动态规划详解进阶.md) 137 | 138 | [下一篇:回溯算法解题框架](../算法思维系列/回溯算法详解修订版.md) 139 | 140 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /动态规划系列/最长公共子序列.md: -------------------------------------------------------------------------------- 1 | # 最长公共子序列 2 | 3 | 最长公共子序列(Longest Common Subsequence,简称 LCS)是一道非常经典的面试题目,因为它的解法是典型的二维动态规划,大部分比较困难的字符串问题都和这个问题一个套路,比如说编辑距离。而且,这个算法稍加改造就可以用于解决其他问题,所以说 LCS 算法是值得掌握的。 4 | 5 | 题目就是让我们求两个字符串的 LCS 长度: 6 | 7 | ``` 8 | 输入: str1 = "abcde", str2 = "ace" 9 | 输出: 3 10 | 解释: 最长公共子序列是 "ace",它的长度是 3 11 | ``` 12 | 13 | 肯定有读者会问,为啥这个问题就是动态规划来解决呢?因为子序列类型的问题,穷举出所有可能的结果都不容易,而动态规划算法做的就是穷举 + 剪枝,它俩天生一对儿。所以可以说只要涉及子序列问题,十有八九都需要动态规划来解决,往这方面考虑就对了。 14 | 15 | 下面就来手把手分析一下,这道题目如何用动态规划技巧解决。 16 | 17 | ### 一、动态规划思路 18 | 19 | **第一步,一定要明确 `dp` 数组的含义**。对于两个字符串的动态规划问题,套路是通用的。 20 | 21 | 比如说对于字符串 `s1` 和 `s2`,一般来说都要构造一个这样的 DP table: 22 | 23 | ![](../pictures/LCS/dp.png) 24 | 25 | 为了方便理解此表,我们暂时认为索引是从 1 开始的,待会的代码中只要稍作调整即可。其中,`dp[i][j]` 的含义是:对于 `s1[1..i]` 和 `s2[1..j]`,它们的 LCS 长度是 `dp[i][j]`。 26 | 27 | 比如上图的例子,d[2][4] 的含义就是:对于 `"ac"` 和 `"babc"`,它们的 LCS 长度是 2。我们最终想得到的答案应该是 `dp[3][6]`。 28 | 29 | **第二步,定义 base case。** 30 | 31 | 我们专门让索引为 0 的行和列表示空串,`dp[0][..]` 和 `dp[..][0]` 都应该初始化为 0,这就是 base case。 32 | 33 | 比如说,按照刚才 dp 数组的定义,`dp[0][3]=0` 的含义是:对于字符串 `""` 和 `"bab"`,其 LCS 的长度为 0。因为有一个字符串是空串,它们的最长公共子序列的长度显然应该是 0。 34 | 35 | **第三步,找状态转移方程。** 36 | 37 | 这是动态规划最难的一步,不过好在这种字符串问题的套路都差不多,权且借这道题来聊聊处理这类问题的思路。 38 | 39 | 状态转移说简单些就是做选择,比如说这个问题,是求 `s1` 和 `s2` 的最长公共子序列,不妨称这个子序列为 `lcs`。那么对于 `s1` 和 `s2` 中的每个字符,有什么选择?很简单,两种选择,要么在 `lcs` 中,要么不在。 40 | 41 | ![](../pictures/LCS/lcs.png) 42 | 43 | 这个「在」和「不在」就是选择,关键是,应该如何选择呢?这个需要动点脑筋:如果某个字符应该在 `lcs` 中,那么这个字符肯定同时存在于 `s1` 和 `s2` 中,因为 `lcs` 是最长**公共**子序列嘛。所以本题的思路是这样: 44 | 45 | 用两个指针 `i` 和 `j` 从后往前遍历 `s1` 和 `s2`,如果 `s1[i]==s2[j]`,那么这个字符**一定在 `lcs` 中**;否则的话,`s1[i]` 和 `s2[j]` 这两个字符**至少有一个不在 `lcs` 中**,需要丢弃一个。先看一下递归解法,比较容易理解: 46 | 47 | ```python 48 | def longestCommonSubsequence(str1, str2) -> int: 49 | def dp(i, j): 50 | # 空串的 base case 51 | if i == -1 or j == -1: 52 | return 0 53 | if str1[i] == str2[j]: 54 | # 这边找到一个 lcs 的元素,继续往前找 55 | return dp(i - 1, j - 1) + 1 56 | else: 57 | # 谁能让 lcs 最长,就听谁的 58 | return max(dp(i-1, j), dp(i, j-1)) 59 | 60 | # i 和 j 初始化为最后一个索引 61 | return dp(len(str1)-1, len(str2)-1) 62 | ``` 63 | 64 | 对于第一种情况,找到一个 `lcs` 中的字符,同时将 `i` `j` 向前移动一位,并给 `lcs` 的长度加一;对于后者,则尝试两种情况,取更大的结果。 65 | 66 | 其实这段代码就是暴力解法,我们可以通过备忘录或者 DP table 来优化时间复杂度,比如通过前文描述的 DP table 来解决: 67 | 68 | ```python 69 | def longestCommonSubsequence(str1, str2) -> int: 70 | m, n = len(str1), len(str2) 71 | # 构建 DP table 和 base case 72 | dp = [[0] * (n + 1) for _ in range(m + 1)] 73 | # 进行状态转移 74 | for i in range(1, m + 1): 75 | for j in range(1, n + 1): 76 | if str1[i - 1] == str2[j - 1]: 77 | # 找到一个 lcs 中的字符 78 | dp[i][j] = 1 + dp[i-1][j-1] 79 | else: 80 | dp[i][j] = max(dp[i-1][j], dp[i][j-1]) 81 | 82 | return dp[-1][-1] 83 | ``` 84 | 85 | ### 二、疑难解答 86 | 87 | 对于 `s1[i]` 和 `s2[j]` 不相等的情况,**至少有一个**字符不在 `lcs` 中,会不会两个字符都不在呢?比如下面这种情况: 88 | 89 | ![](../pictures/LCS/1.png) 90 | 91 | 所以代码是不是应该考虑这种情况,改成这样: 92 | 93 | ```python 94 | if str1[i - 1] == str2[j - 1]: 95 | # ... 96 | else: 97 | dp[i][j] = max(dp[i-1][j], 98 | dp[i][j-1], 99 | dp[i-1][j-1]) 100 | ``` 101 | 102 | 我一开始也有这种怀疑,其实可以这样改,也能得到正确答案,但是多此一举,因为 `dp[i-1][j-1]` 永远是三者中最小的,max 根本不可能取到它。 103 | 104 | 原因在于我们对 dp 数组的定义:对于 `s1[1..i]` 和 `s2[1..j]`,它们的 LCS 长度是 `dp[i][j]`。 105 | 106 | ![](../pictures/LCS/2.png) 107 | 108 | 这样一看,显然 `dp[i-1][j-1]` 对应的 `lcs` 长度不可能比前两种情况大,所以没有必要参与比较。 109 | 110 | ### 三、总结 111 | 112 | 对于两个字符串的动态规划问题,一般来说都是像本文一样定义 DP table,因为这样定义有一个好处,就是容易写出状态转移方程,`dp[i][j]` 的状态可以通过之前的状态推导出来: 113 | 114 | ![](../pictures/LCS/3.png) 115 | 116 | 找状态转移方程的方法是,思考每个状态有哪些「选择」,只要我们能用正确的逻辑做出正确的选择,算法就能够正确运行。 117 | 118 | 119 | 120 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 121 | 122 | ![labuladong](../pictures/labuladong.jpg) 123 | 124 | 125 | [上一篇:动态规划之正则表达](../动态规划系列/动态规划之正则表达.md) 126 | 127 | [下一篇:学习算法和刷题的思路指南](../算法思维系列/学习数据结构和算法的高效方法.md) 128 | 129 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /动态规划系列/贪心算法之区间调度问题.md: -------------------------------------------------------------------------------- 1 | # 贪心算法之区间调度问题 2 | 3 | 什么是贪心算法呢?贪心算法可以认为是动态规划算法的一个特例,相比动态规划,使用贪心算法需要满足更多的条件(贪心选择性质),但是效率比动态规划要高。 4 | 5 | 比如说一个算法问题使用暴力解法需要指数级时间,如果能使用动态规划消除重叠子问题,就可以降到多项式级别的时间,如果满足贪心选择性质,那么可以进一步降低时间复杂度,达到线性级别的。 6 | 7 | 什么是贪心选择性质呢,简单说就是:每一步都做出一个局部最优的选择,最终的结果就是全局最优。注意哦,这是一种特殊性质,其实只有一部分问题拥有这个性质。 8 | 9 | 比如你面前放着 100 张人民币,你只能拿十张,怎么才能拿最多的面额?显然每次选择剩下钞票中面值最大的一张,最后你的选择一定是最优的。 10 | 11 | 然而,大部分问题明显不具有贪心选择性质。比如打斗地主,对手出对儿三,按照贪心策略,你应该出尽可能小的牌刚好压制住对方,但现实情况我们甚至可能会出王炸。这种情况就不能用贪心算法,而得使用动态规划解决,参见前文「动态规划解决博弈问题」。 12 | 13 | ### 一、问题概述 14 | 15 | 言归正传,本文解决一个很经典的贪心算法问题 Interval Scheduling(区间调度问题)。给你很多形如 `[start, end]` 的闭区间,请你设计一个算法,**算出这些区间中最多有几个互不相交的区间**。 16 | 17 | ```java 18 | int intervalSchedule(int[][] intvs) {} 19 | ``` 20 | 21 | 举个例子,`intvs = [[1,3], [2,4], [3,6]]`,这些区间最多有 2 个区间互不相交,即 `[[1,3], [3,6]]`,你的算法应该返回 2。注意边界相同并不算相交。 22 | 23 | 这个问题在生活中的应用广泛,比如你今天有好几个活动,每个活动都可以用区间 `[start, end]` 表示开始和结束的时间,请问你今天**最多能参加几个活动呢?**显然你一个人不能同时参加两个活动,所以说这个问题就是求这些时间区间的最大不相交子集。 24 | 25 | ### 二、贪心解法 26 | 27 | 这个问题有许多看起来不错的贪心思路,却都不能得到正确答案。比如说: 28 | 29 | 也许我们可以每次选择可选区间中开始最早的那个?但是可能存在某些区间开始很早,但是很长,使得我们错误地错过了一些短的区间。或者我们每次选择可选区间中最短的那个?或者选择出现冲突最少的那个区间?这些方案都能很容易举出反例,不是正确的方案。 30 | 31 | 正确的思路其实很简单,可以分为以下三步: 32 | 33 | 1. 从区间集合 intvs 中选择一个区间 x,这个 x 是在当前所有区间中**结束最早的**(end 最小)。 34 | 2. 把所有与 x 区间相交的区间从区间集合 intvs 中删除。 35 | 3. 重复步骤 1 和 2,直到 intvs 为空为止。之前选出的那些 x 就是最大不相交子集。 36 | 37 | 把这个思路实现成算法的话,可以按每个区间的 `end` 数值升序排序,因为这样处理之后实现步骤 1 和步骤 2 都方便很多: 38 | 39 | ![1](../pictures/interval/1.gif) 40 | 41 | 现在来实现算法,对于步骤 1,由于我们预先按照 `end` 排了序,所以选择 x 是很容易的。关键在于,如何去除与 x 相交的区间,选择下一轮循环的 x 呢? 42 | 43 | **由于我们事先排了序**,不难发现所有与 x 相交的区间必然会与 x 的 `end` 相交;如果一个区间不想与 x 的 `end` 相交,它的 `start` 必须要大于(或等于)x 的 `end`: 44 | 45 | ![2](../pictures/interval/2.jpg) 46 | 47 | 看下代码: 48 | 49 | ```java 50 | public int intervalSchedule(int[][] intvs) { 51 | if (intvs.length == 0) return 0; 52 | // 按 end 升序排序 53 | Arrays.sort(intvs, new Comparator() { 54 | public int compare(int[] a, int[] b) { 55 | return a[1] - b[1]; 56 | } 57 | }); 58 | // 至少有一个区间不相交 59 | int count = 1; 60 | // 排序后,第一个区间就是 x 61 | int x_end = intvs[0][1]; 62 | for (int[] interval : intvs) { 63 | int start = interval[0]; 64 | if (start >= x_end) { 65 | // 找到下一个选择的区间了 66 | count++; 67 | x_end = interval[1]; 68 | } 69 | } 70 | return count; 71 | } 72 | ``` 73 | 74 | ### 三、应用举例 75 | 76 | 下面举例几道 LeetCode 题目应用一下区间调度算法。 77 | 78 | 第 435 题,无重叠区间: 79 | 80 | ![title1](../pictures/interval/title1.png) 81 | 82 | 我们已经会求最多有几个区间不会重叠了,那么剩下的不就是至少需要去除的区间吗? 83 | 84 | ```java 85 | int eraseOverlapIntervals(int[][] intervals) { 86 | int n = intervals.length; 87 | return n - intervalSchedule(intervals); 88 | } 89 | ``` 90 | 91 | 第 452 题,用最少的箭头射爆气球: 92 | 93 | ![title2](../pictures/interval/title2.png) 94 | 95 | 其实稍微思考一下,这个问题和区间调度算法一模一样!如果最多有 `n` 个不重叠的区间,那么就至少需要 `n` 个箭头穿透所有区间: 96 | 97 | ![3](../pictures/interval/3.jpg) 98 | 99 | 只是有一点不一样,在 `intervalSchedule` 算法中,如果两个区间的边界触碰,不算重叠;而按照这道题目的描述,箭头如果碰到气球的边界气球也会爆炸,所以说相当于区间的边界触碰也算重叠: 100 | 101 | ![4](../pictures/interval/4.jpg) 102 | 103 | 所以只要将之前的算法稍作修改,就是这道题目的答案: 104 | 105 | ```java 106 | int findMinArrowShots(int[][] intvs) { 107 | // ... 108 | 109 | for (int[] interval : intvs) { 110 | int start = interval[0]; 111 | // 把 >= 改成 > 就行了 112 | if (start > x_end) { 113 | count++; 114 | x_end = interval[1]; 115 | } 116 | } 117 | return count; 118 | } 119 | ``` 120 | 121 | 这么做的原因也不难理解,因为现在边界接触也算重叠,所以 `start == x_end` 时不能更新 x。 122 | 123 | 如果本文对你有帮助,欢迎关注我的公众号 labuladong,致力于把算法问题讲清楚~ 124 | 125 | [上一篇:动态规划之博弈问题](../动态规划系列/动态规划之博弈问题.md) 126 | 127 | [下一篇:动态规划之KMP字符匹配算法](../动态规划系列/动态规划之KMP字符匹配算法.md) 128 | 129 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /技术/linuxshell.md: -------------------------------------------------------------------------------- 1 | 我个人很喜欢使用 Linux 系统,虽然说 Windows 的图形化界面做的确实比 Linux 好,但是对脚本的支持太差了。一开始有点不习惯命令行操作,但是熟悉了之后反而发现移动鼠标点点点才是浪费时间的罪魁祸首。。。 2 | 3 | **那么对于 Linux 命令行,本文不是介绍某些命令的用法,而是说明一些简单却特别容易让人迷惑的细节问题**。 4 | 5 | 1、标准输入和命令参数的区别。 6 | 7 | 2、在后台运行命令在退出终端后也全部退出了。 8 | 9 | 3、单引号和双引号表示字符串的区别。 10 | 11 | 4、有的命令和`sudo`一起用就 command not found。 12 | 13 | ### 一、标准输入和参数的区别 14 | 15 | 这个问题一定是最容易让人迷惑的,具体来说,就是搞不清什么时候用管道符`|`和文件重定向`>`,`<`,什么时候用变量`$`。 16 | 17 | 比如说,我现在有个自动连接宽带的 shell 脚本`connect.sh`,存在我的家目录: 18 | 19 | ```shell 20 | $ where connect.sh 21 | /home/fdl/bin/connect.sh 22 | ``` 23 | 24 | 如果我想删除这个脚本,而且想少敲几次键盘,应该怎么操作呢?我曾经这样尝试过: 25 | 26 | ```shell 27 | $ where connect.sh | rm 28 | ``` 29 | 30 | 实际上,这样操作是错误的,正确的做法应该是这样的: 31 | 32 | ```shell 33 | $ rm $(where connect.sh) 34 | ``` 35 | 36 | 前者试图将`where`的结果连接到`rm`的标准输入,后者试图将结果作为命令行参数传入。 37 | 38 | **标准输入就是编程语言中诸如`scanf`或者`readline`这种命令;而参数是指程序的`main`函数传入的`args`字符数组**。 39 | 40 | 前文「Linux文件描述符」说过,管道符和重定向符是将数据作为程序的标准输入,而`$(cmd)`是读取`cmd`命令输出的数据作为参数。 41 | 42 | 用刚才的例子说,`rm`命令源代码中肯定不接受标准输入,而是接收命令行参数,删除相应的文件。作为对比,`cat`命令是既接受标准输入,又接受命令行参数: 43 | 44 | ```shell 45 | $ cat filename 46 | ...file text... 47 | 48 | $ cat < filename 49 | ...file text... 50 | 51 | $ echo 'hello world' | cat 52 | hello world 53 | ``` 54 | 55 | **如果命令能够让终端阻塞,说明该命令接收标准输入,反之就是不接受**,比如你只运行`cat`命令不加任何参数,终端就会阻塞,等待你输入字符串并回显相同的字符串。 56 | 57 | ### 二、后台运行程序 58 | 59 | 比如说你远程登录到服务器上,运行一个 Django web 程序: 60 | 61 | ```shell 62 | $ python manager.py runserver 0.0.0.0 63 | Listening on 0.0.0.0:8080... 64 | ``` 65 | 66 | 现在你可以通过服务器的 IP 地址测试 Django 服务,但是终端此时就阻塞了,你输入什么都不响应,除非输入 Ctrl-C 或者 Ctrl-/ 终止 python 进程。 67 | 68 | 可以在命令之后加一个`&`符号,这样命令行不会阻塞,可以响应你后续输入的命令,但是如果你退出服务器的登录,就不能访问该网页了。 69 | 70 | 如果你想在退出服务器之后仍然能够访问 web 服务,应该这样写命令 `(cmd &)`: 71 | 72 | ```shell 73 | $ (python manager.py runserver 0.0.0.0 &) 74 | Listening on 0.0.0.0:8080... 75 | 76 | $ logout 77 | ``` 78 | 79 | **底层原理是这样的**: 80 | 81 | 每一个命令行终端都是一个 shell 进程,你在这个终端里执行的程序实际上都是这个 shell 进程分出来的子进程。正常情况下,shell 进程会阻塞,等待子进程退出才重新接收你输入的新的命令。加上`&`号,只是让 shell 进程不再阻塞,可以继续响应你的新命令。但是无论如何,你如果关掉了这个 shell 命令行端口,依附于它的所有子进程都会退出。 82 | 83 | 而`(cmd &)`这样运行命令,则是将`cmd`命令挂到一个`systemd`系统守护进程名下,认`systemd`做爸爸,这样当你退出当前终端时,对于刚才的`cmd`命令就完全没有影响了。 84 | 85 | 类似的,还有一种后台运行常用的做法是这样: 86 | 87 | ```shell 88 | $ nohub some_cmd & 89 | ``` 90 | 91 | `nohub`命令也是类似的原理,不过通过我的测试,还是`(cmd &)`这种形式更加稳定。 92 | 93 | ### 三、单引号和双引号的区别 94 | 95 | 不同的 shell 行为会有细微区别,但有一点是确定的,**对于`$`,`(`,`)`这几个符号,单引号包围的字符串不会做任何转义,双引号包围的字符串会转义**。 96 | 97 | shell 的行为可以测试,使用`set -x`命令,会开启 shell 的命令回显,你可以通过回显观察 shell 到底在执行什么命令: 98 | 99 | ![](../pictures/linuxshell/1.png) 100 | 101 | 可见 `echo $(cmd)` 和 `echo "$(cmd)"`,结果差不多,但是仍然有区别。注意观察,双引号转义完成的结果会自动增加单引号,而前者不会。 102 | 103 | **也就是说,如果 `$` 读取出的参数字符串包含空格,应该用双引号括起来,否则就会出错**。 104 | 105 | ### 四、sudo 找不到命令 106 | 107 | 有时候我们普通用户可以用的命令,用`sudo`加权限之后却报错 command not found: 108 | 109 | ```shell 110 | $ connect.sh 111 | network-manager: Permission denied 112 | 113 | $ sudo connect.sh 114 | sudo: command not found 115 | ``` 116 | 117 | 原因在于,`connect.sh`这个脚本仅存在于该用户的环境变量中: 118 | 119 | ```shell 120 | $ where connect.sh 121 | /home/fdl/bin/connect.sh 122 | ``` 123 | 124 | **当使用`sudo`时,系统认为是 root 用户在执行命令,所以会去搜索 root 用户的环境变量**,而这个脚本在 root 的环境变量目录中当然是找不到的。 125 | 126 | 解决方法是使用脚本文件的路径,而不是仅仅通过脚本名称: 127 | 128 | ```shell 129 | $ sudo /home/fdl/bin/connect.sh 130 | ``` 131 | 132 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 133 | 134 | ![labuladong](../pictures/labuladong.jpg) 135 | 136 | 137 | [上一篇:一文看懂 session 和 cookie](../技术/session和cookie.md) 138 | 139 | [下一篇:加密算法的前身今世](../技术/密码技术.md) 140 | 141 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /技术/linux进程.md: -------------------------------------------------------------------------------- 1 | # Linux的进程、线程、文件描述符是什么 2 | 3 | 说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案:**在 Linux 系统中,进程和线程几乎没有区别**。 4 | 5 | Linux 中的进程就是一个数据结构,看明白就可以理解文件描述符、重定向、管道命令的底层工作原理,最后我们从操作系统的角度看看为什么说线程和进程基本没有区别。 6 | 7 | ### 一、进程是什么 8 | 9 | 首先,抽象地来说,我们的计算机就是这个东西: 10 | 11 | ![](../pictures/linuxProcess/1.jpg) 12 | 13 | 这个大的矩形表示计算机的**内存空间**,其中的小矩形代表**进程**,左下角的圆形表示**磁盘**,右下角的图形表示一些**输入输出设备**,比如鼠标键盘显示器等等。另外,注意到内存空间被划分为了两块,上半部分表示**用户空间**,下半部分表示**内核空间**。 14 | 15 | 用户空间装着用户进程需要使用的资源,比如你在程序代码里开一个数组,这个数组肯定存在用户空间;内核空间存放内核进程需要加载的系统资源,这一些资源一般是不允许用户访问的。但是注意有的用户进程会共享一些内核空间的资源,比如一些动态链接库等等。 16 | 17 | 我们用 C 语言写一个 hello 程序,编译后得到一个可执行文件,在命令行运行就可以打印出一句 hello world,然后程序退出。在操作系统层面,就是新建了一个进程,这个进程将我们编译出来的可执行文件读入内存空间,然后执行,最后退出。 18 | 19 | **你编译好的那个可执行程序只是一个文件**,不是进程,可执行文件必须要载入内存,包装成一个进程才能真正跑起来。进程是要依靠操作系统创建的,每个进程都有它的固有属性,比如进程号(PID)、进程状态、打开的文件等等,进程创建好之后,读入你的程序,你的程序才被系统执行。 20 | 21 | 那么,操作系统是如何创建进程的呢?**对于操作系统,进程就是一个数据结构**,我们直接来看 Linux 的源码: 22 | 23 | ```cpp 24 | struct task_struct { 25 | // 进程状态 26 | long state; 27 | // 虚拟内存结构体 28 | struct mm_struct *mm; 29 | // 进程号 30 | pid_t pid; 31 | // 指向父进程的指针 32 | struct task_struct __rcu *parent; 33 | // 子进程列表 34 | struct list_head children; 35 | // 存放文件系统信息的指针 36 | struct fs_struct *fs; 37 | // 一个数组,包含该进程打开的文件指针 38 | struct files_struct *files; 39 | }; 40 | ``` 41 | 42 | `task_struct`就是 Linux 内核对于一个进程的描述,也可以称为「进程描述符」。源码比较复杂,我这里就截取了一小部分比较常见的。 43 | 44 | 其中比较有意思的是`mm`指针和`files`指针。`mm`指向的是进程的虚拟内存,也就是载入资源和可执行文件的地方;`files`指针指向一个数组,这个数组里装着所有该进程打开的文件的指针。 45 | 46 | ### 二、文件描述符是什么 47 | 48 | 先说`files`,它是一个文件指针数组。一般来说,一个进程会从`files[0]`读取输入,将输出写入`files[1]`,将错误信息写入`files[2]`。 49 | 50 | 举个例子,以我们的角度 C 语言的`printf`函数是向命令行打印字符,但是从进程的角度来看,就是向`files[1]`写入数据;同理,`scanf`函数就是进程试图从`files[0]`这个文件中读取数据。 51 | 52 | **每个进程被创建时,`files`的前三位被填入默认值,分别指向标准输入流、标准输出流、标准错误流。我们常说的「文件描述符」就是指这个文件指针数组的索引**,所以程序的文件描述符默认情况下 0 是输入,1 是输出,2 是错误。 53 | 54 | 我们可以重新画一幅图: 55 | 56 | ![](../pictures/linuxProcess/2.jpg) 57 | 58 | 对于一般的计算机,输入流是键盘,输出流是显示器,错误流也是显示器,所以现在这个进程和内核连了三根线。因为硬件都是由内核管理的,我们的进程需要通过「系统调用」让内核进程访问硬件资源。 59 | 60 | PS:不要忘了,Linux 中一切都被抽象成文件,设备也是文件,可以进行读和写。 61 | 62 | 如果我们写的程序需要其他资源,比如打开一个文件进行读写,这也很简单,进行系统调用,让内核把文件打开,这个文件就会被放到`files`的第 4 个位置: 63 | 64 | ![](../pictures/linuxProcess/3.jpg) 65 | 66 | 明白了这个原理,**输入重定向**就很好理解了,程序想读取数据的时候就会去`files[0]`读取,所以我们只要把`files[0]`指向一个文件,那么程序就会从这个文件中读取数据,而不是从键盘: 67 | 68 | ```shell 69 | $ command < file.txt 70 | ``` 71 | 72 | ![](../pictures/linuxProcess/5.jpg) 73 | 74 | 同理,**输出重定向**就是把`files[1]`指向一个文件,那么程序的输出就不会写入到显示器,而是写入到这个文件中: 75 | 76 | ```shell 77 | $ command > file.txt 78 | ``` 79 | 80 | ![](../pictures/linuxProcess/4.jpg) 81 | 82 | 错误重定向也是一样的,就不再赘述。 83 | 84 | **管道符**其实也是异曲同工,把一个进程的输出流和另一个进程的输入流接起一条「管道」,数据就在其中传递,不得不说这种设计思想真的很优美: 85 | 86 | ```shell 87 | $ cmd1 | cmd2 | cmd3 88 | ``` 89 | 90 | ![](../pictures/linuxProcess/6.jpg) 91 | 92 | 到这里,你可能也看出「Linux 中一切皆文件」设计思路的高明了,不管是设备、另一个进程、socket 套接字还是真正的文件,全部都可以读写,统一装进一个简单的`files`数组,进程通过简单的文件描述符访问相应资源,具体细节交于操作系统,有效解耦,优美高效。 93 | 94 | ### 三、线程是什么 95 | 96 | 首先要明确的是,多进程和多线程都是并发,都可以提高处理器的利用效率,所以现在的关键是,多线程和多进程有啥区别。 97 | 98 | 为什么说 Linux 中线程和进程基本没有区别呢,因为从 Linux 内核的角度来看,并没有把线程和进程区别对待。 99 | 100 | 我们知道系统调用`fork()`可以新建一个子进程,函数`pthread()`可以新建一个线程。**但无论线程还是进程,都是用`task_struct`结构表示的,唯一的区别就是共享的数据区域不同**。 101 | 102 | 换句话说,线程看起来跟进程没有区别,只是线程的某些数据区域和其父进程是共享的,而子进程是拷贝副本,而不是共享。就比如说,`mm`结构和`files`结构在线程中都是共享的,我画两张图你就明白了: 103 | 104 | ![](../pictures/linuxProcess/7.jpg) 105 | 106 | ![](../pictures/linuxProcess/8.jpg) 107 | 108 | 所以说,我们的多线程程序要利用锁机制,避免多个线程同时往同一区域写入数据,否则可能造成数据错乱。 109 | 110 | 那么你可能问,**既然进程和线程差不多,而且多进程数据不共享,即不存在数据错乱的问题,为什么多线程的使用比多进程普遍得多呢**? 111 | 112 | 因为现实中数据共享的并发更普遍呀,比如十个人同时从一个账户取十元,我们希望的是这个共享账户的余额正确减少一百元,而不是希望每人获得一个账户的拷贝,每个拷贝账户减少十元。 113 | 114 | 当然,必须要说明的是,只有 Linux 系统将线程看做共享数据的进程,不对其做特殊看待,其他的很多操作系统是对线程和进程区别对待的,线程有其特有的数据结构,我个人认为不如 Linux 的这种设计简洁,增加了系统的复杂度。 115 | 116 | 在 Linux 中新建线程和进程的效率都是很高的,对于新建进程时内存区域拷贝的问题,Linux 采用了 copy-on-write 的策略优化,也就是并不真正复制父进程的内存空间,而是等到需要写操作时才去复制。**所以 Linux 中新建进程和新建线程都是很迅速的**。 117 | 118 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 119 | 120 | ![labuladong](../pictures/labuladong.jpg) 121 | 122 | 123 | [上一篇:双指针技巧解题框架](../算法思维系列/双指针技巧.md) 124 | 125 | [下一篇:Git/SQL/正则表达式的在线练习平台](../技术/在线练习平台.md) 126 | 127 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /技术/redis入侵.md: -------------------------------------------------------------------------------- 1 | 好吧,我也做了回标题党,像我这么细心的同学,怎么可能让服务器被入侵呢? 2 | 3 | 其实是这样的,昨天我和一个朋友聊天,他说他自己有一台云服务器运行了 Redis 数据库,有一天突然发现数据库里的**数据全没了**,只剩下一个奇奇怪怪的键值对,其中值看起来像一个 RSA 公钥的字符串,他以为是误操作删库了,幸好自己的服务器里没啥重要的数据,也就没在意。 4 | 5 | 经过一番攀谈交心了解到,他跑了一个比较古老已经停止维护的开源项目,安装的旧版本的 Redis,而且他对 Linux 的使用不是很熟练。我就知道,他的服务器已经被攻陷了,想到也许还会有不少像我这位朋友的人,不重视操作系统的权限、防火墙的设置和数据库的保护,我就写一篇文章简单看看这种情况出现的原因,以及如何防范。 6 | 7 | PS:这种手法现在已经行不通了,因为新版本 Redis 都增加了 protect mode,增加了安全性,我们只能在本地简单模拟一下,就别乱试了。 8 | 9 | ### 事件经过 10 | 11 | 其实这种攻击手法都是 2015 年的事了,那时候 Redis 的安全保护机制比较差,只能靠运维人员来合理配置以保证数据库的安全。有段时间,全球几万个 Redis 节点遭到了攻击,出现了上述奇怪的现象,所有数据被清空,只剩一个键叫 `crackit`,它的值形似 RSA 公钥的字符串。 12 | 13 | 后来查证,攻击者利用 Redis 动态设置配置和数据持久化的功能,把自己的 RSA 公钥写入到了被攻击服务器的 `/root/.ssh/authored_keys` 这个文件,从而可以用私钥直接登录对方的 root 用户,侵入对方系统。 14 | 15 | 沦陷的服务器安全防护做的很不好,具体如下: 16 | 17 | 1、Redis 的端口是默认端口,而且可以从公网访问。 18 | 19 | 2、Redis 还没设密码。 20 | 21 | 3、Redis 进程是由 root 用户启动的。 22 | 23 | 以上每个点都是比较危险的,合在一起,那真是很致命了。且不说别人把公钥写到你的系统里,就说连上你的数据库然后删库,那损失都够大了。那么具体的流程是什么呢,下面我在本地回环地址上简单演示一下。 24 | 25 | ### 本地演示 26 | 27 | Redis 监听的默认端口是 6379,我们设置它接收网卡 127.0.0.1 的连接,这样我从本地肯定可以连接 Redis,以此模拟「从公网可以访问 Redis」这一条件。 28 | 29 | 现在我是名叫 fdl 的普通用户,我想用 ssh 登录我系统上的 root 用户,要输入 root 的密码,我不知道,所以没办法登录。 30 | 31 | 除了密码登录之外,还可以使用 RSA 密钥对登录,但是必须要把我的公钥存到 root 的家目录中 `/root/.ssh/authored_keys`。我们知道 `/root` 目录的权限设置是不允许任何其他用户闯入读写的: 32 | 33 | ![](../pictures/redis入侵/1.png) 34 | 35 | 但是,我发现自己竟然可以直接访问 Redis: 36 | 37 | ![](../pictures/redis入侵/2.png) 38 | 39 | 如果 Redis 是以 root 的身份运行的,那么我就可以通过操作 Redis,让它把我的公钥写到 root 的家目录中。Redis 有一种持久化方式是生成 RDB 文件,其中会包含原始数据。 40 | 41 | 我露出了邪恶的微笑,先把 Redis 中的数据全部清空,然后把我的 RSA 公钥写到数据库里,这里在开头和结尾加换行符目的是避免 RDB 文件生成过程中损坏到公钥字符串: 42 | 43 | ![](../pictures/redis入侵/3.png) 44 | 45 | 命令 Redis 把生成的数据文件保存到 `/root/.ssh/` 中的 `authored_keys` 文件中: 46 | 47 | ![](../pictures/redis入侵/4.png) 48 | 49 | 现在,root 的家目录中已经包含了我们的 RSA 公钥,我们现在可以通过密钥对登录进 root 了: 50 | 51 | ![](../pictures/redis入侵/5.png) 52 | 53 | 看一下刚才写入 root 家的公钥: 54 | 55 | ![](../pictures/redis入侵/6.png) 56 | 57 | 乱码是 GDB 文件的某种编码吧,但是中间的公钥被完整保存了,而且 ssh 登录程序竟然也识别了这段被乱码包围的公钥! 58 | 59 | 至此,拥有了 root 权限,就可以为所欲为了。。。 60 | 61 | ### 吸取教训 62 | 63 | 虽然现在基本不会受到这种攻击(新版本的 Redis 没有密码时默认不对外网开放),但是对于系统的安全性是每个人都应该重视的。 64 | 65 | 我们自己折腾东西,用个低配云服务器,为了省事儿一般也不认真配置防火墙,数据库不设密码或者设成 admin、root 这样简单的密码,反正也没啥数据。这样肯定不是个好习惯。 66 | 67 | 现在我们的计算机系统越来越完善,每个成熟的项目都由最优秀的一帮人维护,从技术上说应该算是无懈可击了,那么唯一可能出问题的地方就在于使用它们的人。 68 | 69 | 就像经常看到有人的 QQ 被盗,我相信盗号的人肯定不是跑到腾讯的数据库里盗号,肯定是 QQ 号主安全防范意识差,在哪个钓鱼网站输入了自己的账号密码,导致被盗。我基本没见过微信被盗的,可能是微信弱化密码登录,改用二维码扫描登录的原因。这应该也算是一种安全方面的考量吧,毕竟微信是有支付功能的。 70 | 71 | 上面这种骗局对于技术人来说,看看 url,浏览器分析一下网络包就很容易识别出来,但是你还别不信,一般人真的搞不明白怎么识别钓鱼网站和官方网站。就像我真没想到都 2020 年了,还有人在找 Redis 的这个漏洞,而且还有人中招。。。 72 | 73 | 那么说回 Redis 数据库的使用,在官网上明确写出了安全防护的建议,我简单总结一下吧: 74 | 75 | 1、不要用 root 用户启动 Redis Server,而且一定要设置密码,而且密码不要太短,否则容易被暴力破解。 76 | 77 | 2、配置服务器防火墙和 Redis 的 config 文件,尽量不要让 Redis 与外界接触。 78 | 79 | 3、利用 rename 功能伪装 flushall 这种危险命令,以防被删库,丢失数据。 80 | 81 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 82 | 83 | ![labuladong](../pictures/labuladong.jpg) 84 | -------------------------------------------------------------------------------- /技术/在线练习平台.md: -------------------------------------------------------------------------------- 1 | 虽说我没事就喜欢喷应试教育,但我也从应试教育中发现了一个窍门:如果能够以刷题的形式学习某项技能,效率和效果是最佳的。对于技术的学习,我经常面临的困境是,**理论知识知道的不少,但是有的场景实在无法模拟,缺少亲自动手实践的机会**,如果能有一本带标准答案的习题册让我刷刷就好了。 2 | 3 | 所以在学习新技术时,我首先会去搜索是否有在线刷题平台,你还别说,有的大神真就做了很不错的在线练习平台,下面就介绍几个平台,分别是学习 Git、SQL、正则表达式的在线练习平台。 4 | 5 | ### 一、练习 Git 6 | 7 | 这是个叫做 Learning Git Branching 的项目,是我一定要推荐的: 8 | 9 | ![](../pictures/online/1.png) 10 | 11 | 正如对话框中的自我介绍,这确实也是我至今发现的**最好**的 Git 动画教程,没有之一。 12 | 13 | 想当年我用 Git 就会 `add .`,`clone`,`push`,`pull`,`commit` 几个命令,其他的命令完全不会,Git 就是一个下载器,Github 就是个资源网站加免费图床,命令能不能达成目的都是靠运气。什么版本控制,我根本搞不懂,也懒得去看那一堆乱七八糟的文档。 14 | 15 | 这个网站的教程不是给你举那种修改文件的细节例子,而是将每次 `commit` 都抽象成树的节点,**用动画闯关的形式**,让你自由使用 Git 命令完成目标: 16 | 17 | ![](../pictures/online/2.png) 18 | 19 | 所有 Git 分支都被可视化了,你只要在左侧的命令行输入 Git 命令,分支会进行相应的变化,只要达成任务目标,你就过关啦!网站还会记录你的命令数,试试能不能以最少的命令数过关! 20 | 21 | ![](../pictures/online/3.png) 22 | 23 | 我一开始以为这个教程只包含本地 Git 仓库的版本管理,**后来我惊奇地发现它还有远程仓库的操作教程**! 24 | 25 | ![](../pictures/online/4.png) 26 | 27 | ![](../pictures/online/5.png) 28 | 29 | 真的跟玩游戏一样,难度设计合理,流畅度很好,我一玩都停不下来了,几小时就打通了,哈哈哈! 30 | 31 | ![](../pictures/online/6.png) 32 | 33 | 总之,这个教程很适合初学和进阶,如果你觉得自己对 Git 的掌握还不太好,用 Git 命令还是靠碰运气,就可以玩玩这个教程,相信能够让你更熟练地使用 Git。 34 | 35 | 它是一个开源项目,Github 项目地址: 36 | 37 | https://github.com/pcottle/learnGitBranching 38 | 39 | 教程网站地址: 40 | 41 | https://learngitbranching.js.org 42 | 43 | ### 二、练习正则表达式 44 | 45 | **正则表达式是个非常强有力的工具**,可以说计算机中的一切数据都是字符,借助正则表达式这种模式匹配工具,操作计算机可以说是如虎添翼。 46 | 47 | 我这里要推荐两个网站,一个是练习平台,一个是测试正则表达式的平台。 48 | 49 | 先说练习平台,叫做 RegexOne: 50 | 51 | ![](../pictures/online/9.png) 52 | 53 | 前面有基本教程,后面有一些常见的正则表达式题目,比如判断邮箱、URL、电话号,或者抽取日志的关键信息等等。 54 | 55 | 只要写出符合要求的正则表达式,就可以进入下一个问题,关键是每道题还有标准答案,可以点击下面的 solution 按钮查看: 56 | 57 | ![](../pictures/online/10.png) 58 | 59 | RegexOne 网址: 60 | 61 | https://regexone.com/ 62 | 63 | 再说测试工具,是个叫做 RegExr 的 Github 项目,这是它的网站: 64 | 65 | ![](../pictures/online/11.png) 66 | 67 | 可以看见,输入文本和正则模式串后,**网站会给正则表达式添加好看且容易辨认的样式,自动在文本中搜索模式串,高亮显示匹配的字符串,并且还会显示每个分组捕获的字符串**。 68 | 69 | 这个网站可以配合前面的正则练习平台使用,在这里尝试各种表达式,成功匹配之后粘贴过去。 70 | 71 | RegExr 网址: 72 | 73 | https://regexr.com/ 74 | 75 | ### 三、练习 SQL 76 | 77 | 这是一个叫做 SQLZOO 的网站,左侧是所有的练习内容: 78 | 79 | ![](../pictures/online/7.png) 80 | 81 | SQLZOO 是一款很好用的 SQL 练习平台,英文不难理解,可以直接看英文版,但是也可以切换繁体中文,比较友好。 82 | 83 | 这里都是比较常用的 SQL 命令,给你一个需求,你写 SQL 语句实现正确的查询结果。**最重要的是,这里不仅对每个命令的用法有详细解释,每个专题后面还有选择题(quiz),而且有判题系统,甚至有的比较难的题目还有视频讲解**: 84 | 85 | ![](../pictures/online/8.png) 86 | 87 | 至于难度,循序渐进,即便对新手也很友好,靠后的问题确实比较有技巧性,相信这是热爱思维挑战的人喜欢的!LeetCode 也有 SQL 相关的题目,不过难度一般比较大,我觉得 SQLZOO 刷完基础 SQL 命令再去 LeetCode 刷比较合适。 88 | 89 | 网站地址: 90 | 91 | https://sqlzoo.net/ 92 | 93 | 94 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 95 | 96 | ![labuladong](../pictures/labuladong.jpg) 97 | 98 | 99 | [上一篇:Linux的进程、线程、文件描述符是什么](../技术/linux进程.md) 100 | 101 | [下一篇:动态规划详解](../动态规划系列/动态规划详解进阶.md) 102 | 103 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /数据结构系列/README.md: -------------------------------------------------------------------------------- 1 | # 数据结构系列 2 | 3 | 这一章主要是一些特殊的数据结构设计,比如单调栈解决 Next Greater Number,单调队列解决滑动窗口问题;还有常用数据结构的操作,比如链表、树、二叉堆。 4 | 5 | 欢迎关注我的公众号 labuladong,方便获得最新的优质文章: 6 | 7 | ![labuladong二维码](../pictures/qrcode.jpg) -------------------------------------------------------------------------------- /数据结构系列/单调栈.md: -------------------------------------------------------------------------------- 1 | ### 如何使用单调栈解题 2 | 3 | 栈(stack)是很简单的一种数据结构,先进后出的逻辑顺序,符合某些问题的特点,比如说函数调用栈。 4 | 5 | 单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。 6 | 7 | 听起来有点像堆(heap)?不是的,单调栈用途不太广泛,只处理一种典型的问题,叫做 Next Greater Element。本文用讲解单调队列的算法模版解决这类问题,并且探讨处理「循环数组」的策略。 8 | 9 | 首先,讲解 Next Greater Number 的原始问题:给你一个数组,返回一个等长的数组,对应索引存储着下一个更大元素,如果没有更大的元素,就存 -1。不好用语言解释清楚,直接上一个例子: 10 | 11 | 给你一个数组 [2,1,2,4,3],你返回数组 [4,2,4,-1,-1]。 12 | 13 | 解释:第一个 2 后面比 2 大的数是 4; 1 后面比 1 大的数是 2;第二个 2 后面比 2 大的数是 4; 4 后面没有比 4 大的数,填 -1;3 后面没有比 3 大的数,填 -1。 14 | 15 | 这道题的暴力解法很好想到,就是对每个元素后面都进行扫描,找到第一个更大的元素就行了。但是暴力解法的时间复杂度是 O(n^2)。 16 | 17 | 这个问题可以这样抽象思考:把数组的元素想象成并列站立的人,元素大小想象成人的身高。这些人面对你站成一列,如何求元素「2」的 Next Greater Number 呢?很简单,如果能够看到元素「2」,那么他后面可见的第一个人就是「2」的 Next Greater Number,因为比「2」小的元素身高不够,都被「2」挡住了,第一个露出来的就是答案。 18 | 19 | ![ink-image](../pictures/%E5%8D%95%E8%B0%83%E6%A0%88/1.png) 20 | 21 | 这个情景很好理解吧?带着这个抽象的情景,先来看下代码。 22 | 23 | ```cpp 24 | vector nextGreaterElement(vector& nums) { 25 | vector ans(nums.size()); // 存放答案的数组 26 | stack s; 27 | for (int i = nums.size() - 1; i >= 0; i--) { // 倒着往栈里放 28 | while (!s.empty() && s.top() <= nums[i]) { // 判定个子高矮 29 | s.pop(); // 矮个起开,反正也被挡着了。。。 30 | } 31 | ans[i] = s.empty() ? -1 : s.top(); // 这个元素身后的第一个高个 32 | s.push(nums[i]); // 进队,接受之后的身高判定吧! 33 | } 34 | return ans; 35 | } 36 | ``` 37 | 38 | 这就是单调队列解决问题的模板。for 循环要从后往前扫描元素,因为我们借助的是栈的结构,倒着入栈,其实是正着出栈。while 循环是把两个“高个”元素之间的元素排除,因为他们的存在没有意义,前面挡着个“更高”的元素,所以他们不可能被作为后续进来的元素的 Next Great Number 了。 39 | 40 | 这个算法的时间复杂度不是那么直观,如果你看到 for 循环嵌套 while 循环,可能认为这个算法的复杂度也是 O(n^2),但是实际上这个算法的复杂度只有 O(n)。 41 | 42 | 分析它的时间复杂度,要从整体来看:总共有 n 个元素,每个元素都被 push 入栈了一次,而最多会被 pop 一次,没有任何冗余操作。所以总的计算规模是和元素规模 n 成正比的,也就是 O(n) 的复杂度。 43 | 44 | 现在,你已经掌握了单调栈的使用技巧,来一个简单的变形来加深一下理解。 45 | 46 | 给你一个数组 T = [73, 74, 75, 71, 69, 72, 76, 73],这个数组存放的是近几天的天气气温(这气温是铁板烧?不是的,这里用的华氏度)。你返回一个数组,计算:对于每一天,你还要至少等多少天才能等到一个更暖和的气温;如果等不到那一天,填 0 。 47 | 48 | 举例:给你 T = [73, 74, 75, 71, 69, 72, 76, 73],你返回 [1, 1, 4, 2, 1, 1, 0, 0]。 49 | 50 | 解释:第一天 73 华氏度,第二天 74 华氏度,比 73 大,所以对于第一天,只要等一天就能等到一个更暖和的气温。后面的同理。 51 | 52 | 你已经对 Next Greater Number 类型问题有些敏感了,这个问题本质上也是找 Next Greater Number,只不过现在不是问你 Next Greater Number 是多少,而是问你当前距离 Next Greater Number 的距离而已。 53 | 54 | 相同类型的问题,相同的思路,直接调用单调栈的算法模板,稍作改动就可以啦,直接上代码把。 55 | 56 | ```cpp 57 | vector dailyTemperatures(vector& T) { 58 | vector ans(T.size()); 59 | stack s; // 这里放元素索引,而不是元素 60 | for (int i = T.size() - 1; i >= 0; i--) { 61 | while (!s.empty() && T[s.top()] <= T[i]) { 62 | s.pop(); 63 | } 64 | ans[i] = s.empty() ? 0 : (s.top() - i); // 得到索引间距 65 | s.push(i); // 加入索引,而不是元素 66 | } 67 | return ans; 68 | } 69 | ``` 70 | 71 | 72 | 73 | 单调栈讲解完毕。下面开始另一个重点:如何处理「循环数组」。 74 | 75 | 同样是 Next Greater Number,现在假设给你的数组是个环形的,如何处理? 76 | 77 | 给你一个数组 [2,1,2,4,3],你返回数组 [4,2,4,-1,4]。拥有了环形属性,最后一个元素 3 绕了一圈后找到了比自己大的元素 4 。 78 | 79 | ![ink-image](../pictures/%E5%8D%95%E8%B0%83%E6%A0%88/2.png) 80 | 81 | 82 | 首先,计算机的内存都是线性的,没有真正意义上的环形数组,但是我们可以模拟出环形数组的效果,一般是通过 % 运算符求模(余数),获得环形特效: 83 | 84 | ```java 85 | int[] arr = {1,2,3,4,5}; 86 | int n = arr.length, index = 0; 87 | while (true) { 88 | print(arr[index % n]); 89 | index++; 90 | } 91 | ``` 92 | 93 | 回到 Next Greater Number 的问题,增加了环形属性后,问题的难点在于:这个 Next 的意义不仅仅是当前元素的右边了,有可能出现在当前元素的左边(如上例)。 94 | 95 | 明确问题,问题就已经解决了一半了。我们可以考虑这样的思路:将原始数组“翻倍”,就是在后面再接一个原始数组,这样的话,按照之前“比身高”的流程,每个元素不仅可以比较自己右边的元素,而且也可以和左边的元素比较了。 96 | 97 | ![ink-image (2)](../pictures/%E5%8D%95%E8%B0%83%E6%A0%88/3.png) 98 | 99 | 怎么实现呢?你当然可以把这个双倍长度的数组构造出来,然后套用算法模板。但是,我们可以不用构造新数组,而是利用循环数组的技巧来模拟。直接看代码吧: 100 | 101 | ```cpp 102 | vector nextGreaterElements(vector& nums) { 103 | int n = nums.size(); 104 | vector res(n); // 存放结果 105 | stack s; 106 | // 假装这个数组长度翻倍了 107 | for (int i = 2 * n - 1; i >= 0; i--) { 108 | while (!s.empty() && s.top() <= nums[i % n]) 109 | s.pop(); 110 | res[i % n] = s.empty() ? -1 : s.top(); 111 | s.push(nums[i % n]); 112 | } 113 | return res; 114 | } 115 | ``` 116 | 117 | 至此,你已经掌握了单调栈的设计方法及代码模板,学会了解决 Next Greater Number,并能够处理循环数组了。 118 | 119 | 你的在看,是对我的鼓励。关注公众号:labuladong 120 | 121 | 122 | [上一篇:二叉搜索树操作集锦](../数据结构系列/二叉搜索树操作集锦.md) 123 | 124 | [下一篇:特殊数据结构:单调队列](../数据结构系列/单调队列.md) 125 | 126 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/README.md: -------------------------------------------------------------------------------- 1 | # 算法思维系列 2 | 3 | 本章包含一些常用的算法技巧,比如前缀和、回溯思想、位操作、双指针、如何正确书写二分查找等等。 4 | 5 | 欢迎关注我的公众号 labuladong,方便获得最新的优质文章: 6 | 7 | ![labuladong二维码](../pictures/qrcode.jpg) -------------------------------------------------------------------------------- /算法思维系列/twoSum问题的核心思想.md: -------------------------------------------------------------------------------- 1 | # twoSum问题的核心思想 2 | 3 | Two Sum 系列问题在 LeetCode 上有好几道,这篇文章就挑出有代表性的几道,介绍一下这种问题怎么解决。 4 | 5 | ### TwoSum I 6 | 7 | 这个问题的**最基本形式**是这样:给你一个数组和一个整数 `target`,可以保证数组中**存在**两个数的和为 `target`,请你返回这两个数的索引。 8 | 9 | 比如输入 `nums = [3,1,3,6], target = 6`,算法应该返回数组 `[0,2]`,因为 3 + 3 = 6。 10 | 11 | 这个问题如何解决呢?首先最简单粗暴的办法当然是穷举了: 12 | 13 | ```java 14 | int[] twoSum(int[] nums, int target) { 15 | 16 | for (int i = 0; i < nums.length; i++) 17 | for (int j = i + 1; j < nums.length; j++) 18 | if (nums[j] == target - nums[i]) 19 | return new int[] { i, j }; 20 | 21 | // 不存在这么两个数 22 | return new int[] {-1, -1}; 23 | } 24 | ``` 25 | 26 | 这个解法非常直接,时间复杂度 O(N^2),空间复杂度 O(1)。 27 | 28 | 可以通过一个哈希表减少时间复杂度: 29 | 30 | ```java 31 | int[] twoSum(int[] nums, int target) { 32 | int n = nums.length; 33 | index index = new HashMap<>(); 34 | // 构造一个哈希表:元素映射到相应的索引 35 | for (int i = 0; i < n; i++) 36 | index.put(nums[i], i); 37 | 38 | for (int i = 0; i < n; i++) { 39 | int other = target - nums[i]; 40 | // 如果 other 存在且不是 nums[i] 本身 41 | if (index.containsKey(other) && index.get(other) != i) 42 | return new int[] {i, index.get(other)}; 43 | } 44 | 45 | return new int[] {-1, -1}; 46 | } 47 | ``` 48 | 49 | 这样,由于哈希表的查询时间为 O(1),算法的时间复杂度降低到 O(N),但是需要 O(N) 的空间复杂度来存储哈希表。不过综合来看,是要比暴力解法高效的。 50 | 51 | **我觉得 Two Sum 系列问题就是想教我们如何使用哈希表处理问题**。我们接着往后看。 52 | 53 | ### TwoSum II 54 | 55 | 这里我们稍微修改一下上面的问题。我们设计一个类,拥有两个 API: 56 | 57 | ```java 58 | class TwoSum { 59 | // 向数据结构中添加一个数 number 60 | public void add(int number); 61 | // 寻找当前数据结构中是否存在两个数的和为 value 62 | public boolean find(int value); 63 | } 64 | ``` 65 | 66 | 如何实现这两个 API 呢,我们可以仿照上一道题目,使用一个哈希表辅助 `find` 方法: 67 | 68 | ```java 69 | class TwoSum { 70 | Map freq = new HashMap<>(); 71 | 72 | public void add(int number) { 73 | // 记录 number 出现的次数 74 | freq.put(number, freq.getOrDefault(number, 0) + 1); 75 | } 76 | 77 | public boolean find(int value) { 78 | for (Integer key : freq.keySet()) { 79 | int other = value - key; 80 | // 情况一 81 | if (other == key && freq.get(key) > 1) 82 | return true; 83 | // 情况二 84 | if (other != key && freq.containsKey(other)) 85 | return true; 86 | } 87 | return false; 88 | } 89 | } 90 | ``` 91 | 92 | 进行 `find` 的时候有两种情况,举个例子: 93 | 94 | 情况一:`add` 了 `[3,3,2,5]` 之后,执行 `find(6)`,由于 3 出现了两次,3 + 3 = 6,所以返回 true。 95 | 96 | 情况二:`add` 了 `[3,3,2,5]` 之后,执行 `find(7)`,那么 `key` 为 2,`other` 为 5 时算法可以返回 true。 97 | 98 | 除了上述两种情况外,`find` 只能返回 false 了。 99 | 100 | 对于这个解法的时间复杂度呢,`add` 方法是 O(1),`find` 方法是 O(N),空间复杂度为 O(N),和上一道题目比较类似。 101 | 102 | **但是对于 API 的设计,是需要考虑现实情况的**。比如说,我们设计的这个类,使用 `find` 方法非常频繁,那么每次都要 O(N) 的时间,岂不是很浪费费时间吗?对于这种情况,我们是否可以做些优化呢? 103 | 104 | 是的,对于频繁使用 `find` 方法的场景,我们可以进行优化。我们可以参考上一道题目的暴力解法,借助**哈希集合**来针对性优化 `find` 方法: 105 | 106 | ```java 107 | class TwoSum { 108 | Set sum = new HashSet<>(); 109 | List nums = new ArrayList<>(); 110 | 111 | public void add(int number) { 112 | // 记录所有可能组成的和 113 | for (int n : nums) 114 | sum.add(n + number); 115 | nums.add(number); 116 | } 117 | 118 | public boolean find(int value) { 119 | return sum.contains(value); 120 | } 121 | } 122 | ``` 123 | 124 | 这样 `sum` 中就储存了所有加入数字可能组成的和,每次 `find` 只要花费 O(1) 的时间在集合中判断一下是否存在就行了,显然非常适合频繁使用 `find` 的场景。 125 | 126 | ### 三、总结 127 | 128 | 对于 TwoSum 问题,一个难点就是给的数组**无序**。对于一个无序的数组,我们似乎什么技巧也没有,只能暴力穷举所有可能。 129 | 130 | **一般情况下,我们会首先把数组排序再考虑双指针技巧**。TwoSum 启发我们,HashMap 或者 HashSet 也可以帮助我们处理无序数组相关的简单问题。 131 | 132 | 另外,设计的核心在于权衡,利用不同的数据结构,可以得到一些针对性的加强。 133 | 134 | 最后,如果 TwoSum I 中给的数组是有序的,应该如何编写算法呢?答案很简单,前文「双指针技巧汇总」写过: 135 | 136 | ```java 137 | int[] twoSum(int[] nums, int target) { 138 | int left = 0, right = nums.length - 1; 139 | while (left < right) { 140 | int sum = nums[left] + nums[right]; 141 | if (sum == target) { 142 | return new int[]{left, right}; 143 | } else if (sum < target) { 144 | left++; // 让 sum 大一点 145 | } else if (sum > target) { 146 | right--; // 让 sum 小一点 147 | } 148 | } 149 | // 不存在这样两个数 150 | return new int[]{-1, -1}; 151 | } 152 | ``` 153 | 154 | 155 | 156 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 157 | 158 | ![labuladong](../pictures/labuladong.jpg) 159 | 160 | 161 | [上一篇:滑动窗口技巧](../算法思维系列/滑动窗口技巧.md) 162 | 163 | [下一篇:常用的位操作](../算法思维系列/常用的位操作.md) 164 | 165 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/为什么推荐算法4.md: -------------------------------------------------------------------------------- 1 | # 为什么我推荐《算法4》 2 | 3 | 咱们的公众号有很多硬核的算法文章,今天就聊点轻松的,就具体聊聊我非常“鼓吹”的《算法4》。这本书我在之前的文章多次推荐过,但是没有具体的介绍,今天就来正式介绍一下。。 4 | 5 | 我的推荐不会直接甩一大堆书目,而是会联系实际生活,讲一些书中有趣有用的知识,无论你最后会不会去看这本书,本文都会给你带来一些收获。 6 | 7 | **首先这本书是适合初学者的**。总是有很多读者问,我只会 C 语言,能不能看《算法4》?学算法最好用什么语言?诸如此类的问题。 8 | 9 | 经常看咱们公众号的读者应该体会到了,算法其实是一种思维模式,和你用什么语言没啥关系。我们的文章也不会固定用某一种语言,而是什么语言写出来容易理解就用什么语言。再退一步说,到底适不适合你,网上找个 PDF 亲自看一下不就知道了? 10 | 11 | 《算法4》看起来挺厚的,但是前面几十页是教你 Java 的;每章后面还有习题,占了不少页数;每章还有一些数学证明,这些都可以忽略。这样算下来,剩下的就是基础知识和疑难解答之类的内容,含金量很高,把这些基础知识动手实践一遍,真的就可以达到不错的水平了。 12 | 13 | 我觉得这本书之所以能有这么高的评分,一个是因为讲解详细,还有大量配图,另一个原因就是书中把一些算法和现实生活中的使用场景联系起来,你不仅知道某个算法怎么实现,也知道它大概能运用到什么场景,下面我就来介绍两个图算法的简单应用。 14 | 15 | ### 一、二分图的应用 16 | 17 | 我想举的第一个例子是**二分图**。简单来说,二分图就是一幅拥有特殊性质的图:能够用两种颜色为所有顶点着色,使得任何一条边的两个顶点颜色不同。 18 | 19 | ![](../pictures/algo4/1.jpg) 20 | 21 | 明白了二分图是什么,能解决什么实际问题呢?**算法方面,常见的操作是如何判定一幅图是不是二分图**。比如说下面这道 LeetCode 题目: 22 | 23 | ![](../pictures/algo4/title.png) 24 | 25 | 你想想,如果我们把每个人视为一个顶点,边代表讨厌;相互讨厌的两个人之间连接一条边,就可以形成一幅图。那么根据刚才二分图的定义,如果这幅图是一幅二分图,就说明这些人可以被分为两组,否则的话就不行。 26 | 27 | 这是判定二分图算法的一个应用,**其实二分图在数据结构方面也有一些不错的特性**。 28 | 29 | 比如说我们需要一种数据结构来储存电影和演员之间的关系:某一部电影肯定是由多位演员出演的,且某一位演员可能会出演多部电影。你使用什么数据结构来存储这种关系呢? 30 | 31 | 既然是存储映射关系,最简单的不就是使用哈希表嘛,我们可以使用一个 `HashMap>` 来存储电影到演员列表的映射,如果给一部电影的名字,就能快速得到出演该电影的演员。 32 | 33 | 但是如果给出一个演员的名字,我们想快速得到该演员演出的所有电影,怎么办呢?这就需要「反向索引」,对之前的哈希表进行一些操作,新建另一个哈希表,把演员作为键,把电影列表作为值。 34 | 35 | 对于上面这个例子,可以使用二分图来取代哈希表。电影和演员是具有二分图性质的:如果把电影和演员视为图中的顶点,出演关系作为边,那么与电影顶点相连的一定是演员,与演员相邻的一定是电影,不存在演员和演员相连,电影和电影相连的情况。 36 | 37 | 回顾二分图的定义,如果对演员和电影顶点着色,肯定就是一幅二分图: 38 | 39 | ![](../pictures/algo4/2.jpg) 40 | 41 | 如果这幅图构建完成,就不需要反向索引,对于演员顶点,其直接连接的顶点就是他出演的电影,对于电影顶点,其直接连接的顶点就是出演演员。 42 | 43 | 当然,对于这个问题,书中还提到了一些其他有趣的玩法,比如说社交网络中「间隔度数」的计算(六度空间理论应该听说过)等等,其实就是一个 BFS 广度优先搜索寻找最短路径的问题,具体代码实现这里就不展开了。 44 | 45 | ### 二、套汇的算法 46 | 47 | 如果我们说货币 A 到货币 B 的汇率是 10,意思就是 1 单位的货币 A 可以换 10 单位货币 B。如果我们把每种货币视为一幅图的顶点,货币之间的汇率视为加权有向边,那么整个汇率市场就是一幅「完全加权有向图」。 48 | 49 | 一旦把现实生活中的情景抽象成图,就有可能运用算法解决一些问题。比如说图中可能存在下面的情况: 50 | 51 | ![](../pictures/algo4/3.jpg) 52 | 53 | 图中的加权有向边代表汇率,我们可以发现如果把 100 单位的货币 A 换成 B,再换成 C,最后换回 A,就可以得到 100×0.9×0.8×1.4 = 100.8 单位的 A!如果交易的金额大一些的话,赚的钱是很可观的,这种空手套白狼的操作就是套汇。 54 | 55 | 现实中交易会有种种限制,而且市场瞬息万变,但是套汇的利润还是很高的,关键就在于如何**快速**找到这种套汇机会呢? 56 | 57 | 借助图的抽象,我们发现套汇机会其实就是一个环,且这个环上的权重之积大于 1,只要在顺着这个环交易一圈就能空手套白狼。 58 | 59 | 图论中有一个经典算法叫做 **Bellman-Ford 算法,可以用于寻找负权重环**。对于我们说的套汇问题,可以先把所有边的权重 w 替换成 -ln(w),这样「寻找权重乘积大于 1 的环」就转化成了「寻找权重和小于 0 的环」,就可以使用 Bellman-Ford 算法在 O(EV) 的时间内寻找负权重环,也就是寻找套汇机会。 60 | 61 | 《算法4》就介绍到这里,关于上面两个例子的具体内容,可以自己去看书,公众号后台回复关键词「算法4」就有 PDF。 62 | 63 | 64 | ### 三、最后说几句 65 | 66 | 首先,前文说对于数学证明、章后习题可以忽略,可能有人要抬杠了:难道习题和数学证明不重要吗? 67 | 68 | 那我想说,就是不重要,起码对大多数人来说不重要。我觉得吧,学习就要带着目的性去学,大部分人学算法不就是巩固计算机知识,对付面试题目吗?**如果是这个目的**,那就学些基本的数据结构和经典算法,明白它们的时间复杂度,然后去刷题就好了,何必和习题、证明过不去? 69 | 70 | 这也是我从来不推荐《算法导论》这本书的原因。如果有人给你推荐这本书,只可能有两个原因,要么他是真大佬,要么他在装大佬。《算法导论》中充斥大量数学证明,而且很多数据结构是很少用到的,顶多当个字典用。你说你学了那些有啥用呢,饶过自己呗。 71 | 72 | 另外,读书在精不在多。你花时间《算法4》过个大半(最后小半部分有点困难),同时刷点题,看看咱们的公众号文章,算法这块真就够了,别对细节问题太较真。 73 | 74 | **致力于把算法讲清楚!欢迎关注我的微信公众号 labuladong,查看更多通俗易懂的文章**,公众号后台回复关键词「算法4」可以获得 PDF 下载: 75 | 76 | ![labuladong](../pictures/labuladong.png) 77 | 78 | [上一篇:学习算法和刷题的框架思维](../算法思维系列/学习数据结构和算法的高效方法.md) 79 | 80 | [下一篇:动态规划解题框架](../动态规划系列/动态规划详解进阶.md) 81 | 82 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/信封嵌套问题.md: -------------------------------------------------------------------------------- 1 | # 信封嵌套问题 2 | 3 | 很多算法问题都需要排序技巧,其难点不在于排序本身,而是需要巧妙地排序进行预处理,将算法问题进行转换,为之后的操作打下基础。 4 | 5 | 信封嵌套问题就需要先按特定的规则排序,之后就转换为一个 [最长递增子序列问题](../动态规划系列/动态规划设计:最长递增子序列.md),可以用前文 [二分查找详解](二分查找详解.md) 的技巧来解决了。 6 | 7 | ### 一、题目概述 8 | 9 | 信封嵌套问题是个很有意思且经常出现在生活中的问题,先看下题目: 10 | 11 | ![title](../pictures/%E4%BF%A1%E5%B0%81%E5%B5%8C%E5%A5%97/title.png) 12 | 13 | 这道题目其实是最长递增子序列(Longes Increasing Subsequence,简写为 LIS)的一个变种,因为很显然,每次合法的嵌套是大的套小的,相当于找一个最长递增的子序列,其长度就是最多能嵌套的信封个数。 14 | 15 | 但是难点在于,标准的 LIS 算法只能在数组中寻找最长子序列,而我们的信封是由 `(w, h)` 这样的二维数对形式表示的,如何把 LIS 算法运用过来呢? 16 | 17 | ![0](../pictures/%E4%BF%A1%E5%B0%81%E5%B5%8C%E5%A5%97/0.jpg) 18 | 19 | 读者也许会想,通过 `w × h` 计算面积,然后对面积进行标准的 LIS 算法。但是稍加思考就会发现这样不行,比如 `1 × 10` 大于 `3 × 3`,但是显然这样的两个信封是无法互相嵌套的。 20 | 21 | ### 二、解法 22 | 23 | 这道题的解法是比较巧妙的: 24 | 25 | **先对宽度 `w` 进行升序排序,如果遇到 `w` 相同的情况,则按照高度 `h` 降序排序。之后把所有的 `h` 作为一个数组,在这个数组上计算 LIS 的长度就是答案。** 26 | 27 | 画个图理解一下,先对这些数对进行排序: 28 | 29 | ![1](../pictures/%E4%BF%A1%E5%B0%81%E5%B5%8C%E5%A5%97/1.jpg) 30 | 31 | 然后在 `h` 上寻找最长递增子序列: 32 | 33 | ![2](../pictures/%E4%BF%A1%E5%B0%81%E5%B5%8C%E5%A5%97/2.jpg) 34 | 35 | 这个子序列就是最优的嵌套方案。 36 | 37 | 这个解法的关键在于,对于宽度 `w` 相同的数对,要对其高度 `h` 进行降序排序。因为两个宽度相同的信封不能相互包含的,逆序排序保证在 `w` 相同的数对中最多只选取一个。 38 | 39 | 下面看代码: 40 | 41 | ```java 42 | // envelopes = [[w, h], [w, h]...] 43 | public int maxEnvelopes(int[][] envelopes) { 44 | int n = envelopes.length; 45 | // 按宽度升序排列,如果宽度一样,则按高度降序排列 46 | Arrays.sort(envelopes, new Comparator() 47 | { 48 | public int compare(int[] a, int[] b) { 49 | return a[0] == b[0] ? 50 | b[1] - a[1] : a[0] - b[0]; 51 | } 52 | }); 53 | // 对高度数组寻找 LIS 54 | int[] height = new int[n]; 55 | for (int i = 0; i < n; i++) 56 | height[i] = envelopes[i][1]; 57 | 58 | return lengthOfLIS(height); 59 | } 60 | ``` 61 | 62 | 关于最长递增子序列的寻找方法,在前文中详细介绍了动态规划解法,并用扑克牌游戏解释了二分查找解法,本文就不展开了,直接套用算法模板: 63 | 64 | ```java 65 | /* 返回 nums 中 LIS 的长度 */ 66 | public int lengthOfLIS(int[] nums) { 67 | int piles = 0, n = nums.length; 68 | int[] top = new int[n]; 69 | for (int i = 0; i < n; i++) { 70 | // 要处理的扑克牌 71 | int poker = nums[i]; 72 | int left = 0, right = piles; 73 | // 二分查找插入位置 74 | while (left < right) { 75 | int mid = (left + right) / 2; 76 | if (top[mid] >= poker) 77 | right = mid; 78 | else 79 | left = mid + 1; 80 | } 81 | if (left == piles) piles++; 82 | // 把这张牌放到牌堆顶 83 | top[left] = poker; 84 | } 85 | // 牌堆数就是 LIS 长度 86 | return piles; 87 | } 88 | ``` 89 | 90 | 为了清晰,我将代码分为了两个函数, 你也可以合并,这样可以节省下 `height` 数组的空间。 91 | 92 | 此算法的时间复杂度为 $O(NlogN)$,因为排序和计算 LIS 各需要 $O(NlogN)$ 的时间。 93 | 94 | 空间复杂度为 $O(N)$,因为计算 LIS 的函数中需要一个 `top` 数组。 95 | 96 | ### 三、总结 97 | 98 | 这个问题是个 Hard 级别的题目,难就难在排序,正确地排序后此问题就被转化成了一个标准的 LIS 问题,容易解决一些。 99 | 100 | 其实这种问题还可以拓展到三维,比如说现在不是让你嵌套信封,而是嵌套箱子,每个箱子有长宽高三个维度,请你算算最多能嵌套几个箱子? 101 | 102 | 我们可能会这样想,先把前两个维度(长和宽)按信封嵌套的思路求一个嵌套序列,最后在这个序列的第三个维度(高度)找一下 LIS,应该能算出答案。 103 | 104 | 实际上,这个思路是错误的。这类问题叫做「偏序问题」,上升到三维会使难度巨幅提升,需要借助一种高级数据结构「树状数组」,有兴趣的读者可以自行搜索。 105 | 106 | 有很多算法问题都需要排序后进行处理,阿东正在进行整理总结。希望本文对你有帮助。 107 | 108 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 109 | 110 | ![labuladong](../pictures/labuladong.jpg) 111 | 112 | 113 | [上一篇:区间调度之区间交集问题](../算法思维系列/区间交集问题.md) 114 | 115 | [下一篇:几个反直觉的概率问题](../算法思维系列/几个反直觉的概率问题.md) 116 | 117 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/几个反直觉的概率问题.md: -------------------------------------------------------------------------------- 1 | # 几个反直觉的概率问题 2 | 3 | 上篇文章 [洗牌算法详解](./洗牌算法.md) 讲到了验证概率算法的蒙特卡罗方法,今天聊点轻松的内容:几个和概率相关的有趣问题。 4 | 5 | 计算概率有下面两个最简单的原则: 6 | 7 | 原则一、计算概率一定要有一个参照系,称作「样本空间」,即随机事件可能出现的所有结果。事件 A 发生的概率 = A 包含的样本点 / 样本空间的样本总数。 8 | 9 | 原则二、计算概率一定要明白,概率是一个连续的整体,不可以把连续的概率分割开,也就是所谓的条件概率。 10 | 11 | 上述两个原则高中就学过,但是我们还是很容易犯错,而且犯错的流程也有异曲同工之妙: 12 | 13 | 先是忽略了原则二,错误地计算了样本空间,然后通过原则一算出了错误的答案。 14 | 15 | 下面介绍几个简单却具有迷惑性的问题,分别是男孩女孩问题、生日悖论、三门问题。当然,三门问题可能是大家最耳熟的,所以就多说一些有趣的思考。 16 | 17 | 18 | ### 一、男孩女孩问题 19 | 20 | 假设有一个家庭,有两个孩子,现在告诉你其中有一个男孩,请问另一个也是男孩的概率是多少? 21 | 22 | 很多人,包括我在内,不假思索地回答:1/2 啊,因为另一个孩子要么是男孩,要么是女孩,而且概率相等呀。但是实际上,答案是 1/3。 23 | 24 | 上述思想为什么错误呢?因为没有正确计算样本空间,导致原则一计算错误。有两个孩子,那么样本空间为 4,即哥哥妹妹,哥哥弟弟,姐姐妹妹,姐姐弟弟这四种情况。已知有一个男孩,那么排除姐姐妹妹这种情况,所以样本空间变成 3。另一个孩子也是男孩只有哥哥弟弟这 1 种情况,所以概率为 1/3。 25 | 26 | 为什么计算样本空间会出错呢?因为我们忽略了条件概率,即混淆了下面两个问题: 27 | 28 | 这个家庭只有一个孩子,这个孩子是男孩的概率是多少? 29 | 30 | 这个家庭有两个孩子,其中一个是男孩,另一个孩子是男孩的概率是多少? 31 | 32 | 根据原则二,概率问题是连续的,不可以把上述两个问题混淆。第二个问题需要用条件概率,即求一个孩子是男孩的条件下,另一个也是男孩的概率。运用条件概率的公式也很好算,就不多说了。 33 | 34 | 通过这个问题,读者应该理解两个概率计算原则的关系了,最具有迷惑性的就是条件概率的忽视。为了不要被迷惑,最简单的办法就是把所有可能结果穷举出来。 35 | 36 | 最后,对于此问题我见过一个很奇葩的质疑:如果这两个孩子是双胞胎,不存在年龄上的差异怎么办? 37 | 38 | 我竟然觉得有那么一丝道理!但其实,我们只是通过年龄差异来表示两个孩子的独立性,也就是说即便两个孩子同性,也有两种可能。所以不要用双胞胎抬杠了。 39 | 40 | 41 | ### 二、生日悖论 42 | 43 | 生日悖论是由这样一个问题引出的:一个屋子里需要有多少人,才能使得存在至少两个人生日是同一天的概率达到 50%? 44 | 45 | 答案是 23 个人,也就是说房子里如果有 23 个人,那么就有 50% 的概率会存在两个人生日相同。这个结论看起来不可思议,所以被称为悖论。按照直觉,要得到 50% 的概率,起码得有 183 个人吧,因为一年有 365 天呀?其实不是的,觉得这个结论不可思议主要有两个思维误区: 46 | 47 | **第一个误区是误解「存在」这个词的含义。** 48 | 49 | 读者可能认为,如果 23 个人中出现相同生日的概率就能达到 50%,是不是意味着: 50 | 51 | 假设现在屋子里坐着 22 个人,然后我走进去,那么有 50% 的概率我可以找到一个人和我生日相同。但这怎么可能呢? 52 | 53 | 并不是的,你这种想法是以自我为中心,而题目的概率是在描述整体。也就是说「存在」的含义是指 23 人中的任意两个人,涉及排列组合,大概率和你没啥关系。 54 | 55 | 如果你非要计算存在和自己生日相同的人的概率是多少,可以这样计算: 56 | 57 | 1 - P(22 个人都和我的生日不同) = 1 -(364/365)^22 = 0.06 58 | 59 | 这样计算得到的结果是不是看起来合理多了?生日悖论计算对象的不是某一个人,而是一个整体,其中包含了所有人的排列组合,它们的概率之和当然会大得多。 60 | 61 | **第二个误区是认为概率是线性变化的。** 62 | 63 | 读者可能认为,如果 23 个人中出现相同生日的概率就能达到 50%,是不是意味着 46 个人的概率就能达到 100%? 64 | 65 | 不是的,就像中奖率 50% 的游戏,你玩两次的中奖率就是 100% 吗?显然不是,你玩两次的中奖率是 75%: 66 | 67 | $P(两次能中奖) = P(第一次就中了) + P(第一次没中但第二次中了) = 1/2 + 1/2*1/2 = 75\%$ 68 | 69 | 那么换到生日悖论也是一个道理,概率不是简单叠加,而要考虑一个连续的过程,所以这个结论并没有什么不合常理之处。 70 | 71 | 那为什么只要 23 个人出现相同生日的概率就能大于 50% 了呢?我们先计算 23 个人生日都唯一(不重复)的概率。只有 1 个人的时候,生日唯一的概率是 $365/365$,2 个人时,生日唯一的概率是 $365/365 × 364/365$,以此类推可知 23 人的生日都唯一的概率: 72 | 73 | ![](../pictures/概率问题/p.png) 74 | 75 | 算出来大约是 0.493,所以存在相同生日的概率就是 0.507,差不多就是 50% 了。实际上,按照这个算法,当人数达到 70 时,存在两个人生日相同的概率就上升到了 99.9%,基本可以认为是 100% 了。所以从概率上说,一个几十人的小团体中存在生日相同的人真没啥稀奇的。 76 | 77 | ### 三、三门问题 78 | 79 | 这个游戏很经典了:游戏参与者面对三扇门,其中两扇门后面是山羊,一扇门后面是跑车。参与者只要随便选一扇门,门后面的东西就归他(跑车的价值当然更大)。但是主持人决定帮一下参与者:在他选择之后,先不急着打开这扇门,而是由主持人打开剩下两扇门中的一扇,展示其中的山羊(主持人知道每扇门后面是什么),然后给参与者一次换门的机会,此时参与者应该换门还是不换门呢? 80 | 81 | 为了防止第一次看到这个问题的读者迷惑,再具体描述一下这个问题: 82 | 83 | 你是游戏参与者,现在有门 1,2,3,假设你随机选择了门 1,然后主持人打开了门 3 告诉你那后面是山羊。现在,你是坚持你最初的选择门 1,还是选择换成门 2 呢? 84 | 85 | ![](../pictures/概率问题/sanmen.png) 86 | 87 | 答案是应该换门,换门之后抽到跑车的概率是 2/3,不换的话是 1/3。又一次反直觉,感觉换不换的中奖概率应该都一样啊,因为最后肯定就剩两个门,一个是羊,一个是跑车,这是事实,所以不管选哪个的概率不都是 1/2 吗? 88 | 89 | 类似前面说的男孩女孩问题,最简单稳妥的方法就是把所有可能结果穷举出来: 90 | 91 | ![穷举树](../pictures/概率问题/tree.png) 92 | 93 | 很容易看到选择换门中奖的概率是 2/3,不换的话是 1/3。 94 | 95 | 关于这个问题还有更简单的方法:主持人开门实际上在「浓缩」概率。一开始你选择到跑车的概率当然是 1/3,剩下两个门中包含跑车的概率当然是 2/3,这没啥可说的。但是主持人帮你排除了一个含有山羊的门,相当于把那 2/3 的概率浓缩到了剩下的这一扇门上。那么,你说你是抱着原来那扇 1/3 的门,还是换成那扇经过「浓缩」的 2/3 概率的门呢? 96 | 97 | 再直观一点,假设你三选一,剩下 2 扇门,再给你加入 98 扇装山羊的门,把这 100 扇门随机打乱,问你换不换?肯定不换对吧,这明摆着把概率稀释了,肯定抱着原来的那扇门是最可能中跑车的。再假设,初始有 100 扇门,你选了一扇,然后主持人在剩下 99 扇门中帮你排除 98 个山羊,问你换不换一扇门?肯定换对吧,你手上那扇门是 1%,另一扇门是 99%,或者也可以这样理解,不换只是选择了 1 扇门,换门相当于选择了 99 扇门,这样结果很明显了吧? 98 | 99 | 以上思想,也许有的读者都思考过,下面我们思考这样一个问题:假设你在决定是否换门的时候,小明破门而入,要求帮你做出选择。他完全不知道之前发生的事,他只知道面前有两扇门,一扇是跑车一扇是山羊,那么他抽中跑车的概率是多大? 100 | 101 | 当然是 1/2,这也是很多人做错三门问题的根本原因。类似生日悖论,人们总是容易以自我为中心,通过这个小明的视角来计算是否换门,这显然会进入误区。 102 | 103 | 就好比有两个箱子,一号箱子有 4 个黑球 2 个红球,二号箱子有 2 个黑球 4 个红球,随便选一个箱子,随便摸一个球,问你摸出红球的概率。 104 | 105 | 对于不知情的小明,他会随机选择一个箱子,随机摸球,摸到红球的概率是:1/2 × 2/6 + 1/2 × 4/6 = 1/2 106 | 107 | 对于知情的你,你知道在二号箱子摸球概率大,所以只在二号箱摸,摸到红球的概率是:0 × 2/6 + 1 × 4/6 = 2/3 108 | 109 | 三门问题是有指导意义的。比如你蒙选择题,先蒙了 A,后来灵机一动排除了 B 和 C,请问你是否要把 A 换成 D?答案是,换! 110 | 111 | 也许读者会问,如果只排除了一个答案,比如说 B,那么我是否应该把 A 换成 C 或者 D 呢?答案是,换! 112 | 113 | 因为按照刚才「浓缩」概率这个思想,只要进行了排除,都是在进行「浓缩」,均摊下来肯定比你一开始蒙的那个答案概率 1/4 高。比如刚才的例子,C 和 D 的正确概率都是 3/8,而你开始蒙的 A 只有 1/4。 114 | 115 | 当然,运用此策略蒙题的前提是你真的抓瞎,真的随机乱选答案,这样概率才能作为最后的杀手锏。 116 | 117 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 118 | 119 | ![labuladong](../pictures/labuladong.jpg) 120 | 121 | 122 | [上一篇:信封嵌套问题](../算法思维系列/信封嵌套问题.md) 123 | 124 | [下一篇:洗牌算法](../算法思维系列/洗牌算法.md) 125 | 126 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/前缀和技巧.md: -------------------------------------------------------------------------------- 1 | # 前缀和技巧 2 | 3 | 今天来聊一道简单却十分巧妙的算法问题:算出一共有几个和为 k 的子数组。 4 | 5 | ![](../pictures/%E5%89%8D%E7%BC%80%E5%92%8C/title.png) 6 | 7 | 那我把所有子数组都穷举出来,算它们的和,看看谁的和等于 k 不就行了。 8 | 9 | 关键是,**如何快速得到某个子数组的和呢**,比如说给你一个数组 `nums`,让你实现一个接口 `sum(i, j)`,这个接口要返回 `nums[i..j]` 的和,而且会被多次调用,你怎么实现这个接口呢? 10 | 11 | 因为接口要被多次调用,显然不能每次都去遍历 `nums[i..j]`,有没有一种快速的方法在 O(1) 时间内算出 `nums[i..j]` 呢?这就需要**前缀和**技巧了。 12 | 13 | ### 一、什么是前缀和 14 | 15 | 前缀和的思路是这样的,对于一个给定的数组 `nums`,我们额外开辟一个前缀和数组进行预处理: 16 | 17 | ```java 18 | int n = nums.length; 19 | // 前缀和数组 20 | int[] preSum = new int[n + 1]; 21 | preSum[0] = 0; 22 | for (int i = 0; i < n; i++) 23 | preSum[i + 1] = preSum[i] + nums[i]; 24 | ``` 25 | 26 | ![](../pictures/%E5%89%8D%E7%BC%80%E5%92%8C/1.jpg) 27 | 28 | 这个前缀和数组 `preSum` 的含义也很好理解,`preSum[i]` 就是 `nums[0..i-1]` 的和。那么如果我们想求 `nums[i..j]` 的和,只需要一步操作 `preSum[j+1]-preSum[i]` 即可,而不需要重新去遍历数组了。 29 | 30 | 回到这个子数组问题,我们想求有多少个子数组的和为 k,借助前缀和技巧很容易写出一个解法: 31 | 32 | ```java 33 | int subarraySum(int[] nums, int k) { 34 | int n = nums.length; 35 | // 构造前缀和 36 | int[] sum = new int[n + 1]; 37 | sum[0] = 0; 38 | for (int i = 0; i < n; i++) 39 | sum[i + 1] = sum[i] + nums[i]; 40 | 41 | int ans = 0; 42 | // 穷举所有子数组 43 | for (int i = 1; i <= n; i++) 44 | for (int j = 0; j < i; j++) 45 | // sum of nums[j..i-1] 46 | if (sum[i] - sum[j] == k) 47 | ans++; 48 | 49 | return ans; 50 | } 51 | ``` 52 | 53 | 这个解法的时间复杂度 $O(N^2)$ 空间复杂度 $O(N)$,并不是最优的解法。不过通过这个解法理解了前缀和数组的工作原理之后,可以使用一些巧妙的办法把时间复杂度进一步降低。 54 | 55 | ### 二、优化解法 56 | 57 | 前面的解法有嵌套的 for 循环: 58 | 59 | ```java 60 | for (int i = 1; i <= n; i++) 61 | for (int j = 0; j < i; j++) 62 | if (sum[i] - sum[j] == k) 63 | ans++; 64 | ``` 65 | 66 | 第二层 for 循环在干嘛呢?翻译一下就是,**在计算,有几个 `j` 能够使得 `sum[i]` 和 `sum[j]` 的差为 k。**毎找到一个这样的 `j`,就把结果加一。 67 | 68 | 我们可以把 if 语句里的条件判断移项,这样写: 69 | 70 | ```java 71 | if (sum[j] == sum[i] - k) 72 | ans++; 73 | ``` 74 | 75 | 优化的思路是:**我直接记录下有几个 `sum[j]` 和 `sum[i] - k` 相等,直接更新结果,就避免了内层的 for 循环**。我们可以用哈希表,在记录前缀和的同时记录该前缀和出现的次数。 76 | 77 | ```java 78 | int subarraySum(int[] nums, int k) { 79 | int n = nums.length; 80 | // map:前缀和 -> 该前缀和出现的次数 81 | HashMap 82 | preSum = new HashMap<>(); 83 | // base case 84 | preSum.put(0, 1); 85 | 86 | int ans = 0, sum0_i = 0; 87 | for (int i = 0; i < n; i++) { 88 | sum0_i += nums[i]; 89 | // 这是我们想找的前缀和 nums[0..j] 90 | int sum0_j = sum0_i - k; 91 | // 如果前面有这个前缀和,则直接更新答案 92 | if (preSum.containsKey(sum0_j)) 93 | ans += preSum.get(sum0_j); 94 | // 把前缀和 nums[0..i] 加入并记录出现次数 95 | preSum.put(sum0_i, 96 | preSum.getOrDefault(sum0_i, 0) + 1); 97 | } 98 | return ans; 99 | } 100 | ``` 101 | 102 | 比如说下面这个情况,需要前缀和 8 就能找到和为 k 的子数组了,之前的暴力解法需要遍历数组去数有几个 8,而优化解法借助哈希表可以直接得知有几个前缀和为 8。 103 | 104 | ![](../pictures/%E5%89%8D%E7%BC%80%E5%92%8C/2.jpg) 105 | 106 | 这样,就把时间复杂度降到了 $O(N)$,是最优解法了。 107 | 108 | ### 三、总结 109 | 110 | 前缀和不难,却很有用,主要用于处理数组区间的问题。 111 | 112 | 比如说,让你统计班上同学考试成绩在不同分数段的百分比,也可以利用前缀和技巧: 113 | 114 | ```java 115 | int[] scores; // 存储着所有同学的分数 116 | // 试卷满分 150 分 117 | int[] count = new int[150 + 1] 118 | // 记录每个分数有几个同学 119 | for (int score : scores) 120 | count[score]++ 121 | // 构造前缀和 122 | for (int i = 1; i < count.length; i++) 123 | count[i] = count[i] + count[i-1]; 124 | ``` 125 | 126 | 这样,给你任何一个分数段,你都能通过前缀和相减快速计算出这个分数段的人数,百分比也就很容易计算了。 127 | 128 | 但是,稍微复杂一些的算法问题,不止考察简单的前缀和技巧。比如本文探讨的这道题目,就需要借助前缀和的思路做进一步的优化,借助哈希表去除不必要的嵌套循环。可见对题目的理解和细节的分析能力对于算法的优化是至关重要的。 129 | 130 | 希望本文对你有帮助。 131 | 132 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 133 | 134 | ![labuladong](../pictures/labuladong.jpg) 135 | 136 | 137 | [上一篇:烧饼排序](../算法思维系列/烧饼排序.md) 138 | 139 | [下一篇:字符串乘法](../算法思维系列/字符串乘法.md) 140 | 141 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/区间交集问题.md: -------------------------------------------------------------------------------- 1 | # 区间交集问题 2 | 3 | 本文是区间系列问题的第三篇,前两篇分别讲了区间的最大不相交子集和重叠区间的合并,今天再写一个算法,可以快速找出两组区间的交集。 4 | 5 | 先看下题目,LeetCode 第 986 题就是这个问题: 6 | 7 | ![title](../pictures/intersection/title.png) 8 | 9 | 题目很好理解,就是让你找交集,注意区间都是闭区间。 10 | 11 | ### 思路 12 | 13 | 解决区间问题的思路一般是先排序,以便操作,不过题目说已经排好序了,那么可以用两个索引指针在 `A` 和 `B` 中游走,把交集找出来,代码大概是这样的: 14 | 15 | ```python 16 | # A, B 形如 [[0,2],[5,10]...] 17 | def intervalIntersection(A, B): 18 | i, j = 0, 0 19 | res = [] 20 | while i < len(A) and j < len(B): 21 | # ... 22 | j += 1 23 | i += 1 24 | return res 25 | ``` 26 | 27 | 不难,我们先老老实实分析一下各种情况。 28 | 29 | 首先,**对于两个区间**,我们用 `[a1,a2]` 和 `[b1,b2]` 表示在 `A` 和 `B` 中的两个区间,那么什么情况下这两个区间**没有交集**呢: 30 | 31 | ![](../pictures/intersection/1.jpg) 32 | 33 | 只有这两种情况,写成代码的条件判断就是这样: 34 | 35 | ```python 36 | if b2 < a1 or a2 < b1: 37 | [a1,a2] 和 [b1,b2] 无交集 38 | ``` 39 | 40 | 那么,什么情况下,两个区间存在交集呢?根据命题的否定,上面逻辑的否命题就是存在交集的条件: 41 | 42 | ```python 43 | # 不等号取反,or 也要变成 and 44 | if b2 >= a1 and a2 >= b1: 45 | [a1,a2] 和 [b1,b2] 存在交集 46 | ``` 47 | 48 | 接下来,两个区间存在交集的情况有哪些呢?穷举出来: 49 | 50 | ![](../pictures/intersection/2.jpg) 51 | 52 | 这很简单吧,就这四种情况而已。那么接下来思考,这几种情况下,交集是否有什么共同点呢? 53 | 54 | ![](../pictures/intersection/3.jpg) 55 | 56 | 我们惊奇地发现,交集区间是有规律的!如果交集区间是 `[c1,c2]`,那么 `c1=max(a1,b1)`,`c2=min(a2,b2)`!这一点就是寻找交集的核心,我们把代码更进一步: 57 | 58 | ```python 59 | while i < len(A) and j < len(B): 60 | a1, a2 = A[i][0], A[i][1] 61 | b1, b2 = B[j][0], B[j][1] 62 | if b2 >= a1 and a2 >= b1: 63 | res.append([max(a1, b1), min(a2, b2)]) 64 | # ... 65 | ``` 66 | 67 | 最后一步,我们的指针 `i` 和 `j` 肯定要前进(递增)的,什么时候应该前进呢? 68 | 69 | ![](../pictures/intersection/4.gif) 70 | 71 | 结合动画示例就很好理解了,是否前进,只取决于 `a2` 和 `b2` 的大小关系: 72 | 73 | ```python 74 | while i < len(A) and j < len(B): 75 | # ... 76 | if b2 < a2: 77 | j += 1 78 | else: 79 | i += 1 80 | ``` 81 | 82 | ### 代码 83 | 84 | ```python 85 | # A, B 形如 [[0,2],[5,10]...] 86 | def intervalIntersection(A, B): 87 | i, j = 0, 0 # 双指针 88 | res = [] 89 | while i < len(A) and j < len(B): 90 | a1, a2 = A[i][0], A[i][1] 91 | b1, b2 = B[j][0], B[j][1] 92 | # 两个区间存在交集 93 | if b2 >= a1 and a2 >= b1: 94 | # 计算出交集,加入 res 95 | res.append([max(a1, b1), min(a2, b2)]) 96 | # 指针前进 97 | if b2 < a2: j += 1 98 | else: i += 1 99 | return res 100 | ``` 101 | 102 | 总结一下,区间类问题看起来都比较复杂,情况很多难以处理,但实际上通过观察各种不同情况之间的共性可以发现规律,用简洁的代码就能处理。 103 | 104 | 另外,区间问题没啥特别厉害的奇技淫巧,其操作也朴实无华,但其应用却十分广泛,接之前的几篇文章: 105 | 106 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 107 | 108 | ![labuladong](../pictures/labuladong.jpg) 109 | 110 | 111 | [上一篇:区间调度之区间合并问题](../算法思维系列/区间调度问题之区间合并.md) 112 | 113 | [下一篇:信封嵌套问题](../算法思维系列/信封嵌套问题.md) 114 | 115 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/区间调度问题之区间合并.md: -------------------------------------------------------------------------------- 1 | # 区间调度问题之区间合并 2 | 3 | 上篇文章用贪心算法解决了区间调度问题:给你很多区间,让你求其中的最大不重叠子集。 4 | 5 | 其实对于区间相关的问题,还有很多其他类型,本文就来讲讲区间合并问题(Merge Interval)。 6 | 7 | LeetCode 第 56 题就是一道相关问题,题目很好理解: 8 | 9 | ![title](../pictures/mergeInterval/title.png) 10 | 11 | 我们解决区间问题的一般思路是先排序,然后观察规律。 12 | 13 | ### 一、思路 14 | 15 | 一个区间可以表示为 `[start, end]`,前文聊的区间调度问题,需要按 `end` 排序,以便满足贪心选择性质。而对于区间合并问题,其实按 `end` 和 `start` 排序都可以,不过为了清晰起见,我们选择按 `start` 排序。 16 | 17 | ![1](../pictures/mergeInterval/1.jpg) 18 | 19 | **显然,对于几个相交区间合并后的结果区间 `x`,`x.start` 一定是这些相交区间中 `start` 最小的,`x.end` 一定是这些相交区间中 `end` 最大的。** 20 | 21 | ![2](../pictures/mergeInterval/2.jpg) 22 | 23 | 由于已经排了序,`x.start` 很好确定,求 `x.end` 也很容易,可以类比在数组中找最大值的过程: 24 | 25 | ```java 26 | int max_ele = arr[0]; 27 | for (int i = 1; i < arr.length; i++) 28 | max_ele = max(max_ele, arr[i]); 29 | return max_ele; 30 | ``` 31 | 32 | ### 二、代码 33 | 34 | ```python 35 | # intervals 形如 [[1,3],[2,6]...] 36 | def merge(intervals): 37 | if not intervals: return [] 38 | # 按区间的 start 升序排列 39 | intervals.sort(key=lambda intv: intv[0]) 40 | res = [] 41 | res.append(intervals[0]) 42 | 43 | for i in range(1, len(intervals)): 44 | curr = intervals[i] 45 | # res 中最后一个元素的引用 46 | last = res[-1] 47 | if curr[0] <= last[1]: 48 | # 找到最大的 end 49 | last[1] = max(last[1], curr[1]) 50 | else: 51 | # 处理下一个待合并区间 52 | res.append(curr) 53 | return res 54 | ``` 55 | 56 | 看下动画就一目了然了: 57 | 58 | ![3](../pictures/mergeInterval/3.gif) 59 | 60 | 至此,区间合并问题就解决了。本文篇幅短小,因为区间合并只是区间问题的一个类型,后续还有一些区间问题。本想把所有问题类型都总结在一篇文章,但有读者反应,长文只会收藏不会看... 所以还是分成小短文吧,读者有什么看法可以在留言板留言交流。 61 | 62 | 本文终,希望对你有帮助。 63 | 64 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 65 | 66 | ![labuladong](../pictures/labuladong.jpg) 67 | 68 | 69 | [上一篇:FloodFill算法详解及应用](../算法思维系列/FloodFill算法详解及应用.md) 70 | 71 | [下一篇:区间调度之区间交集问题](../算法思维系列/区间交集问题.md) 72 | 73 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/字符串乘法.md: -------------------------------------------------------------------------------- 1 | # 字符串乘法 2 | 3 | 对于比较小的数字,做运算可以直接使用编程语言提供的运算符,但是如果相乘的两个因数非常大,语言提供的数据类型可能就会溢出。一种替代方案就是,运算数以字符串的形式输入,然后模仿我们小学学习的乘法算术过程计算出结果,并且也用字符串表示。 4 | 5 | ![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/title.png) 6 | 7 | 需要注意的是,`num1` 和 `num2` 可以非常长,所以不可以把他们直接转成整型然后运算,唯一的思路就是模仿我们手算乘法。 8 | 9 | 比如说我们手算 `123 × 45`,应该会这样计算: 10 | 11 | ![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/1.jpg) 12 | 13 | 计算 `123 × 5`,再计算 `123 × 4`,最后错一位相加。这个流程恐怕小学生都可以熟练完成,但是你是否能**把这个运算过程进一步机械化**,写成一套算法指令让没有任何智商的计算机来执行呢? 14 | 15 | 你看这个简单过程,其中涉及乘法进位,涉及错位相加,还涉及加法进位;而且还有一些不易察觉的问题,比如说两位数乘以两位数,结果可能是四位数,也可能是三位数,你怎么想出一个标准化的处理方式?这就是算法的魅力,如果没有计算机思维,简单的问题可能都没办法自动化处理。 16 | 17 | 首先,我们这种手算方式还是太「高级」了,我们要再「低级」一点,`123 × 5` 和 `123 × 4` 的过程还可以进一步分解,最后再相加: 18 | 19 | ![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/2.jpg) 20 | 21 | 现在 `123` 并不大,如果是个很大的数字的话,是无法直接计算乘积的。我们可以用一个数组在底下接收相加结果: 22 | 23 | ![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/3.jpg) 24 | 25 | 整个计算过程大概是这样,**有两个指针 `i,j` 在 `num1` 和 `num2` 上游走,计算乘积,同时将乘积叠加到 `res` 的正确位置**: 26 | 27 | ![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/4.gif) 28 | 29 | 现在还有一个关键问题,如何将乘积叠加到 `res` 的正确位置,或者说,如何通过 `i,j` 计算 `res` 的对应索引呢? 30 | 31 | 其实,细心观察之后就发现,**`num1[i]` 和 `num2[j]` 的乘积对应的就是 `res[i+j]` 和 `res[i+j+1]` 这两个位置**。 32 | 33 | ![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/6.jpg) 34 | 35 | 明白了这一点,就可以用代码模仿出这个计算过程了: 36 | 37 | ```java 38 | string multiply(string num1, string num2) { 39 | int m = num1.size(), n = num2.size(); 40 | // 结果最多为 m + n 位数 41 | vector res(m + n, 0); 42 | // 从个位数开始逐位相乘 43 | for (int i = m - 1; i >= 0; i--) 44 | for (int j = n - 1; j >= 0; j--) { 45 | int mul = (num1[i]-'0') * (num2[j]-'0'); 46 | // 乘积在 res 对应的索引位置 47 | int p1 = i + j, p2 = i + j + 1; 48 | // 叠加到 res 上 49 | int sum = mul + res[p2]; 50 | res[p2] = sum % 10; 51 | res[p1] += sum / 10; 52 | } 53 | // 结果前缀可能存的 0(未使用的位) 54 | int i = 0; 55 | while (i < res.size() && res[i] == 0) 56 | i++; 57 | // 将计算结果转化成字符串 58 | string str; 59 | for (; i < res.size(); i++) 60 | str.push_back('0' + res[i]); 61 | 62 | return str.size() == 0 ? "0" : str; 63 | } 64 | ``` 65 | 66 | 至此,字符串乘法算法就完成了。 67 | 68 | **总结一下**,我们习以为常的一些思维方式,在计算机看来是非常难以做到的。比如说我们习惯的算术流程并不复杂,但是如果让你再进一步,翻译成代码逻辑,并不简单。算法需要将计算流程再简化,通过边算边叠加的方式来得到结果。 69 | 70 | 俗话教育我们,不要陷入思维定式,不要程序化,要发散思维,要创新。但我觉得程序化并不是坏事,可以大幅提高效率,减小失误率。算法不就是一套程序化的思维吗,只有程序化才能让计算机帮助我们解决复杂问题呀! 71 | 72 | 也许算法就是一种**寻找思维定式的思维**吧,希望本文对你有帮助。 73 | 74 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 75 | 76 | ![labuladong](../pictures/labuladong.jpg) 77 | 78 | 79 | [上一篇:前缀和技巧](../算法思维系列/前缀和技巧.md) 80 | 81 | [下一篇:FloodFill算法详解及应用](../算法思维系列/FloodFill算法详解及应用.md) 82 | 83 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/常用的位操作.md: -------------------------------------------------------------------------------- 1 | # 常用的位操作 2 | 3 | 本文分两部分,第一部分列举几个有趣的位操作,第二部分讲解算法中常用的 n & (n - 1) 操作,顺便把用到这个技巧的算法题列出来讲解一下。因为位操作很简单,所以假设读者已经了解与、或、异或这三种基本操作。 4 | 5 | 位操作(Bit Manipulation)可以玩出很多奇技淫巧,但是这些技巧大部分都过于晦涩,没必要深究,读者只要记住一些有用的操作即可。 6 | 7 | ### 一、几个有趣的位操作 8 | 9 | 1. 利用或操作 `|` 和空格将英文字符转换为小写 10 | 11 | ```c 12 | ('a' | ' ') = 'a' 13 | ('A' | ' ') = 'a' 14 | ``` 15 | 16 | 2. 利用与操作 `&` 和下划线将英文字符转换为大写 17 | 18 | ```c 19 | ('b' & '_') = 'B' 20 | ('B' & '_') = 'B' 21 | ``` 22 | 23 | 3. 利用异或操作 `^` 和空格进行英文字符大小写互换 24 | 25 | ```c 26 | ('d' ^ ' ') = 'D' 27 | ('D' ^ ' ') = 'd' 28 | ``` 29 | 30 | PS:以上操作能够产生奇特效果的原因在于 ASCII 编码。字符其实就是数字,恰巧这些字符对应的数字通过位运算就能得到正确的结果,有兴趣的读者可以查 ASCII 码表自己算算,本文就不展开讲了。 31 | 32 | 4. 判断两个数是否异号 33 | 34 | ```c 35 | int x = -1, y = 2; 36 | bool f = ((x ^ y) < 0); // true 37 | 38 | int x = 3, y = 2; 39 | bool f = ((x ^ y) < 0); // false 40 | ``` 41 | 42 | PS:这个技巧还是很实用的,利用的是补码编码的符号位。如果不用位运算来判断是否异号,需要使用 if else 分支,还挺麻烦的。读者可能想利用乘积或者商来判断两个数是否异号,但是这种处理方式可能造成溢出,从而出现错误。(关于补码编码和溢出,参见前文) 43 | 44 | 5. 交换两个数 45 | 46 | ```c 47 | int a = 1, b = 2; 48 | a ^= b; 49 | b ^= a; 50 | a ^= b; 51 | // 现在 a = 2, b = 1 52 | ``` 53 | 54 | 6. 加一 55 | 56 | ```c 57 | int n = 1; 58 | n = -~n; 59 | // 现在 n = 2 60 | ``` 61 | 62 | 7. 减一 63 | 64 | ```c 65 | int n = 2; 66 | n = ~-n; 67 | // 现在 n = 1 68 | ``` 69 | 70 | PS:上面这三个操作就纯属装逼用的,没啥实际用处,大家了解了解乐呵一下就行。 71 | 72 | ### 二、算法常用操作 n&(n-1) 73 | 74 | 这个操作是算法中常见的,作用是消除数字 n 的二进制表示中的最后一个 1。 75 | 76 | 看个图就很容易理解了: 77 | 78 | ![n](../pictures/%E4%BD%8D%E6%93%8D%E4%BD%9C/1.png) 79 | 80 | 1. 计算汉明权重(Hamming Weight) 81 | 82 | ![title](../pictures/%E4%BD%8D%E6%93%8D%E4%BD%9C/title.png) 83 | 84 | 就是让你返回 n 的二进制表示中有几个 1。因为 n & (n - 1) 可以消除最后一个 1,所以可以用一个循环不停地消除 1 同时计数,直到 n 变成 0 为止。 85 | 86 | ```cpp 87 | int hammingWeight(uint32_t n) { 88 | int res = 0; 89 | while (n != 0) { 90 | n = n & (n - 1); 91 | res++; 92 | } 93 | return res; 94 | } 95 | ``` 96 | 97 | 1. 判断一个数是不是 2 的指数 98 | 99 | 一个数如果是 2 的指数,那么它的二进制表示一定只含有一个 1: 100 | 101 | ```cpp 102 | 2^0 = 1 = 0b0001 103 | 2^1 = 2 = 0b0010 104 | 2^2 = 4 = 0b0100 105 | ``` 106 | 107 | 如果使用位运算技巧就很简单了(注意运算符优先级,括号不可以省略): 108 | 109 | ```cpp 110 | bool isPowerOfTwo(int n) { 111 | if (n <= 0) return false; 112 | return (n & (n - 1)) == 0; 113 | } 114 | ``` 115 | 116 | 以上便是一些有趣/常用的位操作。其实位操作的技巧很多,有一个叫做 Bit Twiddling Hacks 的外国网站收集了几乎所有位操作的黑科技玩法,感兴趣的读者可以点击「阅读原文」按钮查看。 117 | 118 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 119 | 120 | ![labuladong](../pictures/labuladong.jpg) 121 | 122 | 123 | [上一篇:twoSum问题的核心思想](../算法思维系列/twoSum问题的核心思想.md) 124 | 125 | [下一篇:拆解复杂问题:实现计算器](../数据结构系列/实现计算器.md) 126 | 127 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/烧饼排序.md: -------------------------------------------------------------------------------- 1 | # 烧饼排序 2 | 3 | 烧饼排序是个很有意思的实际问题:假设盘子上有 `n` 块**面积大小不一**的烧饼,你如何用一把锅铲进行若干次翻转,让这些烧饼的大小有序(小的在上,大的在下)? 4 | 5 | ![](../pictures/pancakeSort/1.jpg) 6 | 7 | 设想一下用锅铲翻转一堆烧饼的情景,其实是有一点限制的,我们每次只能将最上面的若干块饼子翻转: 8 | 9 | ![](../pictures/pancakeSort/2.png) 10 | 11 | 我们的问题是,**如何使用算法得到一个翻转序列,使得烧饼堆变得有序**? 12 | 13 | 首先,需要把这个问题抽象,用数组来表示烧饼堆: 14 | 15 | ![](../pictures/pancakeSort/title.png) 16 | 17 | 如何解决这个问题呢?其实类似上篇文章 [递归反转链表的一部分](../数据结构系列/递归反转链表的一部分.md),这也是需要**递归思想**的。 18 | 19 | ### 一、思路分析 20 | 21 | 为什么说这个问题有递归性质呢?比如说我们需要实现这样一个函数: 22 | 23 | ```java 24 | // cakes 是一堆烧饼,函数会将前 n 个烧饼排序 25 | void sort(int[] cakes, int n); 26 | ``` 27 | 28 | 如果我们找到了前 `n` 个烧饼中最大的那个,然后设法将这个饼子翻转到最底下: 29 | 30 | ![](../pictures/pancakeSort/3.jpg) 31 | 32 | 那么,原问题的规模就可以减小,递归调用 `pancakeSort(A, n-1)` 即可: 33 | 34 | ![](../pictures/pancakeSort/4.jpg) 35 | 36 | 接下来,对于上面的这 `n - 1` 块饼,如何排序呢?还是先从中找到最大的一块饼,然后把这块饼放到底下,再递归调用 `pancakeSort(A, n-1-1)`…… 37 | 38 | 你看,这就是递归性质,总结一下思路就是: 39 | 40 | 1、找到 `n` 个饼中最大的那个。 41 | 42 | 2、把这个最大的饼移到最底下。 43 | 44 | 3、递归调用 `pancakeSort(A, n - 1)`。 45 | 46 | base case:`n == 1` 时,排序 1 个饼时不需要翻转。 47 | 48 | 那么,最后剩下个问题,**如何设法将某块烧饼翻到最后呢**? 49 | 50 | 其实很简单,比如第 3 块饼是最大的,我们想把它换到最后,也就是换到第 `n` 块。可以这样操作: 51 | 52 | 1、用锅铲将前 3 块饼翻转一下,这样最大的饼就翻到了最上面。 53 | 54 | 2、用锅铲将前 `n` 块饼全部翻转,这样最大的饼就翻到了第 `n` 块,也就是最后一块。 55 | 56 | 以上两个流程理解之后,基本就可以写出解法了,不过题目要求我们写出具体的反转操作序列,这也很简单,只要在每次翻转烧饼时记录下来就行了。 57 | 58 | ### 二、代码实现 59 | 60 | 只要把上述的思路用代码实现即可,唯一需要注意的是,数组索引从 0 开始,而我们要返回的结果是从 1 开始算的。 61 | 62 | ```java 63 | // 记录反转操作序列 64 | LinkedList res = new LinkedList<>(); 65 | 66 | List pancakeSort(int[] cakes) { 67 | sort(cakes, cakes.length); 68 | return res; 69 | } 70 | 71 | void sort(int[] cakes, int n) { 72 | // base case 73 | if (n == 1) return; 74 | 75 | // 寻找最大饼的索引 76 | int maxCake = 0; 77 | int maxCakeIndex = 0; 78 | for (int i = 0; i < n; i++) 79 | if (cakes[i] > maxCake) { 80 | maxCakeIndex = i; 81 | maxCake = cakes[i]; 82 | } 83 | 84 | // 第一次翻转,将最大饼翻到最上面 85 | reverse(cakes, 0, maxCakeIndex); 86 | res.add(maxCakeIndex + 1); 87 | // 第二次翻转,将最大饼翻到最下面 88 | reverse(cakes, 0, n - 1); 89 | res.add(n); 90 | 91 | // 递归调用 92 | sort(cakes, n - 1); 93 | } 94 | 95 | void reverse(int[] arr, int i, int j) { 96 | while (i < j) { 97 | int temp = arr[i]; 98 | arr[i] = arr[j]; 99 | arr[j] = temp; 100 | i++; j--; 101 | } 102 | } 103 | ``` 104 | 105 | 通过刚才的详细解释,这段代码应该是很清晰了。 106 | 107 | 算法的时间复杂度很容易计算,因为递归调用的次数是 `n`,每次递归调用都需要一次 for 循环,时间复杂度是 O(n),所以总的复杂度是 O(n^2)。 108 | 109 | **最后,我们可以思考一个问题​**:按照我们这个思路,得出的操作序列长度应该为​ `2(n - 1)`,因为每次递归都要进行 2 次翻转并记录操作,总共有 `n` 层递归,但由于 base case 直接返回结果,不进行翻转,所以最终的操作序列长度应该是固定的 `2(n - 1)`。 110 | 111 | 显然,这个结果不是最优的(最短的),比如说一堆煎饼 `[3,2,4,1]`,我们的算法得到的翻转序列是 `[3,4,2,3,1,2]`,但是最快捷的翻转方法应该是 `[2,3,4]`: 112 | 113 | 初始状态 :[3,2,4,1] 114 | 翻前 2 个:[2,3,4,1] 115 | 翻前 3 个:[4,3,2,1] 116 | 翻前 4 个:[1,2,3,4] 117 | 118 | 如果要求你的算法计算排序烧饼的**最短**操作序列,你该如何计算呢?或者说,解决这种求最优解法的问题,核心思路什么,一定需要使用什么算法技巧呢? 119 | 120 | 不妨分享一下你的思考。 121 | 122 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 123 | 124 | ![labuladong](../pictures/labuladong.jpg) 125 | 126 | 127 | [上一篇:拆解复杂问题:实现计算器](../数据结构系列/实现计算器.md) 128 | 129 | [下一篇:前缀和技巧](../算法思维系列/前缀和技巧.md) 130 | 131 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /算法思维系列/算法学习之路.md: -------------------------------------------------------------------------------- 1 | # 算法学习之路 2 | 3 | 之前发的那篇关于框架性思维的文章,我也发到了不少其他圈子,受到了大家的普遍好评,这一点我真的没想到,首先感谢大家的认可,我会更加努力,写出通俗易懂的算法文章。 4 | 5 | 有很多朋友问我数据结构和算法到底该怎么学,尤其是很多朋友说自己是「小白」,感觉这些东西好难啊,就算看了之前的「框架思维」,也感觉自己刷题乏力,希望我能聊聊我从一个非科班小白一路是怎么学过来的。 6 | 7 | 首先要给怀有这样疑问的朋友鼓掌,因为你现在已经「知道自己不知道」,而且开始尝试学习、刷题、寻求帮助,能做到这一点本身就是及其困难的。 8 | 9 | 关于「框架性思维」,对于一个小白来说,可能暂时无法完全理解(如果你能理解,说明你水平已经不错啦,不是小白啦)。就像软件工程,对于我这种没带过项目的人来说,感觉其内容枯燥乏味,全是废话,但是对于一个带过团队的人,他就会觉得软件工程里的每一句话都是精华。暂时不太理解没关系,留个印象,功夫到了很快就明白了。 10 | 11 | 下面写一写我一路过来的一些经验。如果你已经看过很多「如何高效刷题」「如何学习算法」的文章,却还是没有开始行动并坚持下去,本文的第五点就是写给你的。 12 | 13 | 我觉得之所以有时候认为自己是「小白」,是由于知识某些方面的空白造成的。具体到数据结构的学习,无非就是两个问题搞得不太清楚:**这是啥?有啥用?** 14 | 15 | 举个例子,比如说你看到了「栈」这个名词,老师可能会讲这些关键词:先进后出、函数堆栈等等。但是,对于初学者,这些描述属于文学词汇,没有实际价值,没有解决最基本的两个问题。如何回答这两个基本问题呢?回答「这是啥」需要看教科书,回答「有啥用」需要刷算法题。 16 | 17 | **一、这是啥?** 18 | 19 | 这个问题最容易解决,就像一层窗户纸,你只要随便找本书看两天,自己动手实现一个「队列」「栈」之类的数据结构,就能捅破这层窗户纸。 20 | 21 | 这时候你就能理解「框架思维」文章中的前半部分了:数据结构无非就是数组、链表为骨架的一些特定操作而已;每个数据结构实现的功能无非增删查改罢了。 22 | 23 | 比如说「列队」这个数据结构,无非就是基于数组或者链表,实现 enqueue 和 dequeue 两个方法。这两个方法就是增和删呀,连查和改的方法都不需要。 24 | 25 | **二、有啥用?** 26 | 27 | 解决这个问题,就涉及算法的设计了,是个持久战,需要经常进行抽象思考,刷算法题,培养「计算机思维」。 28 | 29 | 之前的文章讲了,算法就是对数据结构准确而巧妙的运用。常用算法问题也就那几大类,算法题无非就是不断变换场景,给那几个算法框架套上不同的皮。刷题,就是在锻炼你的眼力,看你能不能看穿问题表象揪出相应的解法框架。 30 | 31 | 比如说,让你求解一个迷宫,你要把这个问题层层抽象:迷宫 -> 图的遍历 -> N 叉树的遍历 -> 二叉树的遍历。然后让框架指导你写具体的解法。 32 | 33 | 抽象问题,直击本质,是刷题中你需要刻意培养的能力。 34 | 35 | **三、如何看书** 36 | 37 | 直接推荐一本公认的好书,《算法第 4 版》,我一般简写成《算法4》。不要蜻蜓点水,这本书你能选择性的看上 50%,基本上就达到平均水平了。别怕这本书厚,因为起码有三分之一不用看,下面讲讲怎么看这本书。 38 | 39 | 看书仍然遵循递归的思想:自顶向下,逐步求精。 40 | 41 | 这本书知识结构合理,讲解也清楚,所以可以按顺序学习。**书中正文的算法代码一定要亲自敲一遍**,因为这些真的是扎实的基础,要认真理解。不要以为自己看一遍就看懂了,不动手的话理解不了的。但是,开头部分的基础可以酌情跳过;书中的数学证明,如不影响对算法本身的理解,完全可以跳过;章节最后的练习题,也可以全部跳过。这样一来,这本书就薄了很多。 42 | 43 | 相信读者现在已经认可了「框架性思维」的重要性,这种看书方式也是一种框架性策略,抓大放小,着重理解整体的知识架构,而忽略证明、练习题这种细节问题,即**保持自己对新知识的好奇心,避免陷入无限的细节被劝退。** 44 | 45 | 当然,《算法4》到后面的内容也比较难了,比如那几个著名的串算法,以及正则表达式算法。这些属于「经典算法」,看个人接受能力吧,单说刷 LeetCode 的话,基本用不上,量力而行即可。 46 | 47 | **四、如何刷题** 48 | 49 | 首先声明一下,**算法和数学水平没关系,和编程语言也没关系**,你爱用什么语言用什么。算法,主要是培养一种新的思维方式。所谓「计算机思维」,就跟你考驾照一样,你以前骑自行车,有一套自行车的规则和技巧,现在你开汽车,就需要适应并练习开汽车的规则和技巧。 50 | 51 | LeetCode 上的算法题和前面说的「经典算法」不一样,我们权且称为「解闷算法」吧,因为很多题目都比较有趣,有种在做奥数题或者脑筋急转弯的感觉。比如说,让你用队列实现一个栈,或者用栈实现一个队列,以及不用加号做加法,开脑洞吧? 52 | 53 | 当然,这些问题虽然看起来无厘头,实际生活中也用不到,但是想解决这些问题依然要靠数据结构以及对基础知识的理解,也许这就是很多公司面试都喜欢出这种「智力题」的原因。下面说几点技巧吧。 54 | 55 | **尽量刷英文版的 LeetCode**,中文版的“力扣”是阉割版,不仅很多题目没有答案,而且连个讨论区都没有。英文版的是真的很良心了,很多问题都有官方解答,详细易懂。而且讨论区(Discuss)也沉淀了大量优质内容,甚至好过官方解答。真正能打开你思路的,很可能是讨论区各路大神的思路荟萃。 56 | 57 | PS:**如果有的英文题目实在看不懂,有个小技巧**,你在题目页面的 url 里加一个 -cn,即 https://leetcode.com/xxx 改成 https://leetcode-cn.com/xxx,这样就能切换到相应的中文版页面查看。 58 | 59 | 对于初学者,**强烈建议从 Explore 菜单里最下面的 Learn 开始刷**,这个专题就是专门教你学习数据结构和基本算法的,教学篇和相应的练习题结合,不要太良心。 60 | 61 | 最近 Learn 专题里新增了一些内容,我们挑数据结构相关的内容刷就行了,像 Ruby,Machine Learning 就没必要刷了。刷完 Learn 专题的基础内容,基本就有能力去 Explore 菜单的 Interview 专题刷面试题,或者去 Problem 菜单,在真正的题海里遨游了。 62 | 63 | 无论刷 Explore 还是 Problems 菜单,**最好一个分类一个分类的刷,不要蜻蜓点水**。比如说这几天就刷链表,刷完链表再去连刷几天二叉树。这样做是为了帮助你提取「框架」。一旦总结出针对一类问题的框架,解决同类问题可谓是手到擒来。 64 | 65 | **五、道理我都懂,还是不能坚持下去** 66 | 67 | 这其实无关算法了,还是老生常谈的执行力的问题。不说什么破鸡汤了,我觉得**解决办法就是「激起欲望」**,注意我说的是欲望,而不是常说的兴趣,拿我自己说说吧。 68 | 69 | 半年前我开始刷题,目的和大部分人都一样的,就是为毕业找工作做准备。只不过,大部分人是等到临近毕业了才开始刷,而我离毕业还有一阵子。这不是炫耀我多有觉悟,而是我承认自己的极度平凡。 70 | 71 | 首先,我真的想找到一份不错的工作(谁都想吧?),我想要高薪呀!否则我在朋友面前,女神面前放下的骚话,最终都会反过来啪啪地打我的脸。我也是要恰饭,要面子,要虚荣心的嘛。赚钱,虚荣心,足以激起我的欲望了。 72 | 73 | 但是,我不擅长 deadline 突击,我理解东西真的慢,所以干脆笨鸟先飞了。智商不够,拿时间来补,我没能力两个月突击,干脆拉长战线,打他个两年游击战,我还不信耗不死算法这个强敌。事实证明,你如果认真学习一个月,就能够取得肉眼可见的进步了。 74 | 75 | 现在,我依然在坚持刷题,而且为了另外一个原因,这个公众号。我没想到自己的文字竟然能够帮助到他人,甚至能得到认可。这也是虚荣心啊,我不能让读者失望啊,我想让更多的人认可(夸)我呀! 76 | 77 | 以上,不光是坚持刷算法题吧,很多场景都适用。执行力是要靠「欲望」支撑的,我也是一凡人,只有那些看得见摸得着的东西才能使我快乐呀。读者不妨也尝试把刷题学习和自己的切身利益联系起来,这恐怕是坚持下去最简单直白的理由了。 78 | 79 | **致力于把算法讲清楚!欢迎关注我的微信公众号 labuladong,查看更多通俗易懂的文章**: 80 | 81 | ![labuladong](../pictures/labuladong.png) 82 | 83 | [上一篇:队列实现栈\|栈实现队列](../数据结构系列/队列实现栈栈实现队列.md) 84 | 85 | [下一篇:回溯算法详解](../算法思维系列/回溯算法详解修订版.md) 86 | 87 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/README.md: -------------------------------------------------------------------------------- 1 | # 高频面试系列 2 | 3 | 8 说了,本章都是高频面试题,配合前面的动态规划系列,祝各位马到成功! 4 | 5 | 欢迎关注我的公众号 labuladong,方便获得最新的优质文章: 6 | 7 | ![labuladong二维码](../pictures/qrcode.jpg) -------------------------------------------------------------------------------- /高频面试系列/koko偷香蕉.md: -------------------------------------------------------------------------------- 1 | # 如何运用二分查找算法 2 | 3 | 二分查找到底有能运用在哪里? 4 | 5 | 最常见的就是教科书上的例子,在**有序数组**中搜索给定的某个目标值的索引。再推广一点,如果目标值存在重复,修改版的二分查找可以返回目标值的左侧边界索引或者右侧边界索引。 6 | 7 | PS:以上提到的三种二分查找算法形式在前文「二分查找详解」有代码详解,如果没看过强烈建议看看。 8 | 9 | 抛开有序数组这个枯燥的数据结构,二分查找如何运用到实际的算法问题中呢?当搜索空间有序的时候,就可以通过二分搜索「剪枝」,大幅提升效率。 10 | 11 | 说起来玄乎得很,本文先用一个具体的「Koko 吃香蕉」的问题来举个例子。 12 | 13 | ### 一、问题分析 14 | 15 | ![](../pictures/二分应用/title1.png) 16 | 17 | 也就是说,Koko 每小时最多吃一堆香蕉,如果吃不下的话留到下一小时再吃;如果吃完了这一堆还有胃口,也只会等到下一小时才会吃下一堆。在这个条件下,让我们确定 Koko 吃香蕉的**最小速度**(根/小时)。 18 | 19 | 如果直接给你这个情景,你能想到哪里能用到二分查找算法吗?如果没有见过类似的问题,恐怕是很难把这个问题和二分查找联系起来的。 20 | 21 | 那么我们先抛开二分查找技巧,想想如何暴力解决这个问题呢? 22 | 23 | 首先,算法要求的是「`H` 小时内吃完香蕉的最小速度」,我们不妨称为 `speed`,请问 `speed` 最大可能为多少,最少可能为多少呢? 24 | 25 | 显然最少为 1,最大为 `max(piles)`,因为一小时最多只能吃一堆香蕉。那么暴力解法就很简单了,只要从 1 开始穷举到 `max(piles)`,一旦发现发现某个值可以在 `H` 小时内吃完所有香蕉,这个值就是最小速度: 26 | 27 | ```java 28 | int minEatingSpeed(int[] piles, int H) { 29 | // piles 数组的最大值 30 | int max = getMax(piles); 31 | for (int speed = 1; speed < max; speed++) { 32 | // 以 speed 是否能在 H 小时内吃完香蕉 33 | if (canFinish(piles, speed, H)) 34 | return speed; 35 | } 36 | return max; 37 | } 38 | ``` 39 | 40 | 注意这个 for 循环,就是在**连续的空间线性搜索,这就是二分查找可以发挥作用的标志**。由于我们要求的是最小速度,所以可以用一个**搜索左侧边界的二分查找**来代替线性搜索,提升效率: 41 | 42 | ```java 43 | int minEatingSpeed(int[] piles, int H) { 44 | // 套用搜索左侧边界的算法框架 45 | int left = 1, right = getMax(piles) + 1; 46 | while (left < right) { 47 | // 防止溢出 48 | int mid = left + (right - left) / 2; 49 | if (canFinish(piles, mid, H)) { 50 | right = mid; 51 | } else { 52 | left = mid + 1; 53 | } 54 | } 55 | return left; 56 | } 57 | ``` 58 | 59 | PS:如果对于这个二分查找算法的细节问题有疑问,建议看下前文「二分查找详解」搜索左侧边界的算法模板,这里不展开了。 60 | 61 | 剩下的辅助函数也很简单,可以一步步拆解实现: 62 | 63 | ```java 64 | // 时间复杂度 O(N) 65 | boolean canFinish(int[] piles, int speed, int H) { 66 | int time = 0; 67 | for (int n : piles) { 68 | time += timeOf(n, speed); 69 | } 70 | return time <= H; 71 | } 72 | 73 | int timeOf(int n, int speed) { 74 | return (n / speed) + ((n % speed > 0) ? 1 : 0); 75 | } 76 | 77 | int getMax(int[] piles) { 78 | int max = 0; 79 | for (int n : piles) 80 | max = Math.max(n, max); 81 | return max; 82 | } 83 | ``` 84 | 85 | 至此,借助二分查找技巧,算法的时间复杂度为 O(NlogN)。 86 | 87 | ### 二、扩展延伸 88 | 89 | 类似的,再看一道运输问题: 90 | 91 | ![](../pictures/二分应用/title2.png) 92 | 93 | 要在 `D` 天内运输完所有货物,货物不可分割,如何确定运输的最小载重呢(下文称为 `cap`)? 94 | 95 | 其实本质上和 Koko 吃香蕉的问题一样的,首先确定 `cap` 的最小值和最大值分别为 `max(weights)` 和 `sum(weights)`。 96 | 97 | 我们要求**最小载重**,所以可以用搜索左侧边界的二分查找算法优化线性搜索: 98 | 99 | ```java 100 | // 寻找左侧边界的二分查找 101 | int shipWithinDays(int[] weights, int D) { 102 | // 载重可能的最小值 103 | int left = getMax(weights); 104 | // 载重可能的最大值 + 1 105 | int right = getSum(weights) + 1; 106 | while (left < right) { 107 | int mid = left + (right - left) / 2; 108 | if (canFinish(weights, D, mid)) { 109 | right = mid; 110 | } else { 111 | left = mid + 1; 112 | } 113 | } 114 | return left; 115 | } 116 | 117 | // 如果载重为 cap,是否能在 D 天内运完货物? 118 | boolean canFinish(int[] w, int D, int cap) { 119 | int i = 0; 120 | for (int day = 0; day < D; day++) { 121 | int maxCap = cap; 122 | while ((maxCap -= w[i]) >= 0) { 123 | i++; 124 | if (i == w.length) 125 | return true; 126 | } 127 | } 128 | return false; 129 | } 130 | ``` 131 | 132 | 通过这两个例子,你是否明白了二分查找在实际问题中的应用? 133 | 134 | ```java 135 | for (int i = 0; i < n; i++) 136 | if (isOK(i)) 137 | return ans; 138 | ``` 139 | 140 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 141 | 142 | ![labuladong](../pictures/labuladong.jpg) 143 | 144 | 145 | [上一篇:如何计算编辑距离](../动态规划系列/编辑距离.md) 146 | 147 | [下一篇:如何高效解决接雨水问题](../高频面试系列/接雨水.md) 148 | 149 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/k个一组反转链表.md: -------------------------------------------------------------------------------- 1 | # 如何k个一组反转链表 2 | 3 | 之前的文章「递归反转链表的一部分」讲了如何递归地反转一部分链表,有读者就问如何迭代地反转链表,这篇文章解决的问题也需要反转链表的函数,我们不妨就用迭代方式来解决。 4 | 5 | 本文要解决「K 个一组反转链表」,不难理解: 6 | 7 | ![](../pictures/kgroup/title.png) 8 | 9 | 这个问题经常在面经中看到,而且 LeetCode 上难度是 Hard,它真的有那么难吗? 10 | 11 | 对于基本数据结构的算法问题其实都不难,只要结合特点一点点拆解分析,一般都没啥难点。下面我们就来拆解一下这个问题。 12 | 13 | ### 一、分析问题 14 | 15 | 首先,前文[学习数据结构的框架思维](../算法思维系列/学习数据结构和算法的框架思维.md)提到过,链表是一种兼具递归和迭代性质的数据结构,认真思考一下可以发现**这个问题具有递归性质**。 16 | 17 | 什么叫递归性质?直接上图理解,比如说我们对这个链表调用 `reverseKGroup(head, 2)`,即以 2 个节点为一组反转链表: 18 | 19 | ![](../pictures/kgroup/1.jpg) 20 | 21 | 如果我设法把前 2 个节点反转,那么后面的那些节点怎么处理?后面的这些节点也是一条链表,而且规模(长度)比原来这条链表小,这就叫**子问题**。 22 | 23 | ![](../pictures/kgroup/2.jpg) 24 | 25 | 我们可以直接递归调用 `reverseKGroup(cur, 2)`,因为子问题和原问题的结构完全相同,这就是所谓的递归性质。 26 | 27 | 发现了递归性质,就可以得到大致的算法流程: 28 | 29 | **1、先反转以 `head` 开头的 `k` 个元素**。 30 | 31 | ![](../pictures/kgroup/3.jpg) 32 | 33 | **2、将第 `k + 1` 个元素作为 `head` 递归调用 `reverseKGroup` 函数**。 34 | 35 | ![](../pictures/kgroup/4.jpg) 36 | 37 | **3、将上述两个过程的结果连接起来**。 38 | 39 | ![](../pictures/kgroup/5.jpg) 40 | 41 | 整体思路就是这样了,最后一点值得注意的是,递归函数都有个 base case,对于这个问题是什么呢? 42 | 43 | 题目说了,如果最后的元素不足 `k` 个,就保持不变。这就是 base case,待会会在代码里体现。 44 | 45 | ### 二、代码实现 46 | 47 | 首先,我们要实现一个 `reverse` 函数反转一个区间之内的元素。在此之前我们再简化一下,给定链表头结点,如何反转整个链表? 48 | 49 | ```java 50 | // 反转以 a 为头结点的链表 51 | ListNode reverse(ListNode a) { 52 | ListNode pre, cur, nxt; 53 | pre = null; cur = a; nxt = a; 54 | while (cur != null) { 55 | nxt = cur.next; 56 | // 逐个结点反转 57 | cur.next = pre; 58 | // 更新指针位置 59 | pre = cur; 60 | cur = nxt; 61 | } 62 | // 返回反转后的头结点 63 | return pre; 64 | } 65 | ``` 66 | 67 | ![](../pictures/kgroup/8.gif) 68 | 69 | 这次使用迭代思路来实现的,借助动画理解应该很容易。 70 | 71 | 「反转以 `a` 为头结点的链表」其实就是「反转 `a` 到 null 之间的结点」,那么如果让你「反转 `a` 到 `b` 之间的结点」,你会不会? 72 | 73 | 只要更改函数签名,并把上面的代码中 `null` 改成 `b` 即可: 74 | 75 | ```java 76 | /** 反转区间 [a, b) 的元素,注意是左闭右开 */ 77 | ListNode reverse(ListNode a, ListNode b) { 78 | ListNode pre, cur, nxt; 79 | pre = null; cur = a; nxt = a; 80 | // while 终止的条件改一下就行了 81 | while (cur != b) { 82 | nxt = cur.next; 83 | cur.next = pre; 84 | pre = cur; 85 | cur = nxt; 86 | } 87 | // 返回反转后的头结点 88 | return pre; 89 | } 90 | ``` 91 | 92 | 现在我们迭代实现了反转部分链表的功能,接下来就按照之前的逻辑编写 `reverseKGroup` 函数即可: 93 | 94 | ```java 95 | ListNode reverseKGroup(ListNode head, int k) { 96 | if (head == null) return null; 97 | // 区间 [a, b) 包含 k 个待反转元素 98 | ListNode a, b; 99 | a = b = head; 100 | for (int i = 0; i < k; i++) { 101 | // 不足 k 个,不需要反转,base case 102 | if (b == null) return head; 103 | b = b.next; 104 | } 105 | // 反转前 k 个元素 106 | ListNode newHead = reverse(a, b); 107 | // 递归反转后续链表并连接起来 108 | a.next = reverseKGroup(b, k); 109 | return newHead; 110 | } 111 | ``` 112 | 113 | 解释一下 `for` 循环之后的几句代码,注意 `reverse` 函数是反转区间 `[a, b)`,所以情形是这样的: 114 | 115 | ![](../pictures/kgroup/6.jpg) 116 | 117 | 递归部分就不展开了,整个函数递归完成之后就是这个结果,完全符合题意: 118 | 119 | ![](../pictures/kgroup/7.jpg) 120 | 121 | ### 三、最后说两句 122 | 123 | 从阅读量上看,基本数据结构相关的算法文章看的人都不多,我想说这是要吃亏的。 124 | 125 | 大家喜欢看动态规划相关的问题,可能因为面试很常见,但就我个人理解,很多算法思想都是源于数据结构的。我们公众号的成名之作之一,「学习数据结构的框架思维」就提过,什么动规、回溯、分治算法,其实都是树的遍历,树这种结构它不就是个多叉链表吗?你能处理基本数据结构的问题,解决一般的算法问题应该也不会太费事。 126 | 127 | 那么如何分解问题、发现递归性质呢?这个只能多练习,也许后续可以专门写一篇文章来探讨一下,本文就到此为止吧,希望对大家有帮助! 128 | 129 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 130 | 131 | ![labuladong](../pictures/labuladong.jpg) 132 | 133 | 134 | [上一篇:如何寻找最长回文子串](../高频面试系列/最长回文子串.md) 135 | 136 | [下一篇:如何判定括号合法性](../高频面试系列/合法括号判定.md) 137 | 138 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/一行代码解决的智力题.md: -------------------------------------------------------------------------------- 1 | # 一行代码就能解决的算法题 2 | 3 | 下文是我在 LeetCode 刷题过程中总结的三道有趣的「脑筋急转弯」题目,可以使用算法编程解决,但只要稍加思考,就能找到规律,直接想出答案。 4 | 5 | ### 一、Nim 游戏 6 | 7 | 游戏规则是这样的:你和你的朋友面前有一堆石子,你们轮流拿,一次至少拿一颗,最多拿三颗,谁拿走最后一颗石子谁获胜。 8 | 9 | 假设你们都很聪明,由你第一个开始拿,请你写一个算法,输入一个正整数 n,返回你是否能赢(true 或 false)。 10 | 11 | 比如现在有 4 颗石子,算法应该返回 false。因为无论你拿 1 颗 2 颗还是 3 颗,对方都能一次性拿完,拿走最后一颗石子,所以你一定会输。 12 | 13 | 首先,这道题肯定可以使用动态规划,因为显然原问题存在子问题,且子问题存在重复。但是因为你们都很聪明,涉及到你和对手的博弈,动态规划会比较复杂。 14 | 15 | **我们解决这种问题的思路一般都是反着思考**: 16 | 17 | 如果我能赢,那么最后轮到我取石子的时候必须要剩下 1~3 颗石子,这样我才能一把拿完。 18 | 19 | 如何营造这样的一个局面呢?显然,如果对手拿的时候只剩 4 颗石子,那么无论他怎么拿,总会剩下 1~3 颗石子,我就能赢。 20 | 21 | 如何逼迫对手面对 4 颗石子呢?要想办法,让我选择的时候还有 5~7 颗石子,这样的话我就有把握让对方不得不面对 4 颗石子。 22 | 23 | 如何营造 5~7 颗石子的局面呢?让对手面对 8 颗石子,无论他怎么拿,都会给我剩下 5~7 颗,我就能赢。 24 | 25 | 这样一直循环下去,我们发现只要踩到 4 的倍数,就落入了圈套,永远逃不出 4 的倍数,而且一定会输。所以这道题的解法非常简单: 26 | 27 | ```cpp 28 | bool canWinNim(int n) { 29 | // 如果上来就踩到 4 的倍数,那就认输吧 30 | // 否则,可以把对方控制在 4 的倍数,必胜 31 | return n % 4 != 0; 32 | } 33 | ``` 34 | 35 | 36 | ### 二、石头游戏 37 | 38 | 游戏规则是这样的:你和你的朋友面前有一排石头堆,用一个数组 piles 表示,piles[i] 表示第 i 堆石子有多少个。你们轮流拿石头,一次拿一堆,但是只能拿走最左边或者最右边的石头堆。所有石头被拿完后,谁拥有的石头多,谁获胜。 39 | 40 | **假设你们都很聪明**,由你第一个开始拿,请你写一个算法,输入一个数组 piles,返回你是否能赢(true 或 false)。 41 | 42 | 注意,石头的堆的数量为偶数,所以你们两人拿走的堆数一定是相同的。石头的总数为奇数,也就是你们最后不可能拥有相同多的石头,一定有胜负之分。 43 | 44 | 举个例子,`piles=[2, 1, 9, 5]`,你先拿,可以拿 2 或者 5,你选择 2。 45 | 46 | `piles=[1, 9, 5]`,轮到对手,可以拿 1 或 5,他选择 5。 47 | 48 | `piles=[1, 9]` 轮到你拿,你拿 9。 49 | 50 | 最后,你的对手只能拿 1 了。 51 | 52 | 这样下来,你总共拥有 `2 + 9 = 11` 颗石头,对手有 `5 + 1 = 6` 颗石头,你是可以赢的,所以算法应该返回 true。 53 | 54 | 你看到了,并不是简单的挑数字大的选,为什么第一次选择 2 而不是 5 呢?因为 5 后面是 9,你要是贪图一时的利益,就把 9 这堆石头暴露给对手了,那你就要输了。 55 | 56 | 这也是强调双方都很聪明的原因,算法也是求最优决策过程下你是否能赢。 57 | 58 | 这道题又涉及到两人的博弈,也可以用动态规划算法暴力试,比较麻烦。但我们只要对规则深入思考,就会大惊失色:只要你足够聪明,你是必胜无疑的,因为你是先手。 59 | 60 | ```java 61 | boolean stoneGame(int[] piles) { 62 | return true; 63 | } 64 | ``` 65 | 66 | 这是为什么呢,因为题目有两个条件很重要:一是石头总共有偶数堆,石头的总数是奇数。这两个看似增加游戏公平性的条件,反而使该游戏成为了一个割韭菜游戏。我们以 `piles=[2, 1, 9, 5]` 讲解,假设这四堆石头从左到右的索引分别是 1,2,3,4。 67 | 68 | 如果我们把这四堆石头按索引的奇偶分为两组,即第 1、3 堆和第 2、4 堆,那么这两组石头的数量一定不同,也就是说一堆多一堆少。因为石头的总数是奇数,不能被平分。 69 | 70 | 而作为第一个拿石头的人,你可以控制自己拿到所有偶数堆,或者所有的奇数堆。 71 | 72 | 你最开始可以选择第 1 堆或第 4 堆。如果你想要偶数堆,你就拿第 4 堆,这样留给对手的选择只有第 1、3 堆,他不管怎么拿,第 2 堆又会暴露出来,你就可以拿。同理,如果你想拿奇数堆,你就拿第 1 堆,留给对手的只有第 2、4 堆,他不管怎么拿,第 3 堆又给你暴露出来了。 73 | 74 | 也就是说,你可以在第一步就观察好,奇数堆的石头总数多,还是偶数堆的石头总数多,然后步步为营,就一切尽在掌控之中了。知道了这个漏洞,可以整一整不知情的同学了。 75 | 76 | ### 三、电灯开关问题 77 | 78 | 这个问题是这样描述的:有 n 盏电灯,最开始时都是关着的。现在要进行 n 轮操作: 79 | 80 | 第 1 轮操作是把每一盏电灯的开关按一下(全部打开)。 81 | 82 | 第 2 轮操作是把每两盏灯的开关按一下(就是按第 2,4,6... 盏灯的开关,它们被关闭)。 83 | 84 | 第 3 轮操作是把每三盏灯的开关按一下(就是按第 3,6,9... 盏灯的开关,有的被关闭,比如 3,有的被打开,比如 6)... 85 | 86 | 如此往复,直到第 n 轮,即只按一下第 n 盏灯的开关。 87 | 88 | 现在给你输入一个正整数 n 代表电灯的个数,问你经过 n 轮操作后,这些电灯有多少盏是亮的? 89 | 90 | 我们当然可以用一个布尔数组表示这些灯的开关情况,然后模拟这些操作过程,最后去数一下就能出结果。但是这样显得没有灵性,最好的解法是这样的: 91 | 92 | ```java 93 | int bulbSwitch(int n) { 94 | return (int)Math.sqrt(n); 95 | } 96 | ``` 97 | 98 | 什么?这个问题跟平方根有什么关系?其实这个解法挺精妙,如果没人告诉你解法,还真不好想明白。 99 | 100 | 首先,因为电灯一开始都是关闭的,所以某一盏灯最后如果是点亮的,必然要被按奇数次开关。 101 | 102 | 我们假设只有 6 盏灯,而且我们只看第 6 盏灯。需要进行 6 轮操作对吧,请问对于第 6 盏灯,会被按下几次开关呢?这不难得出,第 1 轮会被按,第 2 轮,第 3 轮,第 6 轮都会被按。 103 | 104 | 为什么第 1、2、3、6 轮会被按呢?因为 `6=1x6=2x3`。一般情况下,因子都是成对出现的,也就是说开关被按的次数一般是偶数次。但是有特殊情况,比如说总共有 16 盏灯,那么第 16 盏灯会被按几次? 105 | 106 | `16=1x16=2x8=4x4` 107 | 108 | 其中因子 4 重复出现,所以第 16 盏灯会被按 5 次,奇数次。现在你应该理解这个问题为什么和平方根有关了吧? 109 | 110 | 不过,我们不是要算最后有几盏灯亮着吗,这样直接平方根一下是啥意思呢?稍微思考一下就能理解了。 111 | 112 | 就假设现在总共有 16 盏灯,我们求 16 的平方根,等于 4,这就说明最后会有 4 盏灯亮着,它们分别是第 `1x1=1` 盏、第 `2x2=4` 盏、第 `3x3=9` 盏和第 `4x4=16` 盏。 113 | 114 | 就算有的 n 平方根结果是小数,强转成 int 型,也相当于一个最大整数上界,比这个上界小的所有整数,平方后的索引都是最后亮着的灯的索引。所以说我们直接把平方根转成整数,就是这个问题的答案。 115 | 116 | 117 | 118 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 119 | 120 | ![labuladong](../pictures/labuladong.jpg) 121 | 122 | 123 | [上一篇:Union-Find算法应用](../算法思维系列/UnionFind算法应用.md) 124 | 125 | [下一篇:二分查找高效判定子序列](../高频面试系列/二分查找判定子序列.md) 126 | 127 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/二分查找判定子序列.md: -------------------------------------------------------------------------------- 1 | # 二分查找高效判定子序列 2 | 3 | 二分查找本身不难理解,难在巧妙地运用二分查找技巧。对于一个问题,你可能都很难想到它跟二分查找有关,比如前文 [最长递增子序列](../动态规划系列/动态规划设计:最长递增子序列.md) 就借助一个纸牌游戏衍生出二分查找解法。 4 | 5 | 今天再讲一道巧用二分查找的算法问题:如何判定字符串 `s` 是否是字符串 `t` 的子序列(可以假定 `s` 长度比较小,且 `t` 的长度非常大)。举两个例子: 6 | 7 | 8 | s = "abc", t = "**a**h**b**gd**c**", return true. 9 | 10 | 11 | s = "axc", t = "ahbgdc", return false. 12 | 13 | 题目很容易理解,而且看起来很简单,但很难想到这个问题跟二分查找有关吧? 14 | 15 | ### 一、问题分析 16 | 17 | 首先,一个很简单的解法是这样的: 18 | 19 | ```cpp 20 | bool isSubsequence(string s, string t) { 21 | int i = 0, j = 0; 22 | while (i < s.size() && j < t.size()) { 23 | if (s[i] == t[j]) i++; 24 | j++; 25 | } 26 | return i == s.size(); 27 | } 28 | ``` 29 | 30 | 其思路也非常简单,利用双指针 `i, j` 分别指向 `s, t`,一边前进一边匹配子序列: 31 | 32 | ![gif](../pictures/%E5%AD%90%E5%BA%8F%E5%88%97/1.gif) 33 | 34 | 读者也许会问,这不就是最优解法了吗,时间复杂度只需 O(N),N 为 `t` 的长度。 35 | 36 | 是的,如果仅仅是这个问题,这个解法就够好了,**不过这个问题还有 follow up**: 37 | 38 | 如果给你一系列字符串 `s1,s2,...` 和字符串 `t`,你需要判定每个串 `s` 是否是 `t` 的子序列(可以假定 `s` 较短,`t` 很长)。 39 | 40 | ```java 41 | boolean[] isSubsequence(String[] sn, String t); 42 | ``` 43 | 44 | 你也许会问,这不是很简单吗,还是刚才的逻辑,加个 for 循环不就行了? 45 | 46 | 可以,但是此解法处理每个 `s` 时间复杂度仍然是 O(N),而如果巧妙运用二分查找,可以将时间复杂度降低,大约是 O(MlogN)。由于 N 相对 M 大很多,所以后者效率会更高。 47 | 48 | ### 二、二分思路 49 | 50 | 二分思路主要是对 `t` 进行预处理,用一个字典 `index` 将每个字符出现的索引位置按顺序存储下来: 51 | 52 | ```java 53 | int m = s.length(), n = t.length(); 54 | ArrayList[] index = new ArrayList[256]; 55 | // 先记下 t 中每个字符出现的位置 56 | for (int i = 0; i < n; i++) { 57 | char c = t.charAt(i); 58 | if (index[c] == null) 59 | index[c] = new ArrayList<>(); 60 | index[c].add(i); 61 | } 62 | ``` 63 | 64 | ![](../pictures/%E5%AD%90%E5%BA%8F%E5%88%97/2.jpg) 65 | 66 | 比如对于这个情况,匹配了 "ab",应该匹配 "c" 了: 67 | 68 | ![](../pictures/%E5%AD%90%E5%BA%8F%E5%88%97/1.jpg) 69 | 70 | 按照之前的解法,我们需要 `j` 线性前进扫描字符 "c",但借助 `index` 中记录的信息,**可以二分搜索 `index[c]` 中比 j 大的那个索引**,在上图的例子中,就是在 `[0,2,6]` 中搜索比 4 大的那个索引: 71 | 72 | ![](../pictures/%E5%AD%90%E5%BA%8F%E5%88%97/3.jpg) 73 | 74 | 这样就可以直接得到下一个 "c" 的索引。现在的问题就是,如何用二分查找计算那个恰好比 4 大的索引呢?答案是,寻找左侧边界的二分搜索就可以做到。 75 | 76 | ### 三、再谈二分查找 77 | 78 | 在前文 [二分查找详解](../算法思维系列/二分查找详解.md) 中,详解了如何正确写出三种二分查找算法的细节。二分查找返回目标值 `val` 的索引,对于搜索**左侧边界**的二分查找,有一个特殊性质: 79 | 80 | **当 `val` 不存在时,得到的索引恰好是比 `val` 大的最小元素索引**。 81 | 82 | 什么意思呢,就是说如果在数组 `[0,1,3,4]` 中搜索元素 2,算法会返回索引 2,也就是元素 3 的位置,元素 3 是数组中大于 2 的最小元素。所以我们可以利用二分搜索避免线性扫描。 83 | 84 | ```java 85 | // 查找左侧边界的二分查找 86 | int left_bound(ArrayList arr, int tar) { 87 | int lo = 0, hi = arr.size(); 88 | while (lo < hi) { 89 | int mid = lo + (hi - lo) / 2; 90 | if (tar > arr.get(mid)) { 91 | lo = mid + 1; 92 | } else { 93 | hi = mid; 94 | } 95 | } 96 | return lo; 97 | } 98 | ``` 99 | 100 | 以上就是搜索左侧边界的二分查找,等会儿会用到,其中的细节可以参见前文《二分查找详解》,这里不再赘述。 101 | 102 | ### 四、代码实现 103 | 104 | 这里以单个字符串 `s` 为例,对于多个字符串 `s`,可以把预处理部分抽出来。 105 | 106 | ```java 107 | boolean isSubsequence(String s, String t) { 108 | int m = s.length(), n = t.length(); 109 | // 对 t 进行预处理 110 | ArrayList[] index = new ArrayList[256]; 111 | for (int i = 0; i < n; i++) { 112 | char c = t.charAt(i); 113 | if (index[c] == null) 114 | index[c] = new ArrayList<>(); 115 | index[c].add(i); 116 | } 117 | 118 | // 串 t 上的指针 119 | int j = 0; 120 | // 借助 index 查找 s[i] 121 | for (int i = 0; i < m; i++) { 122 | char c = s.charAt(i); 123 | // 整个 t 压根儿没有字符 c 124 | if (index[c] == null) return false; 125 | int pos = left_bound(index[c], j); 126 | // 二分搜索区间中没有找到字符 c 127 | if (pos == index[c].size()) return false; 128 | // 向前移动指针 j 129 | j = index[c].get(pos) + 1; 130 | } 131 | return true; 132 | } 133 | ``` 134 | 135 | 算法执行的过程是这样的: 136 | 137 | ![](../pictures/%E5%AD%90%E5%BA%8F%E5%88%97/2.gif) 138 | 139 | 可见借助二分查找,算法的效率是可以大幅提升的。 140 | 141 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 142 | 143 | ![labuladong](../pictures/labuladong.jpg) 144 | 145 | 146 | [上一篇:一行代码就能解决的算法题](../高频面试系列/一行代码解决的智力题.md) 147 | 148 | [下一篇:Linux的进程、线程、文件描述符是什么](../技术/linux进程.md) 149 | 150 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/合法括号判定.md: -------------------------------------------------------------------------------- 1 | # 如何判定括号合法性 2 | 3 | 对括号的合法性判断是一个很常见且实用的问题,比如说我们写的代码,编辑器和编译器都会检查括号是否正确闭合。而且我们的代码可能会包含三种括号 `[](){}`,判断起来有一点难度。 4 | 5 | 本文就来聊一道关于括号合法性判断的算法题,相信能加深你对**栈**这种数据结构的理解。 6 | 7 | 题目很简单,输入一个字符串,其中包含 `[](){}` 六种括号,请你判断这个字符串组成的括号是否合法。 8 | 9 | ``` 10 | Input: "()[]{}" 11 | Output: true 12 | 13 | Input: "([)]" 14 | Output: false 15 | 16 | Input: "{[]}" 17 | Output: true 18 | ``` 19 | 20 | 解决这个问题之前,我们先降低难度,思考一下,**如果只有一种括号 `()`**,应该如何判断字符串组成的括号是否合法呢? 21 | 22 | ### 一、处理一种括号 23 | 24 | 字符串中只有圆括号,如果想让括号字符串合法,那么必须做到: 25 | 26 | **每个右括号 `)` 的左边必须有一个左括号 `(` 和它匹配**。 27 | 28 | 比如说字符串 `()))((` 中,中间的两个右括号**左边**就没有左括号匹配,所以这个括号组合是不合法的。 29 | 30 | 那么根据这个思路,我们可以写出算法: 31 | 32 | ```cpp 33 | bool isValid(string str) { 34 | // 待匹配的左括号数量 35 | int left = 0; 36 | for (char c : str) { 37 | if (c == '(') 38 | left++; 39 | else // 遇到右括号 40 | left--; 41 | 42 | if (left < 0) 43 | return false; 44 | } 45 | return left == 0; 46 | } 47 | ``` 48 | 如果只有圆括号,这样就能正确判断合法性。对于三种括号的情况,我一开始想模仿这个思路,定义三个变量 `left1`,`left2`,`left3` 分别处理每种括号,虽然要多写不少 if else 分支,但是似乎可以解决问题。 49 | 50 | 但实际上直接照搬这种思路是不行的,比如说只有一个括号的情况下 `(())` 是合法的,但是多种括号的情况下, `[(])` 显然是不合法的。 51 | 52 | 仅仅记录每种左括号出现的次数已经不能做出正确判断了,我们要加大存储的信息量,可以利用栈来模仿类似的思路。 53 | 54 | ### 二、处理多种括号 55 | 56 | 栈是一种先进后出的数据结构,处理括号问题的时候尤其有用。 57 | 58 | 我们这道题就用一个名为 `left` 的栈代替之前思路中的 `left` 变量,**遇到左括号就入栈,遇到右括号就去栈中寻找最近的左括号,看是否匹配**。 59 | 60 | ```cpp 61 | bool isValid(string str) { 62 | stack left; 63 | for (char c : str) { 64 | if (c == '(' || c == '{' || c == '[') 65 | left.push(c); 66 | else // 字符 c 是右括号 67 | if (!left.empty() && leftOf(c) == left.top()) 68 | left.pop(); 69 | else 70 | // 和最近的左括号不匹配 71 | return false; 72 | } 73 | // 是否所有的左括号都被匹配了 74 | return left.empty(); 75 | } 76 | 77 | char leftOf(char c) { 78 | if (c == '}') return '{'; 79 | if (c == ')') return '('; 80 | return '['; 81 | } 82 | ``` 83 | 84 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 85 | 86 | ![labuladong](../pictures/labuladong.jpg) 87 | 88 | 89 | [上一篇:如何k个一组反转链表](../高频面试系列/k个一组反转链表.md) 90 | 91 | [下一篇:如何寻找消失的元素](../高频面试系列/消失的元素.md) 92 | 93 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/如何去除有序数组的重复元素.md: -------------------------------------------------------------------------------- 1 | # 如何去除有序数组的重复元素 2 | 3 | 我们知道对于数组来说,在尾部插入、删除元素是比较高效的,时间复杂度是 O(1),但是如果在中间或者开头插入、删除元素,就会涉及数据的搬移,时间复杂度为 O(N),效率较低。 4 | 5 | 所以对于一般处理数组的算法问题,我们要尽可能只对数组尾部的元素进行操作,以避免额外的时间复杂度。 6 | 7 | 这篇文章讲讲如何对一个有序数组去重,先看下题目: 8 | 9 | ![](../pictures/%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E5%8E%BB%E9%87%8D/title.png) 10 | 11 | 显然,由于数组已经排序,所以重复的元素一定连在一起,找出它们并不难,但如果毎找到一个重复元素就立即删除它,就是在数组中间进行删除操作,整个时间复杂度是会达到 O(N^2)。而且题目要求我们原地修改,也就是说不能用辅助数组,空间复杂度得是 O(1)。 12 | 13 | 其实,**对于数组相关的算法问题,有一个通用的技巧:要尽量避免在中间删除元素,那我就想先办法把这个元素换到最后去**。这样的话,最终待删除的元素都拖在数组尾部,一个一个 pop 掉就行了,每次操作的时间复杂度也就降低到 O(1) 了。 14 | 15 | 按照这个思路呢,又可以衍生出解决类似需求的通用方式:双指针技巧。具体一点说,应该是快慢指针。 16 | 17 | 我们让慢指针 `slow` 走左后面,快指针 `fast` 走在前面探路,找到一个不重复的元素就告诉 `slow` 并让 `slow` 前进一步。这样当 `fast` 指针遍历完整个数组 `nums` 后,**`nums[0..slow]` 就是不重复元素,之后的所有元素都是重复元素**。 18 | 19 | ```java 20 | int removeDuplicates(int[] nums) { 21 | int n = nums.length; 22 | if (n == 0) return 0; 23 | int slow = 0, fast = 1; 24 | while (fast < n) { 25 | if (nums[fast] != nums[slow]) { 26 | slow++; 27 | // 维护 nums[0..slow] 无重复 28 | nums[slow] = nums[fast]; 29 | } 30 | fast++; 31 | } 32 | // 长度为索引 + 1 33 | return slow + 1; 34 | } 35 | ``` 36 | 37 | 看下算法执行的过程: 38 | 39 | ![](../pictures/%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E5%8E%BB%E9%87%8D/1.gif) 40 | 41 | 再简单扩展一下,如果给你一个有序链表,如何去重呢?其实和数组是一模一样的,唯一的区别是把数组赋值操作变成操作指针而已: 42 | 43 | ```java 44 | ListNode deleteDuplicates(ListNode head) { 45 | if (head == null) return null; 46 | ListNode slow = head, fast = head.next; 47 | while (fast != null) { 48 | if (fast.val != slow.val) { 49 | // nums[slow] = nums[fast]; 50 | slow.next = fast; 51 | // slow++; 52 | slow = slow.next; 53 | } 54 | // fast++ 55 | fast = fast.next; 56 | } 57 | // 断开与后面重复元素的连接 58 | slow.next = null; 59 | return head; 60 | } 61 | ``` 62 | 63 | ![](../pictures/%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E5%8E%BB%E9%87%8D/2.gif) 64 | 65 | **致力于把算法讲清楚!欢迎关注我的微信公众号 labuladong,查看更多通俗易懂的文章**: 66 | 67 | ![labuladong](../pictures/labuladong.png) 68 | 69 | [上一篇:如何高效解决接雨水问题](../高频面试系列/接雨水.md) 70 | 71 | [下一篇:如何寻找最长回文子串](../高频面试系列/最长回文子串.md) 72 | 73 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/打印素数.md: -------------------------------------------------------------------------------- 1 | # 如何高效寻找素数 2 | 3 | 素数的定义看起来很简单,如果一个数如果只能被 1 和它本身整除,那么这个数就是素数。 4 | 5 | 不要觉得素数的定义简单,恐怕没多少人真的能把素数相关的算法写得高效。比如让你写这样一个函数: 6 | 7 | ```java 8 | // 返回区间 [2, n) 中有几个素数 9 | int countPrimes(int n) 10 | 11 | // 比如 countPrimes(10) 返回 4 12 | // 因为 2,3,5,7 是素数 13 | ``` 14 | 15 | 你会如何写这个函数?我想大家应该会这样写: 16 | 17 | ```java 18 | int countPrimes(int n) { 19 | int count = 0; 20 | for (int i = 2; i < n; i++) 21 | if (isPrim(i)) count++; 22 | return count; 23 | } 24 | 25 | // 判断整数 n 是否是素数 26 | boolean isPrime(int n) { 27 | for (int i = 2; i < n; i++) 28 | if (n % i == 0) 29 | // 有其他整除因子 30 | return false; 31 | return true; 32 | } 33 | ``` 34 | 35 | 这样写的话时间复杂度 O(n^2),问题很大。**首先你用 isPrime 函数来辅助的思路就不够高效;而且就算你要用 isPrime 函数,这样写算法也是存在计算冗余的**。 36 | 37 | 先来简单说下**如果你要判断一个数是不是素数,应该如何写算法**。只需稍微修改一下上面的 isPrim 代码中的 for 循环条件: 38 | 39 | ```java 40 | boolean isPrime(int n) { 41 | for (int i = 2; i * i <= n; i++) 42 | ... 43 | } 44 | ``` 45 | 46 | 换句话说,`i` 不需要遍历到 `n`,而只需要到 `sqrt(n)` 即可。为什么呢,我们举个例子,假设 `n = 12`。 47 | 48 | ```java 49 | 12 = 2 × 6 50 | 12 = 3 × 4 51 | 12 = sqrt(12) × sqrt(12) 52 | 12 = 4 × 3 53 | 12 = 6 × 2 54 | ``` 55 | 56 | 可以看到,后两个乘积就是前面两个反过来,反转临界点就在 `sqrt(n)`。 57 | 58 | 换句话说,如果在 `[2,sqrt(n)]` 这个区间之内没有发现可整除因子,就可以直接断定 `n` 是素数了,因为在区间 `[sqrt(n),n]` 也一定不会发现可整除因子。 59 | 60 | 现在,`isPrime` 函数的时间复杂度降为 O(sqrt(N)),**但是我们实现 `countPrimes` 函数其实并不需要这个函数**,以上只是希望读者明白 `sqrt(n)` 的含义,因为等会还会用到。 61 | 62 | 63 | ### 高效实现 `countPrimes` 64 | 65 | 高效解决这个问题的核心思路是和上面的常规思路反着来: 66 | 67 | 首先从 2 开始,我们知道 2 是一个素数,那么 2 × 2 = 4, 3 × 2 = 6, 4 × 2 = 8... 都不可能是素数了。 68 | 69 | 然后我们发现 3 也是素数,那么 3 × 2 = 6, 3 × 3 = 9, 3 × 4 = 12... 也都不可能是素数了。 70 | 71 | 看到这里,你是否有点明白这个排除法的逻辑了呢?先看我们的第一版代码: 72 | 73 | ```java 74 | int countPrimes(int n) { 75 | boolean[] isPrim = new boolean[n]; 76 | // 将数组都初始化为 true 77 | Arrays.fill(isPrim, true); 78 | 79 | for (int i = 2; i < n; i++) 80 | if (isPrim[i]) 81 | // i 的倍数不可能是素数了 82 | for (int j = 2 * i; j < n; j += i) 83 | isPrim[j] = false; 84 | 85 | int count = 0; 86 | for (int i = 2; i < n; i++) 87 | if (isPrim[i]) count++; 88 | 89 | return count; 90 | } 91 | ``` 92 | 93 | 如果上面这段代码你能够理解,那么你已经掌握了整体思路,但是还有两个细微的地方可以优化。 94 | 95 | 首先,回想刚才判断一个数是否是素数的 `isPrime` 函数,由于因子的对称性,其中的 for 循环只需要遍历 `[2,sqrt(n)]` 就够了。这里也是类似的,我们外层的 for 循环也只需要遍历到 `sqrt(n)`: 96 | 97 | ```java 98 | for (int i = 2; i * i < n; i++) 99 | if (isPrim[i]) 100 | ... 101 | ``` 102 | 103 | 除此之外,很难注意到内层的 for 循环也可以优化。我们之前的做法是: 104 | 105 | ```java 106 | for (int j = 2 * i; j < n; j += i) 107 | isPrim[j] = false; 108 | ``` 109 | 110 | 这样可以把 `i` 的整数倍都标记为 `false`,但是仍然存在计算冗余。 111 | 112 | 比如 `n = 25`,`i = 4` 时算法会标记 4 × 2 = 8,4 × 3 = 12 等等数字,但是这两个数字已经被 `i = 2` 和 `i = 3` 的 2 × 4 和 3 × 4 标记了。 113 | 114 | 我们可以稍微优化一下,让 `j` 从 `i` 的平方开始遍历,而不是从 `2 * i` 开始: 115 | 116 | ```java 117 | for (int j = i * i; j < n; j += i) 118 | isPrim[j] = false; 119 | ``` 120 | 121 | 这样,素数计数的算法就高效实现了,其实这个算法有一个名字,叫做 Sieve of Eratosthenes。看下完整的最终代码: 122 | 123 | ```java 124 | int countPrimes(int n) { 125 | boolean[] isPrim = new boolean[n]; 126 | Arrays.fill(isPrim, true); 127 | for (int i = 2; i * i < n; i++) 128 | if (isPrim[i]) 129 | for (int j = i * i; j < n; j += i) 130 | isPrim[j] = false; 131 | 132 | int count = 0; 133 | for (int i = 2; i < n; i++) 134 | if (isPrim[i]) count++; 135 | 136 | return count; 137 | } 138 | ``` 139 | 140 | **该算法的时间复杂度比较难算**,显然时间跟这两个嵌套的 for 循环有关,其操作数应该是: 141 | 142 | n/2 + n/3 + n/5 + n/7 + ... 143 | = n × (1/2 + 1/3 + 1/5 + 1/7...) 144 | 145 | 括号中是素数的倒数。其最终结果是 O(N * loglogN),有兴趣的读者可以查一下该算法的时间复杂度证明。 146 | 147 | 以上就是素数算法相关的全部内容。怎么样,是不是看似简单的问题却有不少细节可以打磨呀? 148 | 149 | **致力于把算法讲清楚!欢迎关注我的微信公众号 labuladong,查看更多通俗易懂的文章**: 150 | 151 | ![labuladong](../pictures/labuladong.png) 152 | 153 | [上一篇:如何实现LRU算法](../高频面试系列/LRU算法.md) 154 | 155 | [下一篇:如何计算编辑距离](../动态规划系列/编辑距离.md) 156 | 157 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/最长回文子串.md: -------------------------------------------------------------------------------- 1 | # 如何寻找最长回文子串 2 | 3 | 回文串是面试常常遇到的问题(虽然问题本身没啥意义),本文就告诉你回文串问题的核心思想是什么。 4 | 5 | 首先,明确一下什:**回文串就是正着读和反着读都一样的字符串**。 6 | 7 | 比如说字符串 `aba` 和 `abba` 都是回文串,因为它们对称,反过来还是和本身一样。反之,字符串 `abac` 就不是回文串。 8 | 9 | 可以看到回文串的的长度可能是奇数,也可能是偶数,这就添加了回文串问题的难度,解决该类问题的核心是**双指针**。下面就通过一道最长回文子串的问题来具体理解一下回文串问题: 10 | 11 | ![](../pictures/回文/title.png) 12 | 13 | ```cpp 14 | string longestPalindrome(string s) {} 15 | ``` 16 | 17 | ### 一、思考 18 | 19 | 对于这个问题,我们首先应该思考的是,给一个字符串 `s`,如何在 `s` 中找到一个回文子串? 20 | 21 | 有一个很有趣的思路:既然回文串是一个正着反着读都一样的字符串,那么如果我们把 `s` 反转,称为 `s'`,然后在 `s` 和 `s'` 中寻找**最长公共子串**,这样应该就能找到最长回文子串。 22 | 23 | 比如说字符串 `abacd`,反过来是 `dcaba`,它的最长公共子串是 `aba`,也就是最长回文子串。 24 | 25 | 但是这个思路是错误的,比如说字符串 `aacxycaa`,反转之后是 `aacyxcaa`,最长公共子串是 `aac`,但是最长回文子串应该是 `aa`。 26 | 27 | 虽然这个思路不正确,但是**这种把问题转化为其他形式的思考方式是非常值得提倡的**。 28 | 29 | 下面,就来说一下正确的思路,如何使用双指针。 30 | 31 | **寻找回文串的问题核心思想是:从中间开始向两边扩散来判断回文串**。对于最长回文子串,就是这个意思: 32 | 33 | ```python 34 | for 0 <= i < len(s): 35 | 找到以 s[i] 为中心的回文串 36 | 更新答案 37 | ``` 38 | 39 | 但是呢,我们刚才也说了,回文串的长度可能是奇数也可能是偶数,如果是 `abba`这种情况,没有一个中心字符,上面的算法就没辙了。所以我们可以修改一下: 40 | 41 | ```python 42 | for 0 <= i < len(s): 43 | 找到以 s[i] 为中心的回文串 44 | 找到以 s[i] 和 s[i+1] 为中心的回文串 45 | 更新答案 46 | ``` 47 | 48 | PS:读者可能发现这里的索引会越界,等会会处理。 49 | 50 | ### 二、代码实现 51 | 52 | 按照上面的思路,先要实现一个函数来寻找最长回文串,这个函数是有点技巧的: 53 | 54 | ```cpp 55 | string palindrome(string& s, int l, int r) { 56 | // 防止索引越界 57 | while (l >= 0 && r < s.size() 58 | && s[l] == s[r]) { 59 | // 向两边展开 60 | l--; r++; 61 | } 62 | // 返回以 s[l] 和 s[r] 为中心的最长回文串 63 | return s.substr(l + 1, r - l - 1); 64 | } 65 | ``` 66 | 67 | 为什么要传入两个指针 `l` 和 `r` 呢?**因为这样实现可以同时处理回文串长度为奇数和偶数的情况**: 68 | 69 | ```python 70 | for 0 <= i < len(s): 71 | # 找到以 s[i] 为中心的回文串 72 | palindrome(s, i, i) 73 | # 找到以 s[i] 和 s[i+1] 为中心的回文串 74 | palindrome(s, i, i + 1) 75 | 更新答案 76 | ``` 77 | 78 | 下面看下 `longestPalindrome` 的完整代码: 79 | 80 | ```cpp 81 | string longestPalindrome(string s) { 82 | string res; 83 | for (int i = 0; i < s.size(); i++) { 84 | // 以 s[i] 为中心的最长回文子串 85 | string s1 = palindrome(s, i, i); 86 | // 以 s[i] 和 s[i+1] 为中心的最长回文子串 87 | string s2 = palindrome(s, i, i + 1); 88 | // res = longest(res, s1, s2) 89 | res = res.size() > s1.size() ? res : s1; 90 | res = res.size() > s2.size() ? res : s2; 91 | } 92 | return res; 93 | } 94 | ``` 95 | 96 | 至此,这道最长回文子串的问题就解决了,时间复杂度 O(N^2),空间复杂度 O(1)。 97 | 98 | 值得一提的是,这个问题可以用动态规划方法解决,时间复杂度一样,但是空间复杂度至少要 O(N^2) 来存储 DP table。这道题是少有的动态规划非最优解法的问题。 99 | 100 | 另外,这个问题还有一个巧妙的解法,时间复杂度只需要 O(N),不过该解法比较复杂,我个人认为没必要掌握。该算法的名字叫 Manacher's Algorithm(马拉车算法),有兴趣的读者可以自行搜索一下。 101 | 102 | **致力于把算法讲清楚!欢迎关注我的微信公众号 labuladong,查看更多通俗易懂的文章**: 103 | 104 | ![labuladong](../pictures/labuladong.png) 105 | 106 | [上一篇:如何去除有序数组的重复元素](../高频面试系列/如何去除有序数组的重复元素.md) 107 | 108 | [下一篇:如何k个一组反转链表](../高频面试系列/k个一组反转链表.md) 109 | 110 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/水塘抽样.md: -------------------------------------------------------------------------------- 1 | 我最近在 LeetCode 上做到两道非常有意思的题目,382 和 398 题,关于水塘抽样算法(Reservoir Sampling),本质上是一种随机概率算法,解法应该说会者不难,难者不会。 2 | 3 | 我第一次见到这个算法问题是谷歌的一道算法题:给你一个**未知长度**的链表,请你设计一个算法,**只能遍历一次**,随机地返回链表中的一个节点。 4 | 5 | 这里说的随机是均匀随机(uniform random),也就是说,如果有 `n` 个元素,每个元素被选中的概率都是 `1/n`,不可以有统计意义上的偏差。 6 | 7 | 一般的想法就是,我先遍历一遍链表,得到链表的总长度 `n`,再生成一个 `[1,n]` 之间的随机数为索引,然后找到索引对应的节点,不就是一个随机的节点了吗? 8 | 9 | 但题目说了,只能遍历一次,意味着这种思路不可行。题目还可以再泛化,给一个未知长度的序列,如何在其中随机地选择 `k` 个元素?想要解决这个问题,就需要著名的水塘抽样算法了。 10 | 11 | ### 算法实现 12 | 13 | **先解决只抽取一个元素的问题**,这个问题的难点在于,随机选择是「动态」的,比如说你现在你有 5 个元素,你已经随机选取了其中的某个元素 `a` 作为结果,但是现在再给你一个新元素 `b`,你应该留着 `a` 还是将 `b` 作为结果呢,以什么逻辑选择 `a` 和 `b` 呢,怎么证明你的选择方法在概率上是公平的呢? 14 | 15 | **先说结论,当你遇到第 `i` 个元素时,应该有 `1/i` 的概率选择该元素,`1 - 1/i` 的概率保持原有的选择**。看代码容易理解这个思路: 16 | 17 | ```java 18 | /* 返回链表中一个随机节点的值 */ 19 | int getRandom(ListNode head) { 20 | Random r = new Random(); 21 | int i = 0, res = 0; 22 | ListNode p = head; 23 | // while 循环遍历链表 24 | while (p != null) { 25 | // 生成一个 [0, i) 之间的整数 26 | // 这个整数等于 0 的概率就是 1/i 27 | if (r.nextInt(++i) == 0) { 28 | res = p.val; 29 | } 30 | p = p.next; 31 | } 32 | return res; 33 | } 34 | ``` 35 | 36 | 对于概率算法,代码往往都是很浅显的,但是这种问题的关键在于证明,你的算法为什么是对的?为什么每次以 `1/i` 的概率更新结果就可以保证结果是平均随机(uniform random)? 37 | 38 | **证明**:假设总共有 `n` 个元素,我们要的随机性无非就是每个元素被选择的概率都是 `1/n` 对吧,那么对于第 `i` 个元素,它被选择的概率就是: 39 | 40 | $$ 41 | \begin{aligned} 42 | &\frac{1}{i} \times (1 - \frac{1}{i+1}) \times (1 - \frac{1}{i+2}) \times ... \times (1 - \frac{1}{n}) \\ 43 | = &\frac{1}{i} \times \frac{i}{i+1} \times \frac{i+1}{i+2} \times ... \times \frac{n-1}{n} \\ 44 | = &\frac{1}{n} 45 | \end{aligned} 46 | $$ 47 | 48 | 第 `i` 个元素被选择的概率是 `1/i`,第 `i+1` 次不被替换的概率是 `1 - 1/(i+1)`,以此类推,相乘就是第 `i` 个元素最终被选中的概率,就是 `1/n`。 49 | 50 | 因此,该算法的逻辑是正确的。 51 | 52 | **同理,如果要随机选择 `k` 个数,只要在第 `i` 个元素处以 `k/i` 的概率选择该元素,以 `1 - k/i` 的概率保持原有选择即可**。代码如下: 53 | 54 | ```java 55 | /* 返回链表中 k 个随机节点的值 */ 56 | int[] getRandom(ListNode head, int k) { 57 | Random r = new Random(); 58 | int[] res = new int[k]; 59 | ListNode p = head; 60 | 61 | // 前 k 个元素先默认选上 62 | for (int j = 0; j < k && p != null; j++) { 63 | res[j] = p.val; 64 | p = p.next; 65 | } 66 | 67 | int i = k; 68 | // while 循环遍历链表 69 | while (p != null) { 70 | // 生成一个 [0, i) 之间的整数 71 | int j = r.nextInt(++i); 72 | // 这个整数小于 k 的概率就是 k/i 73 | if (j < k) { 74 | res[j] = p.val; 75 | } 76 | p = p.next; 77 | } 78 | return res; 79 | } 80 | ``` 81 | 82 | 对于数学证明,和上面区别不大: 83 | 84 | $$ 85 | \begin{aligned} 86 | &\frac{k}{i} \times (1 - \frac{k}{i+1} \times \frac{1}{k}) \times (1 - \frac{k}{i+2} \times \frac{1}{k}) \times ... \times (1 - \frac{k}{n} \times \frac{1}{k}) \\ 87 | = &\frac{k}{i} \times (1 - \frac{1}{i+1}) \times (1 - \frac{1}{i+2}) \times ... \times (1 - \frac{1}{n}) \\ 88 | = &\frac{k}{i} \times \frac{i}{i+1} \times \frac{i+1}{i+2} \times ... \times \frac{n-1}{n} \\ 89 | = &\frac{k}{n} 90 | \end{aligned} 91 | $$ 92 | 93 | 因为虽然每次更新选择的概率增大了 `k` 倍,但是选到具体第 `i` 个元素的概率还是要乘 `1/k`,也就回到了上一个推导。 94 | 95 | ### 拓展延伸 96 | 97 | 以上的抽样算法时间复杂度是 O(n),但不是最优的方法,更优化的算法基于几何分布(geometric distribution),时间复杂度为 O(k + klog(n/k))。由于涉及的数学知识比较多,这里就不列出了,有兴趣的读者可以自行搜索一下。 98 | 99 | 还有一种思路是基于「Fisher–Yates 洗牌算法」的。随机抽取 `k` 个元素,等价于对所有元素洗牌,然后选取前 `k` 个。只不过,洗牌算法需要对元素的随机访问,所以只能对数组这类支持随机存储的数据结构有效。 100 | 101 | 另外有一种思路也比较有启发意义:给每一个元素关联一个随机数,然后把每个元素插入一个容量为 `k` 的二叉堆(优先级队列)按照配对的随机数进行排序,最后剩下的 `k` 个元素也是随机的。 102 | 103 | 这个方案看起来似乎有点多此一举,因为插入二叉堆需要 O(logk) 的时间复杂度,所以整个抽样算法就需要 O(nlogk) 的复杂度,还不如我们最开始的算法。但是,这种思路可以指导我们解决**加权随机抽样算法**,权重越高,被随机选中的概率相应增大,这种情况在现实生活中是很常见的,比如你不往游戏里充钱,就永远抽不到皮肤。 104 | 105 | 最后,我想说随机算法虽然不多,但其实很有技巧的,读者不妨思考两个常见且看起来很简单的问题: 106 | 107 | 1、如何对带有权重的样本进行加权随机抽取?比如给你一个数组 `w`,每个元素 `w[i]` 代表权重,请你写一个算法,按照权重随机抽取索引。比如 `w = [1,99]`,算法抽到索引 0 的概率是 1%,抽到索引 1 的概率是 99%。 108 | 109 | 2、实现一个生成器类,构造函数传入一个很长的数组,请你实现 `randomGet` 方法,每次调用随机返回数组中的一个元素,多次调用不能重复返回相同索引的元素。要求不能对该数组进行任何形式的修改,且操作的时间复杂度是 O(1)。 110 | 111 | 这两个问题都是比较困难的,以后有时间我会写一写相关的文章。 112 | 113 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 114 | 115 | ![labuladong](../pictures/labuladong.jpg) 116 | 117 | 118 | [上一篇:如何判断回文链表](../高频面试系列/判断回文链表.md) 119 | 120 | [下一篇:如何调度考生的座位](../高频面试系列/座位调度.md) 121 | 122 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/消失的元素.md: -------------------------------------------------------------------------------- 1 | # 如何寻找消失的元素 2 | 3 | 之前也有文章写过几个有趣的智力题,今天再聊一道巧妙的题目。 4 | 5 | 题目非常简单: 6 | 7 | ![](../pictures/缺失元素/title.png) 8 | 9 | 给一个长度为 n 的数组,其索引应该在 `[0,n)`,但是现在你要装进去 n + 1 个元素 `[0,n]`,那么肯定有一个元素装不下嘛,请你找出这个缺失的元素。 10 | 11 | 这道题不难的,我们应该很容易想到,把这个数组排个序,然后遍历一遍,不就很容易找到缺失的那个元素了吗? 12 | 13 | 或者说,借助数据结构的特性,用一个 HashSet 把数组里出现的数字都储存下来,再遍历 `[0,n]` 之间的数字,去 HashSet 中查询,也可以很容易查出那个缺失的元素。 14 | 15 | 排序解法的时间复杂度是 O(NlogN),HashSet 的解法时间复杂度是 O(N),但是还需要 O(N) 的空间复杂度存储 HashSet。 16 | 17 | **第三种方法是位运算**。 18 | 19 | 对于异或运算(`^`),我们知道它有一个特殊性质:一个数和它本身做异或运算结果为 0,一个数和 0 做异或运算还是它本身。 20 | 21 | 而且异或运算满足交换律和结合律,也就是说: 22 | 23 | 2 ^ 3 ^ 2 = 3 ^ (2 ^ 2) = 3 ^ 0 = 3 24 | 25 | 而这道题索就可以通过这些性质巧妙算出缺失的那个元素。比如说 `nums = [0,3,1,4]`: 26 | 27 | ![](../pictures/缺失元素/1.jpg) 28 | 29 | 30 | 为了容易理解,我们假设先把索引补一位,然后让每个元素和自己相等的索引相对应: 31 | 32 | ![](../pictures/缺失元素/2.jpg) 33 | 34 | 35 | 这样做了之后,就可以发现除了缺失元素之外,所有的索引和元素都组成一对儿了,现在如果把这个落单的索引 2 找出来,也就找到了缺失的那个元素。 36 | 37 | 如何找这个落单的数字呢,**只要把所有的元素和索引做异或运算,成对儿的数字都会消为 0,只有这个落单的元素会剩下**,也就达到了我们的目的。 38 | 39 | ```java 40 | int missingNumber(int[] nums) { 41 | int n = nums.length; 42 | int res = 0; 43 | // 先和新补的索引异或一下 44 | res ^= n; 45 | // 和其他的元素、索引做异或 46 | for (int i = 0; i < n; i++) 47 | res ^= i ^ nums[i]; 48 | return res; 49 | } 50 | ``` 51 | 52 | ![](../pictures/缺失元素/3.jpg) 53 | 54 | 由于异或运算满足交换律和结合律,所以总是能把成对儿的数字消去,留下缺失的那个元素的。 55 | 56 | 至此,时间复杂度 O(N),空间复杂度 O(1),已经达到了最优,我们是否就应该打道回府了呢? 57 | 58 | 如果这样想,说明我们受算法的毒害太深,随着我们学习的知识越来越多,反而容易陷入思维定式,这个问题其实还有一个特别简单的解法:**等差数列求和公式**。 59 | 60 | 题目的意思可以这样理解:现在有个等差数列 0, 1, 2,..., n,其中少了某一个数字,请你把它找出来。那这个数字不就是 `sum(0,1,..n) - sum(nums)` 嘛? 61 | 62 | ```java 63 | int missingNumber(int[] nums) { 64 | int n = nums.length; 65 | // 公式:(首项 + 末项) * 项数 / 2 66 | int expect = (0 + n) * (n + 1) / 2; 67 | 68 | int sum = 0; 69 | for (int x : nums) 70 | sum += x; 71 | return expect - sum; 72 | ``` 73 | 74 | 你看,这种解法应该是最简单的,但说实话,我自己也没想到这个解法,而且我去问了几个大佬,他们也没想到这个最简单的思路。相反,如果去问一个初中生,他也许很快就能想到。 75 | 76 | 做到这一步了,我们是否就应该打道回府了呢? 77 | 78 | 如果这样想,说明我们对细节的把控还差点火候。在用求和公式计算 `expect` 时,你考虑过**整型溢出**吗?如果相乘的结果太大导致溢出,那么结果肯定是错误的。 79 | 80 | 刚才我们的思路是把两个和都加出来然后相减,为了避免溢出,干脆一边求和一边减算了。很类似刚才位运算解法的思路,仍然假设 `nums = [0,3,1,4]`,先补一位索引再让元素跟索引配对: 81 | 82 | ![](../pictures/缺失元素/xor.png) 83 | 84 | 85 | 我们让每个索引减去其对应的元素,再把相减的结果加起来,不就是那个缺失的元素吗? 86 | 87 | ```java 88 | public int missingNumber(int[] nums) { 89 | int n = nums.length; 90 | int res = 0; 91 | // 新补的索引 92 | res += n - 0; 93 | // 剩下索引和元素的差加起来 94 | for (int i = 0; i < n; i++) 95 | res += i - nums[i]; 96 | return res; 97 | } 98 | ``` 99 | 100 | 由于加减法满足交换律和结合律,所以总是能把成对儿的数字消去,留下缺失的那个元素的。 101 | 102 | 至此这道算法题目经历九曲十八弯,终于再也没有什么坑了。 103 | 104 | 105 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 106 | 107 | ![labuladong](../pictures/labuladong.jpg) 108 | 109 | 110 | [上一篇:如何判定括号合法性](../高频面试系列/合法括号判定.md) 111 | 112 | [下一篇:如何寻找缺失和重复的元素](../高频面试系列/缺失和重复的元素.md) 113 | 114 | [目录](../README.md#目录) -------------------------------------------------------------------------------- /高频面试系列/缺失和重复的元素.md: -------------------------------------------------------------------------------- 1 | 今天就聊一道很看起来简单却十分巧妙的问题,寻找缺失和重复的元素。之前的一篇文章「寻找缺失元素」也写过类似的问题,不过这次的和上次的问题使用的技巧不同。 2 | 3 | 这是 LeetCode 645 题,我来描述一下这个题目: 4 | 5 | 给一个长度为 `N` 的数组 `nums`,其中本来装着 `[1..N]` 这 `N` 个元素,无序。但是现在出现了一些错误,`nums` 中的一个元素出现了重复,也就同时导致了另一个元素的缺失。请你写一个算法,找到 `nums` 中的重复元素和缺失元素的值。 6 | 7 | ```cpp 8 | // 返回两个数字,分别是 {dup, missing} 9 | vector findErrorNums(vector& nums); 10 | ``` 11 | 12 | 比如说输入:`nums = [1,2,2,4]`,算法返回 `[2,3]`。 13 | 14 | 其实很容易解决这个问题,先遍历一次数组,用一个哈希表记录每个数字出现的次数,然后遍历一次 `[1..N]`,看看那个元素重复出现,那个元素没有出现,就 OK 了。 15 | 16 | 但问题是,这个常规解法需要一个哈希表,也就是 O(N) 的空间复杂度。你看题目给的条件那么巧,在 `[1..N]` 的几个数字中恰好有一个重复,一个缺失,**事出反常必有妖**,对吧。 17 | 18 | O(N) 的时间复杂度遍历数组是无法避免的,所以我们可以想想办法如何降低空间复杂度,是否可以在 O(1) 的空间复杂度之下找到重复和确实的元素呢? 19 | 20 | ### 思路分析 21 | 22 | 这个问题的特点是,每个元素和数组索引有一定的对应关系。 23 | 24 | 我们现在自己改造下问题,**暂且将 `nums` 中的元素变为 `[0..N-1]`,这样每个元素就和一个数组索引完全对应了,这样方便理解一些**。 25 | 26 | 如果说 `nums` 中不存在重复元素和缺失元素,那么每个元素就和唯一一个索引值对应,对吧? 27 | 28 | 现在的问题是,有一个元素重复了,同时导致一个元素缺失了,这会产生什么现象呢?**会导致有两个元素对应到了同一个索引,而且会有一个索引没有元素对应过去**。 29 | 30 | 那么,如果我能够通过某些方法,找到这个重复对应的索引,不就是找到了那个重复元素么?找到那个没有元素对应的索引,不就是找到了那个缺失的元素了么? 31 | 32 | 那么,如何不使用额外空间判断某个索引有多少个元素对应呢?这就是这个问题的精妙之处了: 33 | 34 | **通过将每个索引对应的元素变成负数,以表示这个索引被对应过一次了**: 35 | 36 | ![](../pictures/dupmissing/1.gif) 37 | 38 | 如果出现重复元素 `4`,直观结果就是,索引 `4` 所对应的元素已经是负数了: 39 | 40 | ![](../pictures/dupmissing/2.jpg) 41 | 42 | 对于缺失元素 `3`,直观结果就是,索引 `3` 所对应的元素是正数: 43 | 44 | ![](../pictures/dupmissing/3.jpg) 45 | 46 | 对于这个现象,我们就可以翻译成代码了: 47 | 48 | ```cpp 49 | vector findErrorNums(vector& nums) { 50 | int n = nums.size(); 51 | int dup = -1; 52 | for (int i = 0; i < n; i++) { 53 | int index = abs(nums[i]); 54 | // nums[index] 小于 0 则说明重复访问 55 | if (nums[index] < 0) 56 | dup = abs(nums[i]); 57 | else 58 | nums[index] *= -1; 59 | } 60 | 61 | int missing = -1; 62 | for (int i = 0; i < n; i++) 63 | // nums[i] 大于 0 则说明没有访问 64 | if (nums[i] > 0) 65 | missing = i; 66 | 67 | return {dup, missing}; 68 | } 69 | ``` 70 | 71 | 这个问题就基本解决了,别忘了我们刚才为了方便分析,假设元素是 `[0..N-1]`,但题目要求是 `[1..N]`,所以只要简单修改两处地方即可得到原题的答案: 72 | 73 | ```cpp 74 | vector findErrorNums(vector& nums) { 75 | int n = nums.size(); 76 | int dup = -1; 77 | for (int i = 0; i < n; i++) { 78 | // 现在的元素是从 1 开始的 79 | int index = abs(nums[i]) - 1; 80 | if (nums[index] < 0) 81 | dup = abs(nums[i]); 82 | else 83 | nums[index] *= -1; 84 | } 85 | 86 | int missing = -1; 87 | for (int i = 0; i < n; i++) 88 | if (nums[i] > 0) 89 | // 将索引转换成元素 90 | missing = i + 1; 91 | 92 | return {dup, missing}; 93 | } 94 | ``` 95 | 96 | 其实,元素从 1 开始是有道理的,也必须从一个非零数开始。因为如果元素从 0 开始,那么 0 的相反数还是自己,所以如果数字 0 出现了重复或者缺失,算法就无法判断 0 是否被访问过。我们之前的假设只是为了简化题目,更通俗易懂。 97 | 98 | ### 最后总结 99 | 100 | 对于这种数组问题,**关键点在于元素和索引是成对儿出现的,常用的方法是排序、异或、映射**。 101 | 102 | 映射的思路就是我们刚才的分析,将每个索引和元素映射起来,通过正负号记录某个元素是否被映射。 103 | 104 | 排序的方法也很好理解,对于这个问题,可以想象如果元素都被从小到大排序,如果发现索引对应的元素如果不相符,就可以找到重复和缺失的元素。 105 | 106 | 异或运算也是常用的,因为异或性质 `a ^ a = 0, a ^ 0 = a`,如果将索引和元素同时异或,就可以消除成对儿的索引和元素,留下的就是重复或者缺失的元素。可以看看前文「寻找缺失元素」,介绍过这种方法。 107 | 108 | 109 | 110 | 坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: 111 | 112 | ![labuladong](../pictures/labuladong.jpg) 113 | 114 | 115 | [上一篇:如何寻找消失的元素](../高频面试系列/消失的元素.md) 116 | 117 | [下一篇:如何判断回文链表](../高频面试系列/判断回文链表.md) 118 | 119 | [目录](../README.md#目录) --------------------------------------------------------------------------------