├── 2021秋招试题.txt ├── LeetCode_Blind_75.md ├── LeetCode刷题的方法论.txt ├── README.md ├── 买卖股票系列 ├── *901. 股票价格跨度.cpp ├── 121. 买卖股票的最佳时机.cpp ├── 122. 买卖股票的最佳时机 II.cpp ├── 123. 买卖股票的最佳时机 III.cpp ├── 188. 买卖股票的最佳时机 IV.cpp ├── 309. 最佳买卖股票时机含冷冻期.cpp └── 714. 买卖股票的最佳时机含手续费.cpp ├── 二分查找 ├── 004. 寻找两个正序数组的中位数.cpp ├── 033. 搜索旋转排序数组.cpp ├── 034. 在排序数组中查找元素的第一个和最后一个位置.cpp ├── 035. 搜索插入位置.cpp ├── 050. Pow(x, n).cpp ├── 069. x 的平方根.cpp ├── 074. 搜索二维矩阵.cpp ├── 081. 搜索旋转排序数组 II.cpp ├── 1283. 使结果不超过阈值的最小除数.cpp ├── 153. 寻找旋转排序数组中的最小值.cpp ├── 154. 寻找旋转排序数组中的最小值 II.cpp ├── 162. 寻找峰值.cpp ├── 240. 搜索二维矩阵 II.cpp ├── 278. 第一个错误的版本.cpp ├── 287. 寻找重复数.cpp ├── 315. 计算右侧小于当前元素的个数.cpp ├── 367. 有效的完全平方数.cpp ├── 378. 有序矩阵中第K小的元素.cpp ├── 475. 供暖器.cpp ├── 668. 乘法表中第k小的数.cpp ├── 875. 爱吃香蕉的珂珂.cpp ├── README.md ├── 剑指 Offer 53 - II. 0~n-1中缺失的数字.cpp ├── 缺失数字.cpp ├── 面试题 08.03. 魔术索引.cpp ├── 面试题 10.03. 搜索旋转数组.cpp └── 面试题 10.05. 稀疏数组搜索.cpp ├── 二叉树 ├── 094. 二叉树的中序遍历.cpp ├── 098. 验证二叉搜索树.cpp ├── 099. 恢复二叉搜索树.cpp ├── 101. 对称二叉树.cpp ├── 102. 二叉树的层序遍历.cpp ├── 1026. 节点与其祖先之间的最大差值.cpp ├── 103. 二叉树的锯齿形层序遍历.cpp ├── 104. 二叉树的最大深度.cpp ├── 105. 从前序与中序遍历序列构造二叉树.cpp ├── 106. 从中序与后序遍历序列构造二叉树.cpp ├── 108. 将有序数组转换为二叉搜索树.cpp ├── 109. 有序链表转换二叉搜索树.cpp ├── 110. 平衡二叉树.cpp ├── 111. 二叉树的最小深度.cpp ├── 112. 路径总和.cpp ├── 1123. 最深叶节点的最近公共祖先.cpp ├── 113. 路径总和 II.cpp ├── 114. 二叉树展开为链表.cpp ├── 116. 填充每个节点的下一个右侧节点指针.cpp ├── 124. 二叉树中的最大路径和.cpp ├── 129. 求根到叶子节点数字之和.cpp ├── 144. 二叉树的前序遍历.cpp ├── 145. 二叉树的后序遍历.cpp ├── 173. 二叉搜索树迭代器.cpp ├── 222. 完全二叉树的节点个数.cpp ├── 230. 二叉搜索树中第K小的元素.cpp ├── 235. 二叉搜索树的最近公共祖先.cpp ├── 236. 二叉树的最近公共祖先.cpp ├── 257. 二叉树的所有路径.cpp ├── 307. 区域和检索 - 数组可修改.cpp ├── 366. 寻找二叉树的叶子节点$.cpp ├── 404. 左叶子之和.cpp ├── 429. N 叉树的层序遍历.cpp ├── 440. 字典序的第K小数字.cpp ├── 501. 二叉搜索树中的众数.cpp ├── 513. 找树左下角的值.cpp ├── 530. 二叉搜索树的最小绝对差.cpp ├── 543. 二叉树的直径.cpp ├── 563. 二叉树的坡度.cpp ├── 572. 另一个树的子树.cpp ├── 589. N叉树的前序遍历.cpp ├── 590. N叉树的后序遍历.cpp ├── 617. 合并二叉树.cpp ├── 653. 两数之和 IV - 输入 BST.cpp ├── 662. 二叉树最大宽度.cpp ├── 687. 最长同值路径.cpp ├── 701. 二叉搜索树中的插入操作.cpp ├── 958. 二叉树的完全性检验.cpp ├── 剑指 Offer 26. 树的子结构.cpp ├── 剑指 Offer 32 - III. 从上到下打印二叉树 III.cpp ├── 剑指 Offer 33. 二叉搜索树的后序遍历序列.cpp ├── 剑指 Offer 34. 二叉树中和为某一值的路径.cpp ├── 剑指 Offer 36. 二叉搜索树与双向链表.cpp ├── 剑指 Offer 37. 序列化二叉树.cpp ├── 剑指 Offer 54. 二叉搜索树的第k大节点.cpp ├── 面试题 04.02. 最小高度树.cpp ├── 面试题 04.03. 特定深度节点链表.cpp ├── 面试题 04.06. 后继者.cpp ├── 面试题 04.10. 检查子树.cpp └── 面试题 17.12. BiNode.cpp ├── 位运算 ├── 136. 只出现一次的数字.cpp ├── 137. 只出现一次的数字 II.cpp ├── 169. 多数元素.cpp ├── 191. 位1的个数.cpp ├── 231. 2的幂.cpp ├── 260. 只出现一次的数字 III.cpp ├── 461. 汉明距离.cpp ├── README.md ├── 剑指 Offer 56 - I. 数组中数字出现的次数.cpp ├── 剑指 Offer 65. 不用加减乘除做加法.cpp ├── 面试题 01.01. 判定字符是否唯一.cpp ├── 面试题 05.01. 插入.cpp ├── 面试题 05.03. 翻转数位.cpp ├── 面试题 05.06. 整数转换.cpp ├── 面试题 05.07. 配对交换.cpp └── 面试题 17.04. 消失的数字.cpp ├── 剑指 Offer(第 2 版) └── All_In_One.cpp ├── 动态规划 ├── *剑指 Offer 46. 把数字翻译成字符串.cpp ├── 005. 最长回文子串.cpp ├── 053. 最大子序和.cpp ├── 062. 不同路径.cpp ├── 063. 不同路径 II.cpp ├── 064. 最小路径和.cpp ├── 070. 爬楼梯.cpp ├── 072. 编辑距离.cpp ├── 085. 最大矩形.cpp ├── 091. 解码方法.cpp ├── 096. 不同的二叉搜索树.cpp ├── 1143. 最长公共子序列.cpp ├── 115. 不同的子序列.cpp ├── 118. 杨辉三角.cpp ├── 119. 杨辉三角 II.cpp ├── 120. 三角形最小路径和.cpp ├── 139. 单词拆分.cpp ├── 152. 乘积最大子数组.cpp ├── 174. 地下城游戏.cpp ├── 198. 打家劫舍.cpp ├── 213. 打家劫舍 II.cpp ├── 221. 最大正方形.cpp ├── 279. 完全平方数.cpp ├── 300. 最长上升子序列.cpp ├── 303. 区域和检索 - 数组不可变.cpp ├── 321. 拼接最大数.cpp ├── 322. 零钱兑换.cpp ├── 337. 打家劫舍 III.cpp ├── 343. 整数拆分.cpp ├── 354. 俄罗斯套娃信封问题.cpp ├── 376. 摆动序列.cpp ├── 416. 分割等和子集.cpp ├── 516. 最长回文子序列.cpp ├── 518. 零钱兑换 II.cpp ├── 647. 回文子串.cpp ├── 651. 4键键盘.cpp ├── 746. 使用最小花费爬楼梯.cpp ├── 887. 鸡蛋掉落.cpp ├── README.md ├── 剑指 Offer 14- I. 剪绳子.cpp ├── 剑指 Offer 47. 礼物的最大价值.cpp ├── 剑指 Offer 66. 构建乘积数组.cpp ├── 字节跳动高频面试题之圆环回原点问题.cpp ├── 打靶问题 ├── 面试题 08.01. 三步问题.cpp ├── 面试题 08.06. 汉诺塔问题.cpp ├── 面试题 08.11. 硬币.cpp ├── 面试题 17.13. 恢复空格.cpp └── 面试题 17.16. 按摩师.cpp ├── 双指针 ├── *075. 颜色分类.cpp ├── *209. 长度最小的子数组.cpp ├── *283. 移动零.cpp ├── 011. 盛最多水的容器.cpp ├── 015. 三数之和.cpp ├── 016. 最接近的三数之和.cpp ├── 026. 删除排序数组中的重复项.cpp ├── 027. 移除元素.cpp ├── 080. 删除排序数组中的重复项 II.cpp ├── 1004. 最大连续1的个数 III.cpp ├── 159. 至多包含两个不同字符的最长子串.cpp ├── 167. 两数之和 II - 输入有序数组.cpp ├── 941. 有效的山脉数组.cpp ├── README.txt ├── 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面.cpp ├── 剑指 Offer 48. 最长不含重复字符的子字符串.cpp ├── 剑指 Offer 57 - II. 和为s的连续正数序列.cpp ├── 剑指 Offer 57. 和为s的两个数字.cpp ├── 面试题 10.01. 合并排序的数组.cpp └── 面试题 17.11. 单词距离.cpp ├── 回溯算法 ├── *040. 组合总和 II.cpp ├── *047. 全排列 II.cpp ├── *093. 复原IP地址.cpp ├── 017. 电话号码的字母组合.cpp ├── 022. 括号生成.cpp ├── 039. 组合总和.cpp ├── 046. 全排列.cpp ├── 051. N 皇后.cpp ├── 052. N皇后 II.cpp ├── 077. 组合.cpp ├── 078. 子集.cpp ├── 090. 子集 II.cpp ├── 113. 路径总和 II.cpp ├── 131. 分割回文串.cpp ├── 剑指 Offer 10- II. 青蛙跳台阶问题.cpp ├── 面试题 08.04. 幂集.cpp ├── 面试题 08.07. 无重复字符串的排列组合.cpp ├── 面试题 08.08. 有重复字符串的排列组合.cpp └── 面试题 08.09. 括号.cpp ├── 堆 ├── 215. 数组中的第K个最大元素.cpp ├── 253. Meeting Rooms II 会议室之二.cpp ├── 347. 前 K 个高频元素.cpp ├── 703. 数据流中的第K大元素.cpp ├── 973. 最接近原点的 K 个点.cpp ├── 剑指 Offer 41. 数据流中的中位数.cpp └── 面试题 17.14. 最小K个数.cpp ├── 字符串 ├── *005. 最长回文子串.cpp ├── *290. 单词规律.cpp ├── 003. 无重复字符的最长子串.cpp ├── 006. Z 字形变换.cpp ├── 008. 字符串转换整数 (atoi).cpp ├── 010. 正则表达式匹配.cpp ├── 014. 最长公共前缀.cpp ├── 028. 实现 strStr().cpp ├── 038. 外观数列.cpp ├── 043. 字符串相乘.cpp ├── 049. 字母异位词分组.cpp ├── 058. 最后一个单词的长度.cpp ├── 067. 二进制求和.cpp ├── 071. 简化路径.cpp ├── 093. 复原IP地址.cpp ├── 125. 验证回文串.cpp ├── 131. 分割回文串.cpp ├── 151. 翻转字符串里的单词.cpp ├── 165. 比较版本号.cpp ├── 345. 反转字符串中的元音字母.cpp ├── 392. 判断子序列.cpp ├── 415. 字符串相加.cpp ├── 443. 压缩字符串.cpp ├── 541. 反转字符串 II.cpp ├── 557. 反转字符串中的单词 III.cpp ├── 剑指 Offer 05. 替换空格.cpp └── 剑指 Offer 58 - I. 翻转单词顺序.cpp ├── 排序 ├── 056. 合并区间.cpp ├── 057. 插入区间.cpp ├── 088. 合并两个有序数组.cpp ├── 179. 最大数.cpp ├── 215. 数组中的第K个最大元素.cpp ├── 274. H 指数.cpp ├── 324. 摆动排序 II.cpp ├── 406. 根据身高重建队列.cpp ├── 493. 翻转对.cpp ├── 905. 按奇偶排序数组.cpp ├── 912. 排序数组.cpp ├── 922. 按奇偶排序数组 II.cpp ├── 剑指 Offer 51. 数组中的逆序对.cpp ├── 快速排序.cpp ├── 面试题 10.11. 峰与谷.cpp └── 面试题 16.16. 部分排序.cpp ├── 数学 ├── *470. 用 Rand7() 实现 Rand10().cpp ├── *剑指 Offer 20. 表示数值的字符串.cpp ├── 007. 整数反转.cpp ├── 013. 罗马数字转整数.cpp ├── 172. 阶乘后的零.cpp ├── 204. 计数质数 ├── 231. 2的幂.cpp ├── 258. 各位相加.cpp ├── 263. 丑数.cpp ├── 264. 丑数 II.cpp ├── 397. 整数替换.cpp ├── 400. 第N个数字.cpp ├── 441. 排列硬币.cpp ├── 507. 完美数.cpp ├── 剑指 Offer 16. 数值的整数次方.cpp ├── 剑指 Offer 17. 打印从1到最大的n位数.cpp ├── 剑指 Offer 43. 1~n 整数中 1 出现的次数.cpp ├── 剑指 Offer 65. 不用加减乘除做加法.cpp ├── 面试题 16.07. 最大数值.cpp └── 面试题 17.09. 第 k 个数.cpp ├── 数组 ├── 001. 两数之和.cpp ├── 011. 盛最多水的容器.cpp ├── 015. 三数之和.cpp ├── 026. 删除排序数组中的重复项.cpp ├── 027. 移除元素.cpp ├── 041. 缺失的第一个正数.cpp ├── 042. 接雨水.cpp ├── 045. 跳跃游戏 II.cpp ├── 048. 旋转图像.cpp ├── 054. 螺旋矩阵.cpp ├── 055. 跳跃游戏.cpp ├── 066. 加一.cpp ├── 084. 柱状图中最大的矩形.cpp ├── 205. 同构字符串.cpp ├── 209. 长度最小的子数组.cpp ├── 217. 存在重复元素.cpp ├── 218. 天际线问题.cpp ├── 219. 存在重复元素 II.cpp ├── 220. 存在重复元素 III.cpp ├── 228. 汇总区间.cpp ├── 238. 除自身以外数组的乘积.cpp ├── 252. 会议室.cpp ├── 253. 会议室 II.cpp ├── 268. 丢失的数字.cpp ├── 290. 单词规律.cpp ├── 325. 和等于 k 的最长子数组长度.cpp ├── 350. 两个数组的交集 II.cpp ├── 352. 将数据流变为多个不相交区间.cpp ├── 414. 第三大的数.cpp ├── 442. 数组中重复的数据.cpp ├── 485. 最大连续1的个数.cpp ├── 560. 和为K的子数组.cpp ├── 605. 种花问题.cpp ├── 674. 最长连续递增序列.cpp ├── 830. 较大分组的位置.cpp ├── 剑指 Offer 03. 数组中重复的数字.cpp ├── 剑指 Offer 62. 圆圈中最后剩下的数字.cpp ├── 面试题 01.07. 旋转矩阵.cpp ├── 面试题 01.08. 零矩阵.cpp ├── 面试题 16.06. 最小差.cpp ├── 面试题 16.10. 生存人数.cpp ├── 面试题 16.11. 跳水板.cpp ├── 面试题 16.20. T9键盘.cpp ├── 面试题 16.24. 数对和.cpp └── 面试题 17.10. 主要元素.cpp ├── 栈 ├── *150. 逆波兰表达式求值.cpp ├── *394. 字符串解码.cpp ├── *895. 最大频率栈.cpp ├── 020. 有效的括号.cpp ├── 032. 最长有效括号.cpp ├── 084. 柱状图中最大的矩形.cpp ├── 085. 最大矩形.cpp ├── 1019. 链表中的下一个更大节点.cpp ├── 224. 基本计算器.cpp ├── 316. 去除重复字母.cpp ├── 402. 移掉K位数字.cpp ├── 456. 132模式.cpp ├── 496. 下一个更大元素 I.cpp ├── 739. 每日温度.cpp ├── 946. 验证栈序列.cpp ├── 剑指 Offer 09. 用两个栈实现队列.cpp ├── 面试题 03.02. 栈的最小值.cpp └── 面试题 03.04. 化栈为队.cpp ├── 深度优先搜索 ├── 079. 单词搜索.cpp ├── 127. 单词接龙.cpp ├── 133. 克隆图.cpp ├── 1376. 通知所有员工所需的时间.cpp ├── 200. 岛屿数量.cpp ├── 207. 课程表.cpp ├── 210. 课程表 II.cpp ├── 212. 单词搜索 II.cpp ├── 257. 二叉树的所有路径.cpp ├── 332. 重新安排行程.cpp ├── 386. 字典序排数.cpp ├── 399. 除法求值.cpp ├── 547. 朋友圈.cpp ├── 695. 岛屿的最大面积.cpp ├── 733. 图像渲染.cpp ├── 剑指 Offer 13. 机器人的运动范围.cpp ├── 剑指 Offer 34. 二叉树中和为某一值的路径.cpp ├── 面试题 04.12. 求和路径.cpp ├── 面试题 16.19. 水域大小.cpp └── 面试题 17.22. 单词转换.cpp ├── 滑动窗口 ├── 076. 最小覆盖子串.cpp ├── 239. 滑动窗口最大值.cpp ├── 567. 字符串的排列.cpp ├── README.md ├── 关于滑动窗口相关问题的思考.md ├── 剑指 Offer 48. 最长不含重复字符的子字符串.cpp ├── 剑指 Offer 59 - I. 滑动窗口的最大值.cpp └── 剑指 Offer 59 - II. 队列的最大值.cpp ├── 设计 ├── 146. LRU缓存机制.cpp ├── 155. 最小栈.cpp ├── 173. 二叉搜索树迭代器.cpp ├── 225. 用队列实现栈.cpp ├── 232. 用栈实现队列.cpp ├── 284. 顶端迭代器.cpp ├── 295. 数据流的中位数.cpp ├── 341. 扁平化嵌套列表迭代器.cpp ├── 380. 常数时间插入、删除和获取随机元素.cpp ├── 895. 最大频率栈.cpp ├── 剑指 Offer 41. 数据流中的中位数.cpp ├── 剑指 Offer 59 - II. 队列的最大值.cpp └── 面试题 03.06. 动物收容所.cpp ├── 贪心算法 ├── 045. 跳跃游戏 II.cpp ├── 055. 跳跃游戏.cpp ├── 134. 加油站.cpp ├── 455. 分发饼干.cpp ├── 621. 任务调度器.cpp ├── 738. 单调递增的数字.cpp ├── 860. 柠檬水找零.cpp └── README.md ├── 链表 ├── *025. K 个一组翻转链表.cpp ├── *061. 旋转链表.cpp ├── *109. 有序链表转换二叉搜索树.cpp ├── 002. 两数相加.cpp ├── 019. 删除链表的倒数第N个节点.cpp ├── 021. 合并两个有序链表.cpp ├── 023. 合并K个升序链表.cpp ├── 024. 两两交换链表中的节点.cpp ├── 082. 删除排序链表中的重复元素 II.cpp ├── 083. 删除排序链表中的重复元素.cpp ├── 086. 分隔链表.cpp ├── 092. 反转链表 II.cpp ├── 138. 复制带随机指针的链表.cpp ├── 141. 环形链表.cpp ├── 142. 环形链表 II.cpp ├── 143. 重排链表.cpp ├── 147. 对链表进行插入排序.cpp ├── 148. 排序链表.cpp ├── 160. 相交链表.cpp ├── 203. 移除链表元素.cpp ├── 206. 反转链表.cpp ├── 234. 回文链表.cpp ├── 328. 奇偶链表.cpp ├── 382. 链表随机节点.cpp ├── README.md ├── 面试题 02.01. 移除重复节点.cpp ├── 面试题 02.02. 返回倒数第 k 个节点.cpp ├── 面试题 02.03. 删除中间节点.cpp └── 面试题 02.04. 分割链表.cpp ├── 队列 ├── 347. 前 K 个高频元素.cpp ├── 973. 最接近原点的 K 个点.cpp ├── Offer 59 - II. 队列的最大值.cpp └── 剑指 Offer 59 - I. 滑动窗口的最大值.cpp ├── 面筋.txt └── 面试心态问题.md /买卖股票系列/121. 买卖股票的最佳时机.cpp: -------------------------------------------------------------------------------- 1 | 121. 买卖股票的最佳时机 2 | 3 | https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ 4 | 5 | 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 6 | 7 | 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 8 | 9 | 注意:你不能在买入股票前卖出股票。 10 | 11 | 示例 1: 12 | 13 | 输入: [7,1,5,3,6,4] 14 | 输出: 5 15 | 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 16 | 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 17 | 示例 2: 18 | 19 | 输入: [7,6,4,3,1] 20 | 输出: 0 21 | 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 22 | 23 | class Solution { 24 | public: 25 | int maxProfit(vector& prices) { 26 | if (prices.empty()) return 0; 27 | int res = 0; 28 | int minValue = prices[0]; 29 | for (int i = 0; i < prices.size(); ++i) { 30 | minValue = min(minValue, prices[i]); 31 | if (prices[i] > minValue) { 32 | res = max(res, prices[i] - minValue); 33 | } 34 | } 35 | return res; 36 | } 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /买卖股票系列/309. 最佳买卖股票时机含冷冻期.cpp: -------------------------------------------------------------------------------- 1 | 309. 最佳买卖股票时机含冷冻期 2 | 3 | https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ 4 | 5 | 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。 6 | 7 | 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 8 | 9 | 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 10 | 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 11 | 示例: 12 | 13 | 输入: [1,2,3,0,2] 14 | 输出: 3 15 | 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出] 16 | 17 | class Solution { 18 | public: 19 | int maxProfit(vector& prices) { 20 | if (prices.size() <= 1) return 0; 21 | vector hold(prices.size(), 0); 22 | vector unhold(prices.size(), 0); 23 | hold[0] = - prices[0]; 24 | hold[1] = max( -prices[0], -prices[1]); 25 | unhold[0] = 0; 26 | 27 | for (int i = 1; i < prices.size(); ++i) { 28 | unhold[i] = max(hold[i - 1] + prices[i], unhold[i - 1]); 29 | if (i == 1) continue; 30 | hold[i] = max(unhold[i - 2] - prices[i], hold[i - 1]); 31 | } 32 | return unhold.back(); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /买卖股票系列/714. 买卖股票的最佳时机含手续费.cpp: -------------------------------------------------------------------------------- 1 | 714. 买卖股票的最佳时机含手续费 2 | 3 | 给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。 4 | 你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。 5 | 返回获得利润的最大值。 6 | 7 | class Solution { 8 | public: 9 | int maxProfit(vector& prices, int fee) { 10 | vector sold(prices.size(), 0), hold = sold; 11 | hold[0] = -prices[0]; 12 | for (int i = 1; i < prices.size(); ++i) { 13 | sold[i] = max(sold[i - 1], hold[i - 1] + prices[i] - fee); 14 | hold[i] = max(hold[i - 1], sold[i - 1] - prices[i]); 15 | } 16 | return sold.back(); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /二分查找/050. Pow(x, n).cpp: -------------------------------------------------------------------------------- 1 | 实现 pow(x, n) ,即计算 x 的 n 次幂函数。 2 | 3 | 题解: 4 | 5 | n次方 = n为偶数,可分成n/2取整次方的平方。n为奇数,分成n/2取整次方的平方 * n 6 | n连续/2,到达边界, 7 | 1次方 = 数本身, 8 | 0次方 = 1 9 | 10 | 这道题目的巧妙是在构造一个函数使得输入参数为long long,循环递归参数除以2。 11 | 12 | class Solution { 13 | public: 14 | double myPow(double x, int n) { 15 | long long N = n; 16 | return N >= 0 ? quickPow(x, N) : 1.0 / quickPow(x, -N); 17 | } 18 | 19 | double quickPow(double x, long long N) { 20 | if (N == 0) 21 | return 1.0; 22 | double y = quickPow(x, N / 2); 23 | return N % 2 == 0 ? y * y : y * y * x; 24 | } 25 | }; 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /二分查找/153. 寻找旋转排序数组中的最小值.cpp: -------------------------------------------------------------------------------- 1 | 153. 寻找旋转排序数组中的最小值 2 | 3 | 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 4 | 5 | ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 6 | 7 | 请找出其中最小的元素。 8 | 9 | 你可以假设数组中不存在重复元素。 10 | 11 | 示例 1: 12 | 13 | 输入: [3,4,5,1,2] 14 | 输出: 1 15 | 示例 2: 16 | 17 | 输入: [4,5,6,7,0,1,2] 18 | 输出: 0 19 | 20 | 21 | class Solution { 22 | public: 23 | int findMin(vector& nums) { 24 | int left = 0, right = nums.size() - 1; 25 | int mid = 0; 26 | while (left < right) { 27 | mid = left + (right - left) / 2; 28 | if (nums[mid] > nums[right]) { 29 | left = mid + 1; 30 | } else { 31 | right = mid; 32 | } 33 | } 34 | return nums[left]; 35 | } 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /二分查找/154. 寻找旋转排序数组中的最小值 II.cpp: -------------------------------------------------------------------------------- 1 | https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/ 2 | 3 | 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 4 | 5 | ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 6 | 7 | 请找出其中最小的元素。 8 | 9 | 注意数组中可能存在重复的元素。 10 | 11 | 示例 1: 12 | 13 | 输入: [1,3,5] 14 | 输出: 1 15 | 示例 2: 16 | 17 | 输入: [2,2,2,0,1] 18 | 输出: 0 19 | 20 | class Solution { 21 | public: 22 | int minArray(vector& numbers) { 23 | if (numbers.size() == 0) return 0; 24 | int left = 0, right = numbers.size() - 1; 25 | while (left < right) { 26 | int mid = left + (right - left) / 2; 27 | if (numbers[mid] > numbers[right]) { 28 | left = mid + 1; 29 | } else if (numbers[mid] < numbers[right]) { 30 | right = mid; 31 | } else { 32 | right = right - 1; 33 | } 34 | } 35 | return numbers[left]; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /二分查找/162. 寻找峰值.cpp: -------------------------------------------------------------------------------- 1 | 162. 寻找峰值 2 | 3 | 峰值元素是指其值大于左右相邻值的元素。 4 | 给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。 5 | 数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。 6 | 你可以假设 nums[-1] = nums[n] = -∞。 7 | 8 | class Solution { 9 | public: 10 | int findPeakElement(vector& nums) { 11 | int left = 0, right = nums.size() - 1; 12 | while (left < right) { 13 | int mid = left + (right - left) / 2; 14 | if (nums[mid] < nums[mid + 1]) left = mid + 1; 15 | else right = mid; 16 | } 17 | return right; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /二分查找/240. 搜索二维矩阵 II.cpp: -------------------------------------------------------------------------------- 1 | 240. 搜索二维矩阵 II 2 | [ 3 | [1, 4, 7, 11, 15], 4 | [2, 5, 8, 12, 19], 5 | [3, 6, 9, 16, 22], 6 | [10, 13, 14, 17, 24], 7 | [18, 21, 23, 26, 30] 8 | ] 9 | 10 | 给定 target = 5,返回 true。 11 | 给定 target = 20,返回 false。 12 | 13 | 解题思路: 14 | 15 | 从矩阵的右上方开始考虑,如果当前值小于target,向下走, 16 | 如果当前值大于target,向左走。 17 | 18 | class Solution { 19 | public: 20 | bool searchMatrix(vector>& matrix, int target) { 21 | if (matrix.size() == 0) return false; 22 | int row = 0, col = matrix[0].size() - 1; 23 | while (row <= matrix.size() - 1 && col >= 0) { 24 | if (matrix[row][col] < target) ++row; 25 | else if (matrix[row][col] > target) --col; 26 | else return true; 27 | } 28 | return false; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /二分查找/278. 第一个错误的版本.cpp: -------------------------------------------------------------------------------- 1 | https://leetcode.cn/problems/first-bad-version/description/ 2 | 3 | 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。 4 | 由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。 5 | 6 | 假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。 7 | 8 | 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。 9 | 实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。 10 | 11 | [题解]使用二分查找。 12 | 13 | // The API isBadVersion is defined for you. 14 | // bool isBadVersion(int version); 15 | 16 | class Solution { 17 | public: 18 | int firstBadVersion(int n) { 19 | int left = 1, right = n; 20 | while (left < right) { 21 | int mid = left + (right - left) / 2; 22 | if (isBadVersion(mid)) { 23 | right = mid; 24 | } else { 25 | left = mid + 1; 26 | } 27 | } 28 | return left; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /二分查找/315. 计算右侧小于当前元素的个数.cpp: -------------------------------------------------------------------------------- 1 | 315. 计算右侧小于当前元素的个数 2 | 3 | https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/ 4 | 5 | 给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。 6 | 7 | 示例: 8 | 9 | 输入:nums = [5,2,6,1] 10 | 输出:[2,1,1,0] 11 | 解释: 12 | 5 的右侧有 2 个更小的元素 (2 和 1) 13 | 2 的右侧仅有 1 个更小的元素 (1) 14 | 6 的右侧有 1 个更小的元素 (1) 15 | 1 的右侧有 0 个更小的元素 16 | 17 | 解题思路: 18 | 19 | 从右到左二分查找插入排序。 20 | 21 | class Solution { 22 | public: 23 | vector countSmaller(vector& nums) { 24 | vector tmp, ans(nums.size()); 25 | for (int i = nums.size() - 1; i >= 0; --i) { 26 | int left = 0, right = tmp.size(); 27 | while (left < right) { 28 | int mid = left + (right - left) / 2; 29 | if (tmp[mid] >= nums[i]) right = mid; 30 | else left = mid + 1; 31 | } 32 | ans[i] = right; 33 | tmp.insert(tmp.begin() + right, nums[i]); 34 | } 35 | return ans; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /二分查找/367. 有效的完全平方数.cpp: -------------------------------------------------------------------------------- 1 | 367. 有效的完全平方数 2 | 3 | 给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。 4 | 5 | 说明:不要使用任何内置的库函数,如  sqrt。 6 | 7 | class Solution { 8 | public: 9 | bool isPerfectSquare(int num) { 10 | int left = 0, right = num; 11 | while (left <= right) { 12 | long mid = left + (right - left) / 2; 13 | long t = mid * mid; 14 | if (num == t) return true; 15 | else if (num < t) right = mid - 1; 16 | else left = mid + 1; 17 | } 18 | return false; 19 | } 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /二分查找/378. 有序矩阵中第K小的元素.cpp: -------------------------------------------------------------------------------- 1 | 378. 有序矩阵中第K小的元素 2 | 3 | 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。 4 | 请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。 5 | 6 | 示例: 7 | 8 | matrix = [ 9 | [ 1, 5, 9], 10 | [10, 11, 13], 11 | [12, 13, 15] 12 | ], 13 | k = 8, 14 | 15 | 返回 13。 16 | 17 | 题解: 18 | 19 | 此题用小顶堆肯定能做,但是二分查找是本题考查的重点,也能让时间复杂度小于O(n^2)。 20 | 先找到中点,然后每行比较。如果cnt累计值小于k,往后找。否则往前找。 21 | 22 | class Solution { 23 | public: 24 | int kthSmallest(vector>& matrix, int k) { 25 | int left = matrix[0][0], right = matrix.back().back(); 26 | while (left < right) { 27 | int mid = left + (right - left) / 2, cnt = 0; 28 | for (int i = 0; i < matrix.size(); ++i) { 29 | cnt += upper_bound(matrix[i].begin(), matrix[i].end(), mid) - matrix[i].begin(); 30 | } 31 | if (cnt < k) left = mid + 1; 32 | else right = mid; 33 | } 34 | return left; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /二分查找/475. 供暖器.cpp: -------------------------------------------------------------------------------- 1 | 475. 供暖器 2 | 3 | 冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。 4 | 在加热器的加热半径范围内的每个房屋都可以获得供暖。 5 | 现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋的最小加热半径。 6 | 说明:所有供暖器都遵循你的半径标准,加热的半径也一样。 7 | 8 | 思路: 9 | 10 | 先将heaters排序。再依次处理每位house,利用二分查找找到每个house的右边第一个heater和左边第一个heater。 11 | 然后每轮循环记录当前最大的heater半径。 12 | 13 | class Solution { 14 | public: 15 | int findRadius(vector& houses, vector& heaters) { 16 | int ans = 0, n = heaters.size(); 17 | sort(heaters.begin(), heaters.end()); 18 | for (auto house : houses) { 19 | int left = 0, right = n; 20 | while (left < right) { 21 | int mid = left + (right - left) / 2; 22 | if (heaters[mid] < house) left = mid + 1; 23 | else right = mid; 24 | } 25 | int inst1 = (right == n) ? INT_MAX : heaters[right] - house; 26 | int inst2 = (right == 0) ? INT_MAX : house - heaters[right - 1]; 27 | ans = max(ans, min(inst1, inst2)); 28 | } 29 | return ans; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /二分查找/668. 乘法表中第k小的数.cpp: -------------------------------------------------------------------------------- 1 | 668. 乘法表中第k小的数 2 | 3 | class Solution { 4 | public: 5 | int findKthNumber(int m, int n, int k) { 6 | int left = 1, right = m * n; 7 | while (left < right) { 8 | int mid = left + (right - left) / 2, cnt = 0; 9 | for (int i = 1; i <= m; ++i) { 10 | cnt += (mid > n * i) ? n : (mid / i); 11 | } 12 | if (cnt < k) left = mid + 1; 13 | else right = mid; 14 | } 15 | return right; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /二分查找/875. 爱吃香蕉的珂珂.cpp: -------------------------------------------------------------------------------- 1 | 875. 爱吃香蕉的珂珂 2 | 3 | 珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。 4 | 警卫已经离开了,将在 H 小时后回来。 5 | 珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。 6 | 每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根, 7 | 她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。   8 | 珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。 9 | 返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。 10 | 11 | class Solution { 12 | public: 13 | int minEatingSpeed(vector& piles, int H) { 14 | int left = 1, right = 1e9; 15 | while (left < right) { 16 | int cnt = 0; 17 | int mid = left + (right - left) / 2; 18 | for (int pile : piles) cnt += (pile + mid - 1) / mid; 19 | if (cnt > H) left = mid + 1; 20 | else right = mid; 21 | } 22 | return left; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /二分查找/剑指 Offer 53 - II. 0~n-1中缺失的数字.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 53 - II. 0~n-1中缺失的数字 2 | 3 | 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。 4 | 5 | class Solution { 6 | public: 7 | int missingNumber(vector& nums) { 8 | int left = 0, right = nums.size() - 1; 9 | 10 | while (left <= right) { 11 | int mid = left + (right - left) / 2; 12 | if (nums[mid] == mid) { 13 | left = mid + 1; // 等于的话missing number肯定出现在mid后面 14 | } else { 15 | right = mid - 1; // 否则的话肯定出现在mid的前面 16 | } 17 | } 18 | 19 | return left; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /二分查找/缺失数字.cpp: -------------------------------------------------------------------------------- 1 | 题目描述 2 | 3 | 从0,1,2,...,n这n+1个数中选择n个数,组成有序数组,找出这n个数中缺失的那个数,要求O(n)尽可能小。 4 | 5 | class Solution { 6 | public: 7 | /** 8 | * 找缺失数字 9 | * @param a int整型一维数组 给定的数字串 10 | * @param aLen int a数组长度 11 | * @return int整型 12 | */ 13 | int solve(int* a, int aLen) { 14 | // write code here 15 | int left = 0, right = aLen - 1; 16 | while (left < right) { 17 | int mid = left + (right - left) / 2; 18 | if (a[mid] == mid) left = mid + 1; 19 | else if (a[mid] > mid) right = mid; 20 | } 21 | return left; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /二分查找/面试题 08.03. 魔术索引.cpp: -------------------------------------------------------------------------------- 1 | 面试题 08.03. 魔术索引 2 | 3 | 魔术索引。 在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i] = i。 4 | 给定一个有序整数数组,编写一种方法找出魔术索引, 5 | 若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。 6 | 若有多个魔术索引,返回索引值最小的一个。 7 | 8 | class Solution { 9 | public: 10 | int findMagicIndex(vector& nums) { 11 | return bsearch(nums, 0, nums.size() - 1); 12 | } 13 | 14 | int bsearch(vector& nums, int left, int right) { 15 | if (left > right) return -1; 16 | int mid = left + (right - left) / 2; 17 | left = bsearch(nums, left, mid - 1); 18 | if (left != -1) return left; 19 | if (mid == nums[mid]) return mid; 20 | else return bsearch(nums, mid + 1, right); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /二分查找/面试题 10.03. 搜索旋转数组.cpp: -------------------------------------------------------------------------------- 1 | 面试题 10.03. 搜索旋转数组 2 | 3 | 搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。 4 | 请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。 5 | 6 | 输入 7 | [5,5,5,1,2,3,4,5] 8 | 5 9 | 10 | 输出 11 | 0 12 | 13 | class Solution { 14 | public: 15 | int search(vector& nums, int target) { 16 | int left = 0, right = nums.size() - 1; 17 | if (nums.empty()) return -1; 18 | while (left < right) { 19 | int mid = left + (right - left) / 2; 20 | if (nums[left] < nums[mid]) { 21 | if (nums[left] <= target && target <= nums[mid]) right = mid; 22 | else left = mid + 1; 23 | } else if (nums[left] > nums[mid]) { 24 | if (nums[left] <= target || target <= nums[mid]) right = mid; // 这套题这步是坑,和33题不一样。 25 | else left = mid + 1; 26 | } else { 27 | if (nums[left] != target) ++left; 28 | else right = left; // 如果相等,那么left就是答案。让left等于right,停止搜索。 29 | } 30 | } 31 | return nums[left] == target ? left : -1; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /二分查找/面试题 10.05. 稀疏数组搜索.cpp: -------------------------------------------------------------------------------- 1 | 面试题 10.05. 稀疏数组搜索 2 | 3 | 稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。 4 | 5 | 示例1: 6 | 7 | 输入: words = ["at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""], s = "ta" 8 | 输出:-1 9 | 说明: 不存在返回-1。 10 | 11 | class Solution { 12 | public: 13 | int findString(vector& words, string s) { 14 | int left = 0, right = words.size() - 1; 15 | while (left <= right) { 16 | while (words[left] == "") ++left; 17 | while (words[right] == "") --right; 18 | int mid = left + (right - left) / 2; 19 | while (words[mid] == "" && mid >= left) --mid; 20 | if (words[mid] == s) return mid; 21 | else if (words[mid] < s) left = mid + 1; 22 | else right = mid - 1; 23 | } 24 | return -1; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /二叉树/098. 验证二叉搜索树.cpp: -------------------------------------------------------------------------------- 1 | 98. 验证二叉搜索树 2 | 3 | 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 4 | 5 | 假设一个二叉搜索树具有如下特征: 6 | 7 | 节点的左子树只包含小于当前节点的数。 8 | 节点的右子树只包含大于当前节点的数。 9 | 所有左子树和右子树自身必须也是二叉搜索树。 10 | 11 | 注意: 12 | 13 | 这道题lower和upper必须是long类型,因为要考虑越界情况。 14 | if (root->val <= lower || root->val >= upper) 这里的判断必须是小于等于和大于等于,因为如果是等于也不满足BST的条件。 15 | 16 | class Solution { 17 | public: 18 | bool isValidBST(TreeNode* root) { 19 | return isValidBST(root, LONG_MIN, LONG_MAX); 20 | } 21 | 22 | bool isValidBST(TreeNode* root, long lower, long upper) { 23 | if (!root) return true; 24 | if (root->val <= lower || root->val >= upper) return false; 25 | return isValidBST(root->left, lower, root->val) && isValidBST(root->right, root->val, upper); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /二叉树/099. 恢复二叉搜索树.cpp: -------------------------------------------------------------------------------- 1 | 99. 恢复二叉搜索树 2 | 3 | 二叉搜索树中的两个节点被错误地交换。 4 | 请在不改变其结构的情况下,恢复这棵树。 5 | 6 | 题解: 7 | 8 | 利用一个中序遍历找出数组的有序队列,然后对vals数组排序,中序遍历出来的BST结果一定是单调递增序列数组。 9 | 然后将list中错误的value对换一下。 10 | 11 | class Solution { 12 | public: 13 | void recoverTree(TreeNode* root) { 14 | vector list; 15 | vector vals; 16 | inorder(root, list, vals); 17 | sort(vals.begin(), vals.end()); 18 | for (int i = 0; i < list.size(); ++i) { 19 | list[i]->val = vals[i]; 20 | } 21 | } 22 | 23 | void inorder(TreeNode *root, vector& list, vector& vals) { 24 | if (root == nullptr) return; 25 | inorder(root->left, list, vals); 26 | list.push_back(root); 27 | vals.push_back(root->val); 28 | inorder(root->right, list, vals); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /二叉树/102. 二叉树的层序遍历.cpp: -------------------------------------------------------------------------------- 1 | 102. 二叉树的层序遍历 2 | 3 | 给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 4 | (即逐层地,从左到右访问所有节点)。 5 | 6 | class Solution { 7 | public: 8 | vector> levelOrder(TreeNode* root) { 9 | vector> res; 10 | queue q; 11 | if (!root) return res; 12 | q.push(root); 13 | 14 | while (!q.empty()) { 15 | vector oneLevel; 16 | int sz = q.size(); 17 | for (int i = 0; i < sz; ++i) { 18 | TreeNode *node = q.front(); 19 | q.pop(); 20 | oneLevel.push_back(node->val); 21 | if (node->left) q.push(node->left); 22 | if (node->right) q.push(node->right); 23 | } 24 | res.push_back(oneLevel); 25 | } 26 | return res; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /二叉树/1026. 节点与其祖先之间的最大差值.cpp: -------------------------------------------------------------------------------- 1 | 1026. 节点与其祖先之间的最大差值 2 | 3 | 给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V, 4 | 其中 V = |A.val - B.val|,且 A 是 B 的祖先。 5 | (如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先, 6 | 那么我们认为 A 是 B 的祖先) 7 | 8 | 题解: 9 | 10 | 把整个path的最大值和最小值一路传下去,ans就是mx - mn值。 11 | 12 | class Solution { 13 | public: 14 | int maxAncestorDiff(TreeNode* root) { 15 | return dfs(root, root->val, root->val); 16 | } 17 | int dfs(TreeNode* root, int mx, int mn) { 18 | if (root == nullptr) return mx - mn; 19 | mx = max(mx, root->val); 20 | mn = min(mn, root->val); 21 | return max(dfs(root->left, mx, mn), dfs(root->right, mx, mn)); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /二叉树/104. 二叉树的最大深度.cpp: -------------------------------------------------------------------------------- 1 | 104. 二叉树的最大深度 2 | 3 | 给定一个二叉树,找出其最大深度。 4 | 5 | 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 6 | 7 | 说明: 叶子节点是指没有子节点的节点。 8 | 9 | class Solution { 10 | public: 11 | int maxDepth(TreeNode* root) { 12 | if (root == nullptr) return 0; 13 | int left = maxDepth(root->left); 14 | int right = maxDepth(root->right); 15 | return 1 + max(left, right); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /二叉树/106. 从中序与后序遍历序列构造二叉树.cpp: -------------------------------------------------------------------------------- 1 | 106. 从中序与后序遍历序列构造二叉树 2 | 3 | 根据一棵树的中序遍历与后序遍历构造二叉树。 4 | 5 | 注意: 6 | 你可以假设树中没有重复的元素。 7 | 8 | 例如,给出 9 | 10 | 中序遍历 inorder = [9,3,15,20,7] 11 | 后序遍历 postorder = [9,15,7,20,3] 12 | 返回如下的二叉树: 13 | 14 | 3 15 | / \ 16 | 9 20 17 | / \ 18 | 15 7 19 | 20 | 21 | class Solution { 22 | public: 23 | TreeNode* buildTree(vector& inorder, vector& postorder) { 24 | 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /二叉树/108. 将有序数组转换为二叉搜索树.cpp: -------------------------------------------------------------------------------- 1 | 108. 将有序数组转换为二叉搜索树 2 | 3 | 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 4 | 5 | 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 6 | 7 | /** 8 | * Definition for a binary tree node. 9 | * struct TreeNode { 10 | * int val; 11 | * TreeNode *left; 12 | * TreeNode *right; 13 | * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 14 | * }; 15 | */ 16 | class Solution { 17 | public: 18 | TreeNode* sortedArrayToBST(vector& nums) { 19 | return helper(nums, 0, nums.size() - 1); 20 | } 21 | 22 | TreeNode* helper(vector& nums, int left, int right) { 23 | if (left > right) return nullptr; 24 | int mid = left + (right - left) / 2; 25 | TreeNode *root = new TreeNode(nums[mid]); 26 | root->left = helper(nums, left, mid - 1); 27 | root->right = helper(nums, mid + 1, right); 28 | return root; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /二叉树/109. 有序链表转换二叉搜索树.cpp: -------------------------------------------------------------------------------- 1 | 109. 有序链表转换二叉搜索树 2 | 3 | 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。 4 | 5 | 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 6 | 7 | class Solution { 8 | public: 9 | TreeNode* sortedListToBST(ListNode* head) { 10 | if (head == nullptr) return nullptr; 11 | if (head->next == nullptr) return new TreeNode(head->val); 12 | ListNode *slow = head, *fast = head, *pre = head; 13 | while (fast->next != nullptr && fast->next->next != nullptr) { 14 | pre = slow; 15 | slow = slow->next; 16 | fast = fast->next->next; 17 | } 18 | fast = slow->next; 19 | pre->next = nullptr; 20 | TreeNode* root = new TreeNode(slow->val); 21 | if (head != slow) root->left = sortedListToBST(head); 22 | root->right = sortedListToBST(fast); 23 | return root; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /二叉树/110. 平衡二叉树.cpp: -------------------------------------------------------------------------------- 1 | 110. 平衡二叉树 2 | 3 | class Solution { 4 | public: 5 | bool isBalanced(TreeNode* root) { 6 | if (root == nullptr) return true; 7 | if (abs(getDepth(root->left) - getDepth(root->right)) <= 1 8 | && isBalanced(root->left) 9 | && isBalanced(root->right)) return true; 10 | return false; 11 | } 12 | 13 | int getDepth(TreeNode* root) { 14 | if (root == nullptr) return 0; 15 | return 1 + max(getDepth(root->left), getDepth(root->right)); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /二叉树/111. 二叉树的最小深度.cpp: -------------------------------------------------------------------------------- 1 | 111. 二叉树的最小深度 2 | 3 | 给定一个二叉树,找出其最小深度。 4 | 5 | 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 6 | 7 | 说明:叶子节点是指没有子节点的节点。 8 | 9 | class Solution { 10 | public: 11 | int minDepth(TreeNode* root) { 12 | if (root == nullptr) return 0; 13 | int m = minDepth(root->left); 14 | int n = minDepth(root->right); 15 | if (root->left == nullptr) return 1 + n; 16 | if (root->right == nullptr) return 1 + m; 17 | return 1 + min(m, n); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /二叉树/1123. 最深叶节点的最近公共祖先.cpp: -------------------------------------------------------------------------------- 1 | 1123. 最深叶节点的最近公共祖先 2 | 3 | 给你一个有根节点的二叉树,找到它最深的叶节点的最近公共祖先。 4 | 5 | 回想一下: 6 | 7 | 叶节点 是二叉树中没有子节点的节点 8 | 树的根节点的 深度 为 0,如果某一节点的深度为 d,那它的子节点的深度就是 d+1 9 | 如果我们假定 A 是一组节点 S 的 最近公共祖先,S 中的每个节点都在以 A 为根节点的子树中,且 A 的深度达到此条件下可能的最大值。 10 |   11 | 注意:本题与力扣 865 重复:https://leetcode-cn.com/problems/smallest-subtree-with-all-the-deepest-nodes/ 12 | 13 | 思路: 14 | 15 | 设置一个全局lca和全局deepest深度值。然后在函数中每一层记录深度,如果一个节点的左右子树的深度都为deepest,那么lca就是当前节点。 16 | 17 | class Solution { 18 | public: 19 | int deepest; 20 | TreeNode* lca; 21 | TreeNode* lcaDeepestLeaves(TreeNode* root) { 22 | helper(root, 0); 23 | return lca; 24 | } 25 | int helper(TreeNode* node, int depth) { 26 | deepest = max(deepest, depth); 27 | if (node == nullptr) return depth; 28 | int left = helper(node->left, depth + 1); 29 | int right = helper(node->right, depth + 1); 30 | if (left == deepest && right == deepest) lca = node; 31 | return max(left, right); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /二叉树/113. 路径总和 II.cpp: -------------------------------------------------------------------------------- 1 | 113. 路径总和 II 2 | 3 | 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 4 | 说明: 叶子节点是指没有子节点的节点。 5 | 6 | 解题思路: 7 | 8 | 注意要sum为零并且左右子节点都为空的情况下才加入答案。 9 | out的push_back和pop_back成对出现。 10 | 11 | class Solution { 12 | public: 13 | vector> pathSum(TreeNode* root, int sum) { 14 | vector> res; 15 | vector out; 16 | helper(root, sum, res, out); 17 | return res; 18 | } 19 | 20 | void helper(TreeNode* root, int sum, vector>& res, vector& out) { 21 | if (root == nullptr) return; 22 | out.push_back(root->val); 23 | sum -= root->val; 24 | if (sum == 0 && root->left == nullptr && root->right == nullptr) res.push_back(out); 25 | helper(root->left, sum, res, out); 26 | helper(root->right, sum, res, out); 27 | out.pop_back(); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /二叉树/114. 二叉树展开为链表.cpp: -------------------------------------------------------------------------------- 1 | 114. 二叉树展开为链表 2 | 3 | 给定一个二叉树,原地将它展开为一个单链表。 4 | 5 | 解题思路: 6 | 7 | 先利用 DFS 的思路找到最左子节点,然后回到其父节点,把其父节点和右子节点断开。 8 | 将原左子结点连上父节点的右子节点上,然后再把原右子节点连到新右子节点的右子节点上,然后再回到上一父节点做相同操作。 9 | 10 | class Solution { 11 | public: 12 | void flatten(TreeNode* root) { 13 | if (root == nullptr) return; 14 | flatten(root->left); 15 | flatten(root->right); 16 | TreeNode *tmp = root->right; 17 | root->right = root->left; 18 | root->left = nullptr; 19 | while (root->right != nullptr) root = root->right; 20 | root->right = tmp; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /二叉树/116. 填充每个节点的下一个右侧节点指针.cpp: -------------------------------------------------------------------------------- 1 | 116. 填充每个节点的下一个右侧节点指针 2 | 3 | 给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下: 4 | 5 | struct Node { 6 | int val; 7 | Node *left; 8 | Node *right; 9 | Node *next; 10 | } 11 | 12 | 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。 13 | 初始状态下,所有 next 指针都被设置为 NULL。 14 | 15 | class Solution { 16 | public: 17 | void connect(TreeLinkNode *root) { 18 | if (!root) return; 19 | queue q; 20 | q.push(root); 21 | while (!q.empty()) { 22 | int n = q.size(); 23 | for (int i = 0; i < n; ++i) { 24 | TreeLinkNode* t = q.front(); 25 | q.pop(); 26 | if (i != n - 1) t->next = q.front(); 27 | else t->next = NULL; 28 | if (t->left) q.push(t->left); 29 | if (t->right) q.push(t->right); 30 | } 31 | } 32 | return; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /二叉树/124. 二叉树中的最大路径和.cpp: -------------------------------------------------------------------------------- 1 | 124. 二叉树中的最大路径和 2 | 3 | 给定一个非空二叉树,返回其最大路径和。 4 | 5 | 本题中,路径被定义为一条从树中任意节点出发,沿父节点-子节点连接, 6 | 达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。 7 | 8 | 9 | class Solution { 10 | public: 11 | int maxPathSum(TreeNode* root) { 12 | int ans = INT_MIN; 13 | gainSum(root, ans); 14 | return ans; 15 | } 16 | 17 | int gainSum(TreeNode* root, int& ans) { 18 | if (root == nullptr) return 0; 19 | // 递归计算左右子节点的最大贡献值 20 | int left = max(0, gainSum(root->left, ans)); 21 | int right = max(0, gainSum(root->right, ans)); 22 | // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值之和 23 | int sum = left + right + root->val; 24 | // 更新答案 25 | ans = max(sum, ans); 26 | // 返回节点的最大贡献值 27 | return max(left, right) + root->val; 28 | } 29 | }; 30 | 31 | 时间复杂度:O(N),其中 N 是二叉树中的节点个数。对每个节点访问不超过 2 次。 32 | 空间复杂度:O(N),其中 N 是二叉树中的节点个数。空间复杂度主要取决于递归调用层数, 33 | 最大层数等于二叉树的高度,最坏情况下,二叉树的高度等于二叉树中的节点个数。 34 | 35 | -------------------------------------------------------------------------------- /二叉树/129. 求根到叶子节点数字之和.cpp: -------------------------------------------------------------------------------- 1 | 129. 求根到叶子节点数字之和 2 | 3 | 给定一个二叉树,它的每个结点都存放一个 0-9 的数字, 4 | 每条从根到叶子节点的路径都代表一个数字。 5 | 6 | 例如,从根到叶子节点路径 1->2->3 代表数字 123。 7 | 计算从根到叶子节点生成的所有数字之和。 8 | 说明: 叶子节点是指没有子节点的节点。 9 | 10 | class Solution { 11 | public: 12 | int sumNumbers(TreeNode* root) { 13 | return helper(root, 0); 14 | } 15 | int helper(TreeNode* root, int sum) { 16 | if (root == nullptr) return 0; 17 | sum = sum * 10 + root->val; 18 | if (root->left == nullptr && root->right == nullptr) return sum; 19 | return helper(root->left, sum) + helper(root->right, sum); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /二叉树/144. 二叉树的前序遍历.cpp: -------------------------------------------------------------------------------- 1 | 144. 二叉树的前序遍历 2 | 3 | class Solution { 4 | public: 5 | vector preorderTraversal(TreeNode* root) { 6 | vector res; 7 | if (!root) return res; 8 | stack st; 9 | st.push(root); 10 | while (!st.empty()) { 11 | TreeNode *node = st.top(); st.pop(); 12 | res.push_back(node->val); 13 | if (node->right) st.push(node->right); 14 | if (node->left) st.push(node->left); 15 | } 16 | return res; 17 | } 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /二叉树/222. 完全二叉树的节点个数.cpp: -------------------------------------------------------------------------------- 1 | 222. 完全二叉树的节点个数 2 | 3 | 给出一个完全二叉树,求出该树的节点个数。 4 | 5 | 说明: 6 | 7 | 完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。 8 | 9 | 示例: 10 | 11 | 输入: 12 | 1 13 | / \ 14 | 2 3 15 | / \ / 16 | 4 5 6 17 | 18 | 输出: 6 19 | 20 | class Solution { 21 | public: 22 | int countNodes(TreeNode* root) { 23 | int nLeft = 0, nRight = 0; 24 | TreeNode *pLeft = root, *pRight = root; 25 | while (pLeft != nullptr) { 26 | nLeft++; 27 | pLeft = pLeft->left; 28 | } 29 | while (pRight != nullptr) { 30 | nRight++; 31 | pRight = pRight->right; 32 | } 33 | if (nLeft == nRight) return pow(2, nLeft) - 1; 34 | return 1 + countNodes(root->left) + countNodes(root->right); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /二叉树/230. 二叉搜索树中第K小的元素.cpp: -------------------------------------------------------------------------------- 1 | 230. 二叉搜索树中第K小的元素 2 | 3 | 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。 4 | 5 | class Solution { 6 | public: 7 | int kthSmallest(TreeNode* root, int k) { 8 | stack st; 9 | st.push(root); 10 | TreeNode *p = root; 11 | while (p || !st.empty()) { 12 | while (p) { 13 | st.push(p); 14 | p = p->left; 15 | } 16 | TreeNode *node = st.top(); st.pop(); 17 | p = node; 18 | --k; 19 | if (k == 0) return p->val; 20 | p = p->right; 21 | } 22 | return 0; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /二叉树/235. 二叉搜索树的最近公共祖先.cpp: -------------------------------------------------------------------------------- 1 | 235. 二叉搜索树的最近公共祖先 2 | 3 | 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 4 | 5 | 6 | class Solution { 7 | public: 8 | TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { 9 | if (root == nullptr) return nullptr; 10 | if (root->val > max(p->val, q->val)) return lowestCommonAncestor(root->left, p, q); 11 | if (root->val < min(p->val, q->val)) return lowestCommonAncestor(root->right, p, q); 12 | return root; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /二叉树/236. 二叉树的最近公共祖先.cpp: -------------------------------------------------------------------------------- 1 | 236. 二叉树的最近公共祖先 2 | 3 | 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 4 | 5 | 6 | class Solution { 7 | public: 8 | TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { 9 | if (root== nullptr || root == p || root == q) return root; 10 | TreeNode* left = lowestCommonAncestor(root->left, p, q); 11 | TreeNode* right = lowestCommonAncestor(root->right, p, q); 12 | if (left != nullptr && right != nullptr) return root; 13 | return (left != nullptr) ? left : right; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /二叉树/257. 二叉树的所有路径.cpp: -------------------------------------------------------------------------------- 1 | 257. 二叉树的所有路径 2 | 3 | 给定一个二叉树,返回所有从根节点到叶子节点的路径。 4 | 5 | class Solution { 6 | public: 7 | vector binaryTreePaths(TreeNode* root) { 8 | vector ans; 9 | string out; 10 | if (root != nullptr) helper(root, "", ans); 11 | return ans; 12 | } 13 | 14 | void helper(TreeNode* root, string out, vector& ans) { 15 | out += to_string(root->val); 16 | if (root->left == nullptr && root->right == nullptr) { 17 | ans.push_back(out); 18 | } 19 | if (root->left) helper(root->left, out + "->", ans); 20 | if (root->right) helper(root->right, out + "->", ans); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /二叉树/404. 左叶子之和.cpp: -------------------------------------------------------------------------------- 1 | 404. 左叶子之和 2 | 3 | 计算给定二叉树的所有左叶子之和。 4 | 5 | class Solution { 6 | public: 7 | int sumOfLeftLeaves(TreeNode* root) { 8 | if (!root || (!root->left && !root->right)) return 0; 9 | int sum = 0; 10 | queue q; 11 | q.push(root); 12 | while (!q.empty()) { 13 | TreeNode *node = q.front(); 14 | q.pop(); 15 | if (node->left && !node->left->left && !node->left->right) sum += node->left->val; 16 | if (node->left) q.push(node->left); 17 | if (node->right) q.push(node->right); 18 | } 19 | return sum; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /二叉树/429. N 叉树的层序遍历.cpp: -------------------------------------------------------------------------------- 1 | 429. N 叉树的层序遍历 2 | 3 | 给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。 4 | 5 | 树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。 6 | 7 | class Solution { 8 | public: 9 | vector> levelOrder(Node* root) { 10 | if (root == nullptr) return {}; 11 | vector> ans; 12 | queue q; 13 | q.push(root); 14 | while (!q.empty()) { 15 | vector out; 16 | for (int i = q.size(); i > 0; --i) { 17 | Node* node = q.front(); q.pop(); 18 | out.push_back(node->val); 19 | if (!node->children.empty()) 20 | for (int i = 0; i < node->children.size(); ++i) q.push(node->children[i]); 21 | } 22 | ans.push_back(out); 23 | } 24 | return ans; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /二叉树/440. 字典序的第K小数字.cpp: -------------------------------------------------------------------------------- 1 | 440. 字典序的第K小数字 2 | 3 | 给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字。 4 | 5 | 解释: 6 | 字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9], 7 | 所以第二小的数字是 10。 8 | 9 | 解题思路: 10 | 11 | 构造一个十叉树。 12 | 找K小的数字其实就是遍历查找这个十叉树的过程。具体看题解。 13 | 14 | class Solution { 15 | public: 16 | int findKthNumber(int n, int k) { 17 | int cur = 1; 18 | --k; 19 | while (k > 0) { 20 | long long step = 0, first = cur, last = cur + 1; 21 | while (first <= n) { 22 | step += min((long long)n + 1, last) - first; 23 | first *= 10; 24 | last *= 10; 25 | } 26 | if (step <= k) { 27 | ++cur; 28 | k -= step; 29 | } else { 30 | cur *= 10; 31 | --k; 32 | } 33 | } 34 | return cur; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /二叉树/513. 找树左下角的值.cpp: -------------------------------------------------------------------------------- 1 | 513. 找树左下角的值 2 | 3 | 给定一个二叉树,在树的最后一行找到最左边的值。 4 | 5 | class Solution { 6 | public: 7 | int findBottomLeftValue(TreeNode* root) { 8 | queue q; 9 | q.push(root); 10 | int ans = 0; 11 | while (!q.empty()) { 12 | 13 | int sz = q.size(); 14 | for (int i = 0; i < sz; ++i) { 15 | TreeNode *node = q.front(); q.pop(); 16 | if (i == 0) ans = node->val; 17 | if (node->left) q.push(node->left); 18 | if (node->right) q.push(node->right); 19 | } 20 | } 21 | return ans; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /二叉树/530. 二叉搜索树的最小绝对差.cpp: -------------------------------------------------------------------------------- 1 | 530. 二叉搜索树的最小绝对差 2 | 3 | 给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。 4 | 5 | class Solution { 6 | public: 7 | int getMinimumDifference(TreeNode* root) { 8 | stack st; 9 | TreeNode* p = root; 10 | int ans = INT_MAX; TreeNode* pre = nullptr; 11 | while (p != nullptr || !st.empty()) { 12 | while (p) { 13 | st.push(p); 14 | p = p->left; 15 | } 16 | p = st.top(); st.pop(); 17 | if (pre != nullptr) ans = min(ans, p->val - pre->val); 18 | pre = p; 19 | p = p->right; 20 | } 21 | return ans; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /二叉树/543. 二叉树的直径.cpp: -------------------------------------------------------------------------------- 1 | 543. 二叉树的直径.cpp 2 | 3 | 给定一棵二叉树,你需要计算它的直径长度。 4 | 一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。 5 | 6 | 思路: 7 | 8 | 直径其实就是左子树深度加上右子树深度。 9 | 10 | class Solution { 11 | public: 12 | int diameterOfBinaryTree(TreeNode* root) { 13 | int res = 0; 14 | maxDepth(root, res); 15 | return res; 16 | } 17 | int maxDepth(TreeNode* node, int& res) { 18 | if (!node) return 0; 19 | int left = maxDepth(node->left, res); 20 | int right = maxDepth(node->right, res); 21 | res = max(res, left + right); 22 | return max(left, right) + 1; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /二叉树/563. 二叉树的坡度.cpp: -------------------------------------------------------------------------------- 1 | 563. 二叉树的坡度 2 | 3 | 给定一个二叉树,计算 整个树 的坡度 。 4 | 5 | 一个树的 节点的坡度 定义即为,该节点左子树的节点之和和右子树节点之和的 差的绝对值 。 6 | 如果没有左子树的话,左子树的节点之和为 0 ;没有右子树的话也是一样。空结点的坡度是 0 。 7 | 8 | 整个树 的坡度就是其所有节点的坡度之和。 9 | 10 | class Solution { 11 | public: 12 | int findTilt(TreeNode* root) { 13 | if (root == nullptr) return 0; 14 | int res = 0; 15 | postorder(root, res); 16 | return res; 17 | } 18 | // 返回整个树的节点值的和 19 | // res是整棵树的坡度 20 | int postorder(TreeNode* root, int& res) { 21 | if (root == nullptr) return 0; 22 | int leftSum = postorder(root->left, res); 23 | int rightSum = postorder(root->right, res); 24 | res += abs(leftSum - rightSum); 25 | return root->val + leftSum + rightSum; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /二叉树/572. 另一个树的子树.cpp: -------------------------------------------------------------------------------- 1 | 572. 另一个树的子树 2 | 3 | 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。 4 | 5 | class Solution { 6 | public: 7 | bool isSubtree(TreeNode* t1, TreeNode* t2) { 8 | if (t1 == nullptr && t2 == nullptr) return true; 9 | if (t1 == nullptr || t2 == nullptr) return false; 10 | return isSameTree(t1, t2) || isSubtree(t1->left, t2) || isSubtree(t1->right, t2); 11 | } 12 | 13 | bool isSameTree(TreeNode* t1, TreeNode* t2) { 14 | if (t1 == nullptr && t2 == nullptr) return true; 15 | if (t1 == nullptr || t2 == nullptr) return false; 16 | return t1->val == t2->val && isSameTree(t1->left, t2->left) && isSameTree(t1->right, t2->right); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /二叉树/589. N叉树的前序遍历.cpp: -------------------------------------------------------------------------------- 1 | 589. N叉树的前序遍历 2 | 3 | /* 4 | * Definition for a Node. 5 | class Node { 6 | public: 7 | int val; 8 | vector children; 9 | 10 | Node() {} 11 | 12 | Node(int _val, vector _children) { 13 | val = _val; 14 | children = _children; 15 | } 16 | }; 17 | */ 18 | 19 | class Solution { 20 | public: 21 | vector preorder(Node* root) { 22 | vector res; 23 | preorder(res, root); 24 | return res; 25 | } 26 | 27 | void preorder(vector& res, Node* root) { 28 | if (!root) return; 29 | res.push_back(root->val); 30 | for (int i = 0; i < root->children.size(); ++i) { 31 | preorder(res, root->children[i]); 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /二叉树/590. N叉树的后序遍历.cpp: -------------------------------------------------------------------------------- 1 | 590. N叉树的后序遍历 2 | 3 | 给定一个 N 叉树,返回其节点值的后序遍历。 4 | 5 | 例如,给定一个 3叉树 : 6 | 7 | 返回其后序遍历: [5,6,3,2,4,1].  8 | 9 | 说明: 递归法很简单,你可以使用迭代法完成此题吗? 10 | 11 | 题解: 12 | 13 | 递归法真的简单吗? 14 | 15 | class Solution { 16 | public: 17 | vector postorder(Node* root) { 18 | vector res; 19 | postorder(res, root); 20 | return res; 21 | } 22 | 23 | void postorder(vector& res, Node* root) { 24 | if (!root) return; 25 | for (int i = 0; i < root->children.size(); ++i) { 26 | postorder(res, root->children[i]); 27 | } 28 | res.push_back(root->val); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /二叉树/617. 合并二叉树.cpp: -------------------------------------------------------------------------------- 1 | 617. 合并二叉树 2 | 3 | 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。 4 | 5 | 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。 6 | 7 | 示例 1: 8 | 9 | 输入: 10 | Tree 1 Tree 2 11 | 1 2 12 | / \ / \ 13 | 3 2 1 3 14 | / \ \ 15 | 5 4 7 16 | 17 | 输出: 18 | 合并后的树: 19 | 3 20 | / \ 21 | 4 5 22 | / \ \ 23 | 5 4 7 24 | 25 | 注意: 合并必须从两个树的根节点开始。 26 | 27 | 28 | class Solution { 29 | public: 30 | TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { 31 | if (!t1) return t2; 32 | if (!t2) return t1; 33 | TreeNode *node = new TreeNode(t1->val + t2->val); 34 | node->left = mergeTrees(t1->left, t2->left); 35 | node->right = mergeTrees(t1->right, t2->right); 36 | return node; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /二叉树/653. 两数之和 IV - 输入 BST.cpp: -------------------------------------------------------------------------------- 1 | 653. 两数之和 IV - 输入 BST 2 | 3 | 给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果, 4 | 则返回 true。 5 | 6 | class Solution { 7 | public: 8 | bool findTarget(TreeNode* root, int k) { 9 | unordered_set m; 10 | return helper(root, k, m); 11 | } 12 | bool helper(TreeNode* node, int k, unordered_set& m) { 13 | if (!node) return false; 14 | if (m.count(k - node->val)) return true; 15 | m.insert(node->val); 16 | return helper(node->left, k, m) || helper(node->right, k, m); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /二叉树/662. 二叉树最大宽度.cpp: -------------------------------------------------------------------------------- 1 | 662. 二叉树最大宽度 2 | 3 | https://leetcode-cn.com/problems/maximum-width-of-binary-tree/ 4 | 5 | 这道题目的关键是设计出一个pair类型的queue。 6 | pair的value是position。 7 | 8 | 然后注意到下层的左右节点的位置分别是上层的2*pos和2*pos+1。 9 | 10 | class Solution { 11 | public: 12 | int widthOfBinaryTree(TreeNode* root) { 13 | if (!root) return 0; 14 | queue> q; 15 | q.push({root, 1}); 16 | int ans = 0; 17 | while (!q.empty()) { 18 | int sz = q.size(); 19 | ans = max(ans, int(q.back().second - q.front().second + 1)); 20 | for (int i = 0; i < sz; ++i) { 21 | TreeNode* node = q.front().first; 22 | unsigned long long pos = q.front().second; 23 | q.pop(); 24 | if (node->left) q.push({node->left, 2 * pos}); 25 | if (node->right) q.push({node->right, 2 * pos + 1}); 26 | } 27 | } 28 | return ans; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /二叉树/687. 最长同值路径.cpp: -------------------------------------------------------------------------------- 1 | 687. 最长同值路径 2 | 3 | 给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。 4 | 注意:两个节点之间的路径长度由它们之间的边数表示。 5 | 6 | 示例 1: 7 | 输入: 8 | 5 9 | / \ 10 | 4 5 11 | / \ \ 12 | 1 1 5 13 | 输出: 14 | 2 15 | 16 | 思路: 17 | 注意这道题和124题 二叉树中的最大路径和 类似。都是选择左子树和右子树的gain做比较。 18 | 19 | class Solution { 20 | public: 21 | int longestUnivaluePath(TreeNode* root) { 22 | if (root == nullptr) return 0; 23 | int ans = 0; 24 | helper(root, ans); 25 | return ans; 26 | } 27 | int helper(TreeNode* root, int& ans) { 28 | if (root == nullptr) return 0; 29 | int left = helper(root->left, ans); 30 | int right = helper(root->right, ans); 31 | int pl = 0; 32 | int pr = 0; 33 | if (root->left != nullptr && root->val == root->left->val) pl = left + 1; 34 | if (root->right != nullptr && root->val == root->right->val) pr = right + 1; 35 | ans = max(ans, pl + pr); 36 | return max(pl, pr); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /二叉树/958. 二叉树的完全性检验.cpp: -------------------------------------------------------------------------------- 1 | 958. 二叉树的完全性检验 2 | 3 | 给定一个二叉树,确定它是否是一个完全二叉树。 4 | 5 | class Solution { 6 | public: 7 | bool isCompleteTree(TreeNode* root) { 8 | queue q; 9 | q.push(root); 10 | bool found = false; // found nullptr节点 11 | while (!q.empty()) { 12 | TreeNode *node = q.front(); q.pop(); 13 | if (node == nullptr) found = true; 14 | else { 15 | if (found) return false; 16 | q.push(node->left); 17 | q.push(node->right); 18 | } 19 | } 20 | return true; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /二叉树/剑指 Offer 33. 二叉搜索树的后序遍历序列.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 33. 二叉搜索树的后序遍历序列 2 | 3 | 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。 4 | 如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 5 | 6 | 题解: 7 | 8 | 这道题的关键在于寻找第一个大于根节点的节点记为m。 9 | 这时候i到m-1就是左子树,m到j-1就是右子树。 10 | 11 | class Solution { 12 | public: 13 | bool verifyPostorder(vector& postorder) { 14 | if (postorder.empty()) return true; 15 | return recur(postorder, 0, postorder.size() - 1); 16 | } 17 | 18 | bool recur(vector& postorder, int i, int j) { 19 | if (i >= j) return true; 20 | int p = i; 21 | while (postorder[p] < postorder[j]) ++p; 22 | int m = p; 23 | while (postorder[p] > postorder[j]) ++p; 24 | return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1); 25 | } 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /二叉树/剑指 Offer 34. 二叉树中和为某一值的路径.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 34. 二叉树中和为某一值的路径 2 | 3 | 输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。 4 | 5 | 示例: 6 | 给定如下二叉树,以及目标和 sum = 22, 7 | 8 | 5 9 | / \ 10 | 4 8 11 | / / \ 12 | 11 13 4 13 | / \ / \ 14 | 7 2 5 1 15 | 16 | 返回: 17 | [ 18 | [5,4,11,2], 19 | [5,8,4,5] 20 | ] 21 | 22 | class Solution { 23 | public: 24 | vector> pathSum(TreeNode* root, int sum) { 25 | vector> res; 26 | vector out; 27 | helper(root, sum, res, out); 28 | return res; 29 | } 30 | 31 | void helper(TreeNode* root, int sum, vector>& res, vector& out) { 32 | if (root == nullptr) return; 33 | out.push_back(root->val); 34 | sum -= root->val; 35 | if (sum == 0 && root->left == nullptr && root->right == nullptr) 36 | res.push_back(out); 37 | helper(root->left, sum, res, out); 38 | helper(root->right, sum, res, out); 39 | out.pop_back(); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /二叉树/剑指 Offer 36. 二叉搜索树与双向链表.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 36. 二叉搜索树与双向链表 2 | 3 | https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/ 4 | 5 | ### 解题思路 6 | 7 | 这道题的一个关键是设计一个全局prev节点指针。 8 | 利用递归中序遍历处理中间节点双向相连, 9 | 利用构造一个dummy指针处理首尾节点连接。 10 | 11 | 最后返回dummy->next。 12 | 13 | ### 代码 14 | 15 | class Solution { 16 | public: 17 | 18 | Node* prev = nullptr; 19 | Node* treeToDoublyList(Node* root) { 20 | // corner case: 21 | if (root == nullptr) return nullptr; 22 | Node *dummy = new Node(0, nullptr, nullptr); 23 | prev = dummy; 24 | // 把nodes全部连起来 25 | helper(root); 26 | // 把头尾连起来 27 | prev->right = dummy->right; 28 | dummy->right->left = prev; 29 | return dummy->right; 30 | } 31 | 32 | void helper(Node* cur) { 33 | if (cur == nullptr) return; 34 | helper(cur->left); 35 | prev->right = cur; 36 | cur->left = prev; 37 | prev = cur; 38 | helper(cur->right); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /二叉树/剑指 Offer 54. 二叉搜索树的第k大节点.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 54. 二叉搜索树的第k大节点 2 | 3 | 中序遍历 4 | 5 | class Solution { 6 | public: 7 | int kthLargest(TreeNode* root, int k) { 8 | TreeNode *p = root; 9 | stack st; 10 | vector ans; 11 | while (!st.empty() || p != nullptr) { 12 | while (p != nullptr) { 13 | st.push(p); 14 | p = p->left; 15 | } 16 | p = st.top(); 17 | st.pop(); 18 | ans.push_back(p->val); 19 | p = p->right; 20 | } 21 | return ans[ans.size() - k]; 22 | } 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /二叉树/面试题 04.02. 最小高度树.cpp: -------------------------------------------------------------------------------- 1 | 面试题 04.02. 最小高度树 2 | 3 | 给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。 4 | 5 | 示例: 6 | 7 | 给定有序数组: [-10,-3,0,5,9], 8 | 9 | 一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树: 10 | 11 | 0 12 | / \ 13 | -3 9 14 | / / 15 | -10 5 16 | 17 | class Solution { 18 | public: 19 | TreeNode* sortedArrayToBST(vector& nums) { 20 | return generateBST(nums, 0, nums.size() - 1); 21 | } 22 | 23 | TreeNode* generateBST(vector& nums, int left, int right) { 24 | if (left > right) return nullptr; 25 | int mid = left + (right - left) / 2; 26 | TreeNode *root = new TreeNode(nums[mid]); 27 | root->left = generateBST(nums, left, mid - 1); 28 | root->right = generateBST(nums, mid + 1, right); 29 | return root; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /二叉树/面试题 04.10. 检查子树.cpp: -------------------------------------------------------------------------------- 1 | 面试题 04.10. 检查子树 2 | 3 | 检查子树。你有两棵非常大的二叉树:T1,有几万个节点;T2,有几万个节点。设计一个算法,判断 T2 是否为 T1 的子树。 4 | 5 | 如果 T1 有这么一个节点 n,其子树与 T2 一模一样,则 T2 为 T1 的子树,也就是说,从节点 n 处把树砍断,得到的树与 T2 完全相同。 6 | 7 | class Solution { 8 | public: 9 | bool checkSubTree(TreeNode* t1, TreeNode* t2) { 10 | if (t1 == nullptr && t2 == nullptr) return true; 11 | if (t1 == nullptr || t2 == nullptr) return false; 12 | return isSameTree(t1, t2) || checkSubTree(t1->left, t2) || checkSubTree(t1->right, t2); 13 | } 14 | 15 | bool isSameTree(TreeNode* t1, TreeNode* t2) { 16 | if (t1 == nullptr && t2 == nullptr) return true; 17 | if (t1 == nullptr || t2 == nullptr) return false; 18 | return t1->val == t2->val && isSameTree(t1->left, t2->left) && isSameTree(t1->right, t2->right); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /位运算/136. 只出现一次的数字.cpp: -------------------------------------------------------------------------------- 1 | 136. 只出现一次的数字 2 | 3 | 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 4 | 5 | 题解: 6 | 7 | 这道题利用异或的性质: 8 | 任何数和 0 做异或运算,结果仍然是原来的数,即 a⊕0=a。 9 | 任何数和其自身做异或运算,结果是 0,即 a⊕a=0。 10 | 异或运算满足交换律和结合律,即 a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b。 11 | 12 | 所以我们把所有的数字进行异或这样的出来的就是那个只出现一次的数字。 13 | 14 | class Solution { 15 | public: 16 | int singleNumber(vector& nums) { 17 | for (int i = 1; i < nums.size(); ++i) { 18 | nums[0] ^= nums[i]; 19 | } 20 | return nums[0]; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /位运算/137. 只出现一次的数字 II.cpp: -------------------------------------------------------------------------------- 1 | 137. 只出现一次的数字 II 2 | 3 | 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。 4 | 5 | class Solution { 6 | public: 7 | int singleNumber(vector& nums) { 8 | int ans = 0; 9 | for (int i = 0; i < 32; ++i) { 10 | int sum = 0; 11 | // 统计每位二进制数出现1的次数,将次数mod3应该为0。不为零的部分就为结果数字。 12 | for (int j = 0; j < nums.size(); ++j) { 13 | sum += (nums[j] >> i) & 1; 14 | } 15 | ans |= (sum % 3) << i; 16 | } 17 | return ans; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /位运算/169. 多数元素.cpp: -------------------------------------------------------------------------------- 1 | 169. 多数元素 2 | 3 | 给定一个大小为 n 的数组,找到其中的多数元素。 4 | 多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 5 | 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 6 | 7 | class Solution { 8 | public: 9 | int majorityElement(vector& nums) { 10 | int ans = 0, n = nums.size(); 11 | for (int i = 0; i < 32; ++i) { 12 | int ones = 0, zeros = 0; 13 | for (int num : nums) { 14 | if (ones > n / 2 || zeros > n / 2) break; 15 | if ((num & (1 << i)) != 0) ++ones; 16 | else ++zeros; 17 | } 18 | if (ones > zeros) ans |= (1 << i); 19 | } 20 | return ans; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /位运算/191. 位1的个数.cpp: -------------------------------------------------------------------------------- 1 | 191. 位1的个数 2 | 3 | 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 4 | 5 | 题解: 6 | 7 | n & (n - 1)运算每次能够移除n最右边的1。 8 | 9 | 题解: 10 | 11 | 更直观的一种写法: 12 | 13 | class Solution { 14 | public: 15 | int hammingWeight(uint32_t n) { 16 | int ans = 0; 17 | for (int i = 0; i < 32; ++i) { 18 | ans += n & 1; 19 | n = n >> 1; 20 | } 21 | return ans; 22 | } 23 | }; 24 | 25 | 相对晦涩的一种写法: 26 | x = x & (x - 1) 清零最低位的1 27 | 28 | class Solution { 29 | public: 30 | int hammingWeight(uint32_t n) { 31 | int ans = 0; 32 | while (n) { 33 | ++ans; 34 | n = n & (n - 1); 35 | } 36 | return ans; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /位运算/231. 2的幂.cpp: -------------------------------------------------------------------------------- 1 | 231. 2的幂 2 | 3 | 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 4 | 5 | class Solution { 6 | public: 7 | bool isPowerOfTwo(int n) { 8 | if (n == 0) return false; 9 | long x = n; 10 | return (x & (-x)) == x; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /位运算/260. 只出现一次的数字 III.cpp: -------------------------------------------------------------------------------- 1 | 260. 只出现一次的数字 III 2 | 3 | 给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。 4 | 5 | 示例 : 6 | 7 | 输入: [1,2,1,3,2,5] 8 | 输出: [3,5] 9 | 10 | class Solution { 11 | public: 12 | vector singleNumber(vector& nums) { 13 | int xor_val = 0; 14 | // 把所有的元素进行异或操作 15 | for (int num : nums) { 16 | xor_val ^= num; 17 | } 18 | // 取异或值最后一个二进制位为 1 的数字作为 mask,如果是 1 则表示两个数字在这一位上不同 19 | int mask = xor_val & (-xor_val); 20 | vector ans(2, 0); 21 | // 通过与这个 mask 进行与操作,如果为 0 的分为一个数组,为 1 的分为另一个数组。 22 | for (int num : nums) { 23 | if ((num & mask) == 0) { 24 | ans[0] ^= num; 25 | } else { 26 | ans[1] ^= num; 27 | } 28 | } 29 | return ans; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /位运算/461. 汉明距离.cpp: -------------------------------------------------------------------------------- 1 | 461. 汉明距离 2 | 3 | 两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。 4 | 5 | 给出两个整数 x 和 y,计算它们之间的汉明距离。 6 | 7 | class Solution { 8 | public: 9 | int hammingDistance(int x, int y) { 10 | int tmp = x ^ y; 11 | int ans = 0; 12 | for (int i = 0; i < 32; ++i) { 13 | if (tmp & 1) ++ans; 14 | tmp >>= 1; 15 | } 16 | return ans; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /位运算/README.md: -------------------------------------------------------------------------------- 1 | 位运算的一些常见操作: 2 | 3 | x & 1 == 0 or 1 判断奇偶 4 | x = x & (x - 1) 清零最低位的1 5 | x & -x 得到最低位(最右边)的1 6 | x & -x = 0 7 | 8 | -------------------------------------------------------------------------------- /位运算/剑指 Offer 56 - I. 数组中数字出现的次数.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 56 - I. 数组中数字出现的次数 2 | 3 | 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。 4 | 5 | a & (-a) 可以获得a最低的非0位 ,比如a的二进制是 ??????10000,取反就是??????01111,加1就是??????10000。前面?的部分是和原来a相反的,相与必然都是0,所以最后整体相与的结果就是00000010000。 6 | 7 | class Solution { 8 | public: 9 | vector singleNumbers(vector& nums) { 10 | int xor_val = 0; 11 | // 把所有的元素进行异或操作 12 | for (int num : nums) { 13 | xor_val ^= num; 14 | } 15 | // 取异或值最后一个二进制位为 1 的数字作为 mask,如果是 1 则表示两个数字在这一位上不同 16 | int mask = xor_val & (-xor_val); 17 | vector ans(2, 0); 18 | // 通过与这个 mask 进行与操作,如果为 0 的分为一个数组,为 1 的分为另一个数组。 19 | for (int num : nums) { 20 | if ((num & mask) == 0) { 21 | ans[0] ^= num; 22 | } else { 23 | ans[1] ^= num; 24 | } 25 | } 26 | return ans; 27 | } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /位运算/剑指 Offer 65. 不用加减乘除做加法.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 65. 不用加减乘除做加法 2 | 3 | class Solution { 4 | public: 5 | int add(int a, int b) { 6 | while (b != 0) { 7 | int carry = (unsigned int) (a & b) << 1; // carry是进位 8 | a = a ^ b; // 非进位和 9 | b = carry; 10 | } 11 | return a; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /位运算/面试题 01.01. 判定字符是否唯一.cpp: -------------------------------------------------------------------------------- 1 | 面试题 01.01. 判定字符是否唯一 2 | 3 | 实现一个算法,确定一个字符串 s 的所有字符是否全都不同。 4 | 5 | class Solution { 6 | public: 7 | bool isUnique(string astr) { 8 | int x = 0; 9 | for (auto c : astr) { 10 | if (x & 1 << (c - 'a')) return false; 11 | else x |= 1 << (c - 'a'); 12 | } 13 | return true; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /位运算/面试题 05.01. 插入.cpp: -------------------------------------------------------------------------------- 1 | 面试题 05.01. 插入 2 | 3 | 插入。给定两个32位的整数N与M,以及表示比特位置的i与j。编写一种方法,将M插入N,使得M从N的第j位开始,到第i位结束。 4 | 假定从j位到i位足以容纳M,也即若M = 10 011,那么j和i之间至少可容纳5个位。例如,不可能出现j = 3和i = 2的情况,因为第3位和第2位之间放不下M。 5 | 6 | 示例1: 7 | 8 | 输入:N = 1024(10000000000), M = 19(10011), i = 2, j = 6 9 | 输出:N = 1100(10001001100) 10 | 11 | 题解: 12 | 13 | 先把N的i到j位清零,再把M左移i位,然后加到N中去。 14 | 15 | class Solution { 16 | public: 17 | int insertBits(int N, int M, int i, int j) { 18 | for (int k = i; k <= j; ++k) { 19 | if (N & (1 << k)) N -= (1 << k); 20 | } 21 | N += (M << i); 22 | return N; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /位运算/面试题 05.03. 翻转数位.cpp: -------------------------------------------------------------------------------- 1 | 面试题 05.03. 翻转数位 2 | 3 | 给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度。 4 | 5 | 题解: 6 | 7 | 统计前一段连续的 1 的个数,以及后一段连续 1 的个数,两者之和 +1 就是翻转一个 0->1 后连续 1 的个数。 8 | 坑:注意一定要把num转成unsigned int n,因为num可能是负数。 9 | 10 | class Solution { 11 | public: 12 | int reverseBits(int num) { 13 | int pre = 0, cur = 0; 14 | if (num == -1) return 32; 15 | int ans = 0; 16 | unsigned int n = num; 17 | while (n) { 18 | if (n & 1) { 19 | ++cur; 20 | } else { 21 | pre = cur; 22 | cur = 0; 23 | } 24 | ans = max(ans, pre + cur); 25 | n = n >> 1; 26 | } 27 | return ans + 1; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /位运算/面试题 05.06. 整数转换.cpp: -------------------------------------------------------------------------------- 1 | 面试题 05.06. 整数转换 2 | 3 | 整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。 4 | 5 | 思路: 6 | 7 | 先异或,再统计1的个数。 8 | 9 | class Solution { 10 | public: 11 | int convertInteger(int A, int B) { 12 | int ans = 0; 13 | unsigned int n = A ^ B; 14 | while (n != 0) { 15 | n = n & (n - 1); 16 | ++ans; 17 | } 18 | return ans; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /位运算/面试题 05.07. 配对交换.cpp: -------------------------------------------------------------------------------- 1 | 面试题 05.07. 配对交换 2 | 3 | 配对交换。编写程序,交换某个整数的奇数位和偶数位,尽量使用较少的指令(也就是说,位0与位1交换,位2与位3交换,以此类推)。 4 | 5 | 题解: 6 | 7 | 把偶数位置的数右移一位,把奇数位置的数左移一位,然后或起来。 8 | 9 | class Solution { 10 | public: 11 | int exchangeBits(int num) { 12 | int even = (num & 0xaaaaaaaa) >> 1; 13 | int odd = (num & 0x55555555) << 1; 14 | return even | odd; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /位运算/面试题 17.04. 消失的数字.cpp: -------------------------------------------------------------------------------- 1 | 面试题 17.04. 消失的数字 2 | 3 | 数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗? 4 | 5 | 题解: 6 | 7 | 把所有数字和0到n异或一下,最后的出来的数字就是缺失的整数。 8 | 9 | class Solution { 10 | public: 11 | int missingNumber(vector& nums) { 12 | int ans = 0; 13 | for (int i = 0; i < nums.size(); ++i) { 14 | ans ^= (i + 1) ^ nums[i]; 15 | } 16 | return ans; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /动态规划/*剑指 Offer 46. 把数字翻译成字符串.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 46. 把数字翻译成字符串 2 | 3 | https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/ 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /动态规划/053. 最大子序和.cpp: -------------------------------------------------------------------------------- 1 | 53. 最大子序和 2 | 3 | https://leetcode-cn.com/problems/maximum-subarray/ 4 | 5 | 给定一个整数数组 nums ,找到一个具有最大和的连续子数组 6 | (子数组最少包含一个元素),返回其最大和。 7 | 8 | 示例: 9 | 10 | 输入: [-2,1,-3,4,-1,2,1,-5,4] 11 | 输出: 6 12 | 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 13 | 进阶: 14 | 15 | 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 16 | 17 | 18 | class Solution { 19 | public: 20 | int maxSubArray(vector& nums) { 21 | int res = 0; 22 | vector dp(nums.size(), 0); 23 | res = dp[0] = nums[0]; 24 | for (int i = 1; i < nums.size(); ++i) { 25 | if (dp[i - 1] < 0) 26 | dp[i] = nums[i]; 27 | else 28 | dp[i] = dp[i - 1] + nums[i]; 29 | res = max(res, dp[i]); 30 | } 31 | return res; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /动态规划/062. 不同路径.cpp: -------------------------------------------------------------------------------- 1 | 62. 不同路径 2 | 3 | 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 4 | 5 | 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 6 | 7 | 问总共有多少条不同的路径? 8 | 9 | class Solution { 10 | public: 11 | int uniquePaths(int m, int n) { 12 | vector> dp(m, vector(n, 0)); 13 | for (int i = 0; i < m; ++i) dp[i][0] = 1; 14 | for (int i = 0; i < n; ++i) dp[0][i] = 1; 15 | for (int i = 1; i < m; ++i) { 16 | for (int j = 1; j < n; ++j) { 17 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 18 | } 19 | } 20 | return dp.back().back(); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /动态规划/063. 不同路径 II.cpp: -------------------------------------------------------------------------------- 1 | 63. 不同路径 II 2 | 3 | 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 4 | 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 5 | 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? 6 | 7 | class Solution { 8 | public: 9 | int uniquePathsWithObstacles(vector>& obstacleGrid) { 10 | int m = obstacleGrid.size(), n = obstacleGrid[0].size(); 11 | vector> dp(m, vector(n, 0)); 12 | for (int i = 0; i < m; ++i) { 13 | if (obstacleGrid[i][0] == 1) break; 14 | dp[i][0] = 1; 15 | } 16 | for (int i = 0; i < n; ++i) { 17 | if (obstacleGrid[0][i] == 1) break; 18 | dp[0][i] = 1; 19 | } 20 | for (int i = 1; i < m; ++i) { 21 | for (int j = 1; j < n; ++j) { 22 | if (obstacleGrid[i][j] == 1) dp[i][j] = 0; 23 | else dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 24 | } 25 | } 26 | return dp.back().back(); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /动态规划/064. 最小路径和.cpp: -------------------------------------------------------------------------------- 1 | 64. 最小路径和 2 | 3 | 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 4 | 5 | 说明:每次只能向下或者向右移动一步。 6 | 7 | class Solution { 8 | public: 9 | int minPathSum(vector>& grid) { 10 | int m = grid.size(), n = grid[0].size(); 11 | vector> dp(m, vector(n, 0)); 12 | dp[0][0] = grid[0][0]; 13 | 14 | for (int i = 1; i < m; ++i) dp[i][0] = grid[i][0] + dp[i - 1][0]; 15 | for (int j = 1; j < n; ++j) dp[0][j] = grid[0][j] + dp[0][j - 1]; 16 | for (int i = 1; i < m; ++i) { 17 | for (int j = 1; j < n; ++j) { 18 | dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; 19 | } 20 | } 21 | return dp.back().back(); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /动态规划/070. 爬楼梯.cpp: -------------------------------------------------------------------------------- 1 | 70. 爬楼梯 2 | 3 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 4 | 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 5 | 注意:给定 n 是一个正整数。 6 | 7 | class Solution { 8 | public: 9 | int climbStairs(int n) { 10 | vector dp(n + 1, 0); 11 | if (n <= 2) return n; 12 | dp[0] = 1; dp[1] = 1; 13 | for (int i = 2; i <= n; ++i) { 14 | dp[i] = dp[i - 1] + dp[i - 2]; 15 | } 16 | return dp.back(); 17 | } 18 | }; 19 | 20 | class Solution { 21 | public: 22 | int climbStairs(int n) { 23 | int p = 0, q = 0, r = 1; 24 | for (int i = 1; i <= n; ++i) { 25 | p = q; 26 | q = r; 27 | r = p + q; 28 | } 29 | return r; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /动态规划/091. 解码方法.cpp: -------------------------------------------------------------------------------- 1 | 91. 解码方法 2 | 3 | 一条包含字母 A-Z 的消息通过以下方式进行了编码: 4 | 5 | 'A' -> 1 6 | 'B' -> 2 7 | ... 8 | 'Z' -> 26 9 | 给定一个只包含数字的非空字符串,请计算解码方法的总数。 10 | 11 | 关键是看当前位置的数字能不能和前面的数字组成一种译码方式。 12 | 建立一维 dp 数组,其中 dp[i] 表示s中前i个字符组成的子串的解码方法的个数。 13 | 考虑不同情况: 14 | 1. dp[i] 至少和 dp[i - 1]一样多 15 | 2. 如果前面两位数<=26并且>=10,那么要加上dp[i - 2] 16 | 17 | class Solution { 18 | public: 19 | int numDecodings(string s) { 20 | vector f(s.size() + 1, 0); 21 | f[0] = 1; 22 | for (int i = 1; i <= s.size(); ++i) { 23 | if (s[i - 1] != '0') f[i] = f[i - 1]; 24 | if (i >= 2) { 25 | int t = (s[i - 2] - '0') * 10 + s[i - 1] - '0'; 26 | if (t >= 10 && t <= 26) f[i] += f[i - 2]; 27 | } 28 | } 29 | return f.back(); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /动态规划/096. 不同的二叉搜索树.cpp: -------------------------------------------------------------------------------- 1 | 96. 不同的二叉搜索树 2 | 3 | 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 4 | 5 | 示例: 6 | 7 | 输入: 3 8 | 输出: 5 9 | 解释: 10 | 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 11 | 12 | 1 3 3 2 1 13 | \ / / / \ \ 14 | 3 2 1 1 3 2 15 | / / \ \ 16 | 2 1 2 3 17 | 18 | 题解: 19 | 20 | 动态规划。以i为root的节点组成的二叉搜索树的种数是左子树乘以右子树的个数。 21 | dp[i - 1] * dp[n - i] 22 | 然后把所有节点为root的情况累加一下。 23 | 24 | class Solution { 25 | public: 26 | int numTrees(int n) { 27 | vector dp(n + 1); 28 | dp[0] = 1; 29 | for (int i = 1; i <= n; ++i) { 30 | for (int j = 0; j <= i - 1; ++j) { 31 | dp[i] += dp[j] * dp[i - 1 - j]; 32 | } 33 | } 34 | return dp.back(); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /动态规划/1143. 最长公共子序列.cpp: -------------------------------------------------------------------------------- 1 | 1143. 最长公共子序列 2 | 3 | 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。 4 | 5 | class Solution { 6 | public: 7 | int longestCommonSubsequence(string text1, string text2) { 8 | int m = text1.size(), n = text2.size(); 9 | vector> dp(m + 1, vector(n + 1, 0)); 10 | for (int i = 1; i <= m; ++i) { 11 | for (int j = 1; j <= n; ++j) { 12 | if (text1[i - 1] == text2[j - 1]) { 13 | dp[i][j] = max(dp[i - 1][j - 1] + 1, max(dp[i - 1][j], dp[i][j - 1])); 14 | } else { 15 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 16 | } 17 | } 18 | } 19 | return dp.back().back(); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /动态规划/118. 杨辉三角.cpp: -------------------------------------------------------------------------------- 1 | 118. 杨辉三角 2 | 3 | 给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。 4 | 5 | 示例: 6 | 7 | 输入: 5 8 | 输出: 9 | [ 10 | [1], 11 | [1,1], 12 | [1,2,1], 13 | [1,3,3,1], 14 | [1,4,6,4,1] 15 | ] 16 | 17 | class Solution { 18 | public: 19 | vector> generate(int numRows) { 20 | vector> ans(numRows, vector()); 21 | for (int i = 0; i < numRows; ++i) { 22 | ans[i].resize(i + 1, 1); 23 | for (int j = 1; j < i; ++j) { 24 | ans[i][j] = ans[i - 1][j - 1] + ans[i - 1][j]; 25 | } 26 | } 27 | return ans; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /动态规划/119. 杨辉三角 II.cpp: -------------------------------------------------------------------------------- 1 | 119. 杨辉三角 II 2 | 3 | 给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。 4 | 5 | 题解: 6 | 7 | 除了第一个和最后一个数字之外,其他的数字都是上一行左右两个值之和。 8 | 那么我们只需要两个 for 循环,除了第一个数为1之外,后面的数都是上一次循环的数值加上它前面位置的数值之和, 9 | 不停地更新每一个位置的值,便可以得到第n行的数字。 10 | 11 | class Solution { 12 | public: 13 | vector getRow(int rowIndex) { 14 | vector ans(rowIndex + 1); 15 | ans[0] = 1; 16 | for (int i = 1; i <= rowIndex; ++i) { 17 | for (int j = i; j >= 1; --j) { 18 | ans[j] += ans[j - 1]; 19 | } 20 | } 21 | return ans; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /动态规划/120. 三角形最小路径和.cpp: -------------------------------------------------------------------------------- 1 | 120. 三角形最小路径和 2 | 3 | 给定一个三角形 triangle ,找出自顶向下的最小路径和。 4 | 5 | class Solution { 6 | public: 7 | int minimumTotal(vector>& triangle) { 8 | int m = triangle.size(), n = triangle[m - 1].size(); 9 | int ans = INT_MAX; 10 | vector> dp(m, vector(n, 0)); 11 | dp[0][0] = triangle[0][0]; 12 | for (int i = 1; i < m; ++i) { 13 | for (int j = 0; j <= i; ++j) { 14 | if (j == 0) { 15 | dp[i][j] = dp[i - 1][j] + triangle[i][j]; 16 | } else if (j == i) { 17 | dp[i][j] = dp[i - 1][j - 1] + triangle[i][j]; 18 | } else { 19 | dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j]; 20 | } 21 | cout << i << " " << j << " " << dp[i][j] << endl; 22 | } 23 | } 24 | for (int j = 0; j < n; ++j) { 25 | ans = min(ans, dp[m - 1][j]); 26 | } 27 | return ans; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /动态规划/152. 乘积最大子数组.cpp: -------------------------------------------------------------------------------- 1 | 152. 乘积最大子数组 2 | 3 | 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 4 | 5 | 题解: 6 | 7 | 当nums[i]<0的时候,前面的最小值乘以nums[i]反而成了最大值,所以最小值也要记录下来。 8 | 9 | class Solution { 10 | public: 11 | int maxProduct(vector& nums) { 12 | int ans = INT_MIN; 13 | int imax = 1, imin = 1; 14 | for (int i = 0; i < nums.size(); ++i) { 15 | if (nums[i] < 0) swap(imax, imin); 16 | imax = max(imax * nums[i], nums[i]); 17 | imin = min(imin * nums[i], nums[i]); 18 | ans = max(ans, imax); 19 | } 20 | return ans; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /动态规划/198. 打家劫舍.cpp: -------------------------------------------------------------------------------- 1 | 198. 打家劫舍 2 | 3 | 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金, 4 | 影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统, 5 | 如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 6 | 给定一个代表每个房屋存放金额的非负整数数组, 7 | 计算你不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 8 | 9 | class Solution { 10 | public: 11 | int rob(vector& nums) { 12 | int ans; 13 | int size = nums.size(); 14 | if (size == 0) return 0; 15 | vector dp(size, 0); 16 | dp[0] = nums[0]; 17 | if (size == 1) return dp[0]; 18 | dp[1] = max(nums[0], nums[1]); 19 | for (int i = 2; i < nums.size(); ++i) { 20 | dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); 21 | } 22 | return dp.back(); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /动态规划/300. 最长上升子序列.cpp: -------------------------------------------------------------------------------- 1 | 300. 最长上升子序列 2 | 3 | 给定一个无序的整数数组,找到其中最长上升子序列的长度。 4 | 5 | 示例: 6 | 7 | 输入: [10,9,2,5,3,7,101,18] 8 | 输出: 4 9 | 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。 10 | 说明: 11 | 12 | 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。 13 | 你算法的时间复杂度应该为 O(n2) 。 14 | 进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗? 15 | 16 | 通过次数160,368提交次数354,214 17 | 18 | 题解: 19 | 20 | 这道题可以用DP做,也可以用二分查找做。 21 | 22 | class Solution { 23 | public: 24 | int lengthOfLIS(vector& nums) { 25 | vector dp(nums.size(), 1); // 犯过错误:注意这里初始化为1 26 | int res = 0; 27 | 28 | for (int i = 0; i < nums.size(); ++i) { 29 | for (int j = 0; j < i; ++j) { 30 | if (nums[j] < nums[i]) { 31 | dp[i] = max(dp[i], dp[j] + 1); 32 | } 33 | } 34 | res = max(res, dp[i]); // 犯过错误:这里res的比较要在大的循环里面而不是里层循环。 35 | } 36 | return res; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /动态规划/322. 零钱兑换.cpp: -------------------------------------------------------------------------------- 1 | 322. 零钱兑换 2 | 3 | 给定不同面额的硬币 coins 和一个总金额 amount。 4 | 编写一个函数来计算可以凑成总金额所需的最少的硬币个数。 5 | 如果没有任何一种硬币组合能组成总金额,返回 -1。 6 | 7 | 你可以认为每种硬币的数量是无限的。 8 | 9 | 示例 1: 10 | 11 | 输入:coins = [1, 2, 5], amount = 11 12 | 输出:3 13 | 解释:11 = 5 + 5 + 1 14 | 15 | class Solution { 16 | public: 17 | int coinChange(vector& coins, int amount) { 18 | vector dp(amount + 1, amount + 1); 19 | dp[0] = 0; 20 | for (int i = 0; i <= amount; ++i) { 21 | for (int j = 0; j < coins.size(); ++j) { 22 | if (coins[j] <= i) 23 | dp[i] = min(dp[i - coins[j]] + 1, dp[i]); 24 | } 25 | } 26 | return dp[amount] == amount + 1 ? -1 : dp[amount]; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /动态规划/337. 打家劫舍 III.cpp: -------------------------------------------------------------------------------- 1 | 337. 打家劫舍 III 2 | 3 | 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。 4 | 这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。 5 | 一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 6 | 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。 7 | 8 | 计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。 9 | 10 | class Solution { 11 | public: 12 | int rob(TreeNode* root) { 13 | vector ans = dfs(root); 14 | return max(ans[0], ans[1]); 15 | } 16 | vector dfs(TreeNode *root) { 17 | vector ans(2, 0); // ans[0]不包括自己,ans[1]包括自己 18 | if (root == nullptr) return ans; 19 | vector left = dfs(root->left); 20 | vector right = dfs(root->right); 21 | // 左边包括left或不包括left的最大值 加上 右边包括right或不包括right的最大值 22 | ans[0] = max(left[0], left[1]) + max(right[0], right[1]); 23 | // root本身加上左边不包括left加上右边不包括right 24 | ans[1] = root->val + left[0] + right[0]; 25 | return ans; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /动态规划/343. 整数拆分.cpp: -------------------------------------------------------------------------------- 1 | 343. 整数拆分 2 | 3 | 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 4 | 5 | class Solution { 6 | public: 7 | int integerBreak(int n) { 8 | vector dp(n + 1, 0); 9 | for (int i = 1; i <= n; ++i) { 10 | for (int j = 0; j < i; ++j) { 11 | dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j])); 12 | } 13 | } 14 | return dp.back(); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /动态规划/354. 俄罗斯套娃信封问题.cpp: -------------------------------------------------------------------------------- 1 | 354. 俄罗斯套娃信封问题 2 | 3 | 给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。 4 | 5 | 请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。 6 | 7 | 说明: 8 | 不允许旋转信封。 9 | 10 | 示例: 11 | 12 | 输入: envelopes = [[5,4],[6,4],[6,7],[2,3]] 13 | 输出: 3 14 | 解释: 最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。 15 | 16 | class Solution { 17 | public: 18 | int maxEnvelopes(vector>& envelopes) { 19 | int res = 0; 20 | int n = envelopes.size(); 21 | vector dp(n, 1); 22 | sort(envelopes.begin(), envelopes.end()); 23 | for (int i = 0; i < envelopes.size(); ++i) { 24 | for (int j = 0; j < i; ++j) { 25 | if (envelopes[i].first > envelopes[j].first && 26 | envelopes[i].second > envelopes[j].second) { 27 | dp[i] = max(dp[i], dp[j] + 1); 28 | } 29 | } 30 | res = max(res, dp[i]); 31 | } 32 | return res; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /动态规划/376. 摆动序列.cpp: -------------------------------------------------------------------------------- 1 | 376. 摆动序列 2 | 3 | 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。 4 | 5 | 例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。 6 | 相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。 7 | 8 | 给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。 9 | 10 | 题解: 11 | 12 | 考虑两个dp数组表示最大wiggle sequence的长度,一个是最后两位上升,一个是最后两位下降。 13 | 当nums[i-1] < nums[i]的时候是up=down+1,另外一种情况相反,也是类似。 14 | 15 | class Solution { 16 | public: 17 | int wiggleMaxLength(vector& nums) { 18 | if (nums.size() < 2) return nums.size(); 19 | int up = 1, down = 1; 20 | int ans = 0; 21 | for (int i = 1; i < nums.size(); ++i) { 22 | if (nums[i - 1] < nums[i]) up = down + 1; 23 | else if (nums[i - 1] > nums[i]) down = up + 1; 24 | } 25 | return max(up, down); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /动态规划/416. 分割等和子集.cpp: -------------------------------------------------------------------------------- 1 | 416. 分割等和子集 2 | 3 | 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 4 | 5 | 注意: 6 | 7 | 每个数组中的元素不会超过 100 8 | 数组的大小不会超过 200 9 | 示例 1: 10 | 11 | 输入: [1, 5, 11, 5] 12 | 13 | 输出: true 14 | 15 | 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 16 | 17 | class Solution { 18 | public: 19 | bool canPartition(vector& nums) { 20 | int sum = accumulate(nums.begin(), nums.end(), 0), target = sum >> 1; 21 | if (sum & 1) return false; 22 | vector dp(target + 1, false); 23 | dp[0] = true; 24 | for (int num : nums) { 25 | for (int i = target; i >= num; --i) { 26 | dp[i] = dp[i] || dp[i - num]; 27 | } 28 | } 29 | return dp[target]; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /动态规划/516. 最长回文子序列.cpp: -------------------------------------------------------------------------------- 1 | 516. 最长回文子序列 2 | 3 | 给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。 4 | 可以假设 s 的最大长度为 1000 。 5 | 6 | 设置dp[i][j]为从i到j的字符串的最长回文子序列。 7 | 如果s[i] == s[j],那么 dp[i][j] = dp[i + 1][j - 1] + 2 8 | 否则,为 max(dp[i][j - 1], dp[i + 1][j]) 9 | 10 | class Solution { 11 | public: 12 | int longestPalindromeSubseq(string s) { 13 | int n = s.size(); 14 | vector> dp(n, vector(n)); 15 | for (int i = n - 1; i >= 0; --i) { 16 | dp[i][i] = 1; 17 | for (int j = i + 1; j < n; ++j) { 18 | if (s[i] == s[j]) dp[i][j] = dp[i + 1][j - 1] + 2; 19 | else dp[i][j] = max(dp[i][j - 1], dp[i + 1][j]); 20 | } 21 | } 22 | return dp[0][n - 1]; 23 | } 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /动态规划/647. 回文子串.cpp: -------------------------------------------------------------------------------- 1 | 647. 回文子串 2 | 3 | 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 4 | 5 | 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 6 | 7 | 示例 1: 8 | 9 | 输入:"abc" 10 | 输出:3 11 | 解释:三个回文子串: "a", "b", "c" 12 | 示例 2: 13 | 14 | 输入:"aaa" 15 | 输出:6 16 | 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa" 17 |   18 | class Solution { 19 | public: 20 | int countSubstrings(string s) { 21 | 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /动态规划/651. 4键键盘.cpp: -------------------------------------------------------------------------------- 1 | 651. 4键键盘 2 | 3 | 假设你有一个特殊的键盘包含下面的按键: 4 | 5 | Key 1: (A):在屏幕上打印一个 'A'。 6 | Key 2: (Ctrl-A):选中整个屏幕。 7 | Key 3: (Ctrl-C):复制选中区域到缓冲区。 8 | Key 4: (Ctrl-V):将缓冲区内容输出到上次输入的结束位置,并显示在屏幕上。 9 | 10 | 现在,你只可以按键 N 次(使用上述四种按键),请问屏幕上最多可以显示几个 'A'呢? 11 | 12 | class Solution { 13 | public: 14 | int maxA(int N) { 15 | // dp[i]表示步骤总数为i时,能打印出的最多A的个数 16 | vector dp(N + 1, 0); 17 | for (int i = 0; i <= N; ++i) { 18 | dp[i] = i; 19 | for (int j = 1; j < i - 2; ++j) { 20 | dp[i] = max(dp[i], dp[j] * dp[i - j - 1]); 21 | } 22 | } 23 | return dp[N]; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /动态规划/887. 鸡蛋掉落.cpp: -------------------------------------------------------------------------------- 1 | 887. 鸡蛋掉落 2 | 3 | 你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。 4 | 5 | class Solution { 6 | public: 7 | int superEggDrop(int K, int N) { 8 | vector> dp(K + 1, vector(N + 1, 0)); 9 | int m = 0; 10 | while (dp[K][m] < N) { 11 | m++; 12 | for (int k = 1; k <= K; ++k) { 13 | dp[k][m] = dp[k][m - 1] + dp[k - 1][m - 1] + 1; 14 | } 15 | } 16 | return m; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /动态规划/README.md: -------------------------------------------------------------------------------- 1 | 只要遇到字符串的子序列或配准问题首先考虑动态规划DP。 2 | 3 | 只要遇到需要求出所有可能情况首先考虑用递归。 4 | -------------------------------------------------------------------------------- /动态规划/剑指 Offer 14- I. 剪绳子.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 14- I. 剪绳子 2 | 3 | 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1), 4 | 每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少? 5 | 例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。 6 | 7 | 示例 1: 8 | 9 | 输入: 2 10 | 输出: 1 11 | 解释: 2 = 1 + 1, 1 × 1 = 1 12 | 13 | 示例 2: 14 | 15 | 输入: 10 16 | 输出: 36 17 | 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36 18 | 19 | 20 | class Solution { 21 | public: 22 | int cuttingRope(int n) { 23 | vector dp(n + 1); 24 | for (int i = 2; i <= n; ++i) { 25 | int curMax = 0; 26 | for (int j = 1; j < i; ++j) { 27 | curMax = max(curMax, max(j * (i - j), j * dp[i - j])); 28 | } 29 | dp[i] = curMax; 30 | } 31 | return dp.back(); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /动态规划/剑指 Offer 47. 礼物的最大价值.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 47. 礼物的最大价值 2 | 3 | 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。 4 | 你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。 5 | 给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物? 6 | 7 | 示例 1: 8 | 9 | 输入: 10 | [ 11 |   [1,3,1], 12 |   [1,5,1], 13 |   [4,2,1] 14 | ] 15 | 16 | 输出: 12 17 | 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物 18 | 19 | class Solution { 20 | public: 21 | int maxValue(vector>& grid) { 22 | vector> dp(grid.size(), vector(grid[0].size(), 0)); 23 | dp[0][0] = grid[0][0]; 24 | for (int j = 1; j < grid[0].size(); ++j) { 25 | dp[0][j] = grid[0][j] + dp[0][j - 1]; 26 | } 27 | 28 | for (int i = 1; i < grid.size(); ++i) { 29 | dp[i][0] = grid[i][0] + dp[i - 1][0]; 30 | } 31 | 32 | for (int i = 1; i < grid.size(); ++i) 33 | for (int j = 1; j < grid[0].size(); ++j) { 34 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; 35 | } 36 | return dp.back().back(); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /动态规划/剑指 Offer 66. 构建乘积数组.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 66. 构建乘积数组 2 | 3 | 给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。 4 | 5 | 示例: 6 | 7 | 输入: [1,2,3,4,5] 8 | 输出: [120,60,40,30,24] 9 | 10 | 题解: 11 | 12 | 设计两个数组,一个记录左边的所有数字(不包括该数字)的乘积,一个记录右边的所有数字的乘积。 13 | 然后将两个乘积相乘。 14 | 15 | class Solution { 16 | public: 17 | vector constructArr(vector& nums) { 18 | int n = nums.size(); 19 | vector forward(n, 1); 20 | vector backward(n, 1); 21 | vector ans; 22 | 23 | for (int i = 0; i < n - 1; ++i) { 24 | forward[i + 1] = nums[i] * forward[i]; 25 | } 26 | for (int i = n - 1; i > 0; --i) { 27 | backward[i - 1] = nums[i] * backward[i]; 28 | } 29 | 30 | for (int i = 0; i < nums.size(); ++i) { 31 | ans.push_back(forward[i] * backward[i]); 32 | } 33 | return ans; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /动态规划/字节跳动高频面试题之圆环回原点问题.cpp: -------------------------------------------------------------------------------- 1 | 圆环回原点问题是字节跳动高频面试题,首先引用几个涉及本题的面经原文 2 | 3 | [1]0-12共13个数构成一个环,从0出发,每次走1步,走n步回到0共有多少种走法(2020.09 字节跳动-广告-后端) 4 | [2]0-8组成一个圆环,从0出发,每次可以逆时针和顺时针走,走n步能回到0有多少种情况(2020.09 字节跳动-people-后端) 5 | [3]0~9的环,从0出发,N步后能否走回0。(2020.07 字节跳动-电商-后端) 6 | [4]圆环回原点问题 (2020.08 字节跳动-飞书-后端) 7 | 8 | 圆环上有10个点,编号为0~9。从0出发,每次可以逆时针和顺时针走一步,问走n步回到0共有多少种走法。 9 | 10 | 输入: 2 11 | 输出: 2 12 | 解释:有2种方案。分别是0->1->0和0->9->0 13 | 14 | 本题考察动态规划。经过分析可知:走n步到0的方案数=走n-1步到1的方案数+走n-1步到9的方案数。 15 | 因此,如果设dp[n][i]为走n步到i的方案数,则动态规划的递推式为: 16 | 17 | [公式] 18 | 公式之所以取余是因为i-1或i+1可能会超过圆环0~9的范围 19 | 20 | class Solution: 21 | def backToOrigin(self,n): 22 | #点的个数为10 23 | length = 10 24 | dp = [[0 for i in range(length)] for j in range(n+1)] 25 | dp[0][0] = 1 26 | for i in range(1,n+1): 27 | for j in range(length): 28 | #dp[i][j]表示从0出发,走i步到j的方案数 29 | dp[i][j] = dp[i-1][(j-1+length)%length] + dp[i-1][(j+1)%length] 30 | return dp[n][0] 31 | -------------------------------------------------------------------------------- /动态规划/打靶问题: -------------------------------------------------------------------------------- 1 | 打靶问题的一种递归解法 2 | 3 | 问题:一个射击运动员打靶,靶共有10环,连开10枪打中90环的可能性有多少种? 4 | 请用递归算法编程实现。 5 | 6 | 思路:一个运动员打出x发子弹,总共命中n环(环数从 10环 到 0环),问命中n环有多少种方式? 7 | 当打第一法子弹时,运动员可以选择的环数的下限是lowlimit = n - 10*(x-1), 8 | 若 < 0 则置零;上限highlimit是 10,若n = 0则不设置上限,直接返回。 9 | 10 | #include 11 | using namespace std; 12 | int countX = 0; 13 | int lowlimit, highlimit; 14 | //有x发子弹,总共打出n环,问有多少种组合? 15 | void f(int x, int n) 16 | { 17 | if (x == 1 || n == 0) //如果只有1发子弹或者剩余要打的环数为0,则当前组合已经确定 18 | { 19 | countX++; 20 | return; 21 | } else { 22 | lowlimit = n - 10*(x-1); 23 | lowlimit = lowlimit < 0 ? 0 : lowlimit; //求出可以打的最小环数 24 | highlimit = n <= 10 ? n : 10; //求出可以打的最大环数 25 | for (int i = lowlimit; i <= highlimit; i++) //对于当前子弹每种可能的打法,递归探寻后继的打法 26 | { 27 | f(x-1, n-i); 28 | } 29 | } 30 | } 31 | 32 | int main() 33 | { 34 | int x, n; 35 | cin >> x >> n; 36 | f(x, n); 37 | cout << countX << endl; 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /动态规划/面试题 08.01. 三步问题.cpp: -------------------------------------------------------------------------------- 1 | 面试题 08.01. 三步问题 2 | 3 | 三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。 4 | 5 | class Solution { 6 | public: 7 | int waysToStep(int n) { 8 | if (n < 4) return n == 3 ? 4 : n; 9 | int a = 1, b = 2, c = 4; 10 | for (int i = 4; i <= n; ++i) { 11 | int tmp = (a + b) % 1000000007 + c; 12 | a = b; 13 | b = c; 14 | c = tmp % 1000000007; 15 | } 16 | return c; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /动态规划/面试题 08.06. 汉诺塔问题.cpp: -------------------------------------------------------------------------------- 1 | 面试题 08.06. 汉诺塔问题 2 | 3 | 在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。 4 | 一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制: 5 | (1) 每次只能移动一个盘子; 6 | (2) 盘子只能从柱子顶端滑出移到下一根柱子; 7 | (3) 盘子只能叠在比它大的盘子上。 8 | 9 | 请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。 10 | 11 | class Solution { 12 | public: 13 | void hanota(vector& A, vector& B, vector& C) { 14 | helper(A.size(), A, B, C); 15 | } 16 | void helper(int n, vector& A, vector& B, vector& C) { 17 | if (n == 1) { 18 | C.push_back(A.back()); 19 | A.pop_back(); 20 | return; 21 | } 22 | helper(n - 1, A, C, B); 23 | helper(1, A, B, C); 24 | helper(n - 1, B, A, C); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /动态规划/面试题 08.11. 硬币.cpp: -------------------------------------------------------------------------------- 1 | 面试题 08.11. 硬币 2 | 3 | 硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007) 4 | 5 | 题解分析: 6 | 7 | 注意这道题和Coin Change的区别是它不是求最小分币数量,而是求有多少种解法。 8 | 9 | 注意循环顺序。比如 1+1+1+1+1+5 和 1+1+1+1+5+1 在Coin Change的算法中被重复计算,但在本题目中看做是一种。 10 | 换句话说本题目求组合数,而Coin Change是求排列数。 11 | 12 | class Solution { 13 | public: 14 | int waysToChange(int n) { 15 | vector dp(n + 1, 0); 16 | vector coins = {1, 5, 10, 25}; 17 | dp[0] = 1; 18 | for (int j = 0; j < coins.size(); ++j) { 19 | for (int i = coins[j]; i <= n; ++i) { 20 | dp[i] = (dp[i] + dp[i - coins[j]]) % 1000000007; 21 | } 22 | } 23 | return dp[n]; 24 | } 25 | }; 26 | 27 | 时间复杂度:O(n) 28 | 空间复杂度:使用滚动数组思想优化后,只需要使用一个长度为 n + 1的一维数组,故渐进空间复杂度为 O(n)。 29 | -------------------------------------------------------------------------------- /动态规划/面试题 17.16. 按摩师.cpp: -------------------------------------------------------------------------------- 1 | 面试题 17.16. 按摩师 2 | 3 | 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。 4 | 在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。 5 | 6 | 题解: 7 | 8 | 这道题其实就是打家劫舍。 9 | 10 | class Solution { 11 | public: 12 | int massage(vector& nums) { 13 | vector dp(nums.size(), 0); 14 | if (nums.size() == 0) return 0; 15 | if (nums.size() == 1) return nums[0]; 16 | dp[0] = nums[0]; 17 | dp[1] = max(nums[0], nums[1]); 18 | for (int i = 2; i < nums.size(); ++i) { 19 | dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]); 20 | } 21 | return dp.back(); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /双指针/*209. 长度最小的子数组.cpp: -------------------------------------------------------------------------------- 1 | 209. 长度最小的子数组 2 | 3 | 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。 4 | 5 | 示例: 6 | 7 | 输入:s = 7, nums = [2,3,1,2,4,3] 8 | 输出:2 9 | 解释:子数组 [4,3] 是该条件下的长度最小的子数组。 10 | 11 | 进阶: 12 | 13 | 如果你已经完成了 O(n) 时间复杂度的解法, 请尝试 O(nlogn) 时间复杂度的解法。 14 | 15 | class Solution { 16 | public: 17 | int minSubArrayLen(int s, vector& nums) { 18 | int res = INT_MAX; 19 | int left = 0; 20 | int sum = 0; 21 | for (int i = 0; i < nums.size(); ++i) { 22 | sum += nums[i]; 23 | while (sum >= s) { 24 | res = min(res, i - left + 1); 25 | sum -= nums[left]; 26 | left++; 27 | } 28 | } 29 | return res == INT_MAX ? 0 : res; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /双指针/*283. 移动零.cpp: -------------------------------------------------------------------------------- 1 | 283. 移动零 2 | 3 | https://leetcode-cn.com/problems/move-zeroes/ 4 | 5 | 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 6 | 7 | 示例: 8 | 9 | 输入: [0,1,0,3,12] 10 | 输出: [1,3,12,0,0] 11 | 说明: 12 | 13 | 必须在原数组上操作,不能拷贝额外的数组。 14 | 尽量减少操作次数。 15 | 16 | ======================================== 17 | 题解: 18 | 19 | 把非零的数字移动到数组前面,然后将后面的数字改成0。 20 | 21 | class Solution { 22 | public: 23 | void moveZeroes(vector& nums) { 24 | int cur = 0; 25 | for (int i = 0; i < nums.size(); ++i){ 26 | if (nums[i] != 0){ 27 | nums[cur] = nums[i]; 28 | cur++; 29 | } 30 | } 31 | for (int i = cur; i < nums.size(); i++) { 32 | nums[i] = 0; 33 | } 34 | return; 35 | } 36 | }; 37 | 38 | 39 | -------------------------------------------------------------------------------- /双指针/011. 盛最多水的容器.cpp: -------------------------------------------------------------------------------- 1 | 11. 盛最多水的容器 2 | 3 | https://leetcode-cn.com/problems/container-with-most-water/ 4 | 5 | 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 6 | 7 | 说明:你不能倾斜容器,且 n 的值至少为 2。 8 | 9 | 示例: 10 | 11 | 输入:[1,8,6,2,5,4,8,3,7] 12 | 输出:49 13 | 14 | =========================================== 15 | 16 | class Solution { 17 | public: 18 | int maxArea(vector& height) { 19 | int res = 0; 20 | int left = 0, right = height.size() - 1; 21 | while (left < right) { 22 | res = max(res, min(height[left], height[right]) * (right - left)); 23 | (height[left] < height[right]) ? left++ : right--; 24 | } 25 | return res; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /双指针/016. 最接近的三数之和.cpp: -------------------------------------------------------------------------------- 1 | 16. 最接近的三数之和 2 | 3 | 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。 4 | 5 | 示例: 6 | 7 | 输入:nums = [-1,2,1,-4], target = 1 8 | 输出:2 9 | 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。 10 | 11 | class Solution { 12 | public: 13 | int threeSumClosest(vector& nums, int target) { 14 | int closet = nums[0] + nums[1] + nums[2]; 15 | sort(nums.begin(), nums.end()); 16 | int left = 0, right = 0; 17 | int diff = abs(target - closet); 18 | 19 | for (int k = 0; k < nums.size() - 2; k++) { 20 | int left = k + 1; right = nums.size() - 1; 21 | while (left < right) { 22 | int sum = nums[k] + nums[left] + nums[right]; 23 | if (abs(target - sum) < diff) { 24 | diff = abs(target - sum); 25 | closet = sum; 26 | } 27 | if (sum < target) left++; 28 | else right--; 29 | } 30 | 31 | } 32 | return closet; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /双指针/026. 删除排序数组中的重复项.cpp: -------------------------------------------------------------------------------- 1 | 26. 删除排序数组中的重复项 2 | 3 | https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ 4 | 5 | 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 6 | 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 7 | 8 | 示例 1: 9 | 10 | 给定数组 nums = [1,1,2], 11 | 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 12 | 你不需要考虑数组中超出新长度后面的元素。 13 | 14 | 示例 2: 15 | 16 | 给定 nums = [0,0,1,1,1,2,2,3,3,4], 17 | 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 18 | 你不需要考虑数组中超出新长度后面的元素。 19 | 20 | 题解: 21 | 22 | 设计两个快慢下标游标,storeIndex指向不重复元素的下标,i指向当前要处理的元素的下标。 23 | 当出现新不重复元素时++storeIndex,并且把nums[i]赋给nums[storeIndex]。 24 | 25 | class Solution { 26 | public: 27 | int removeDuplicates(vector& nums) { 28 | if (nums.size() == 0) return 0; 29 | int storeIndex = 0; 30 | for (int i = 0; i < nums.size(); ++i) { 31 | if (nums[i] != nums[storeIndex]) { 32 | nums[++storeIndex] = nums[i]; 33 | } 34 | } 35 | return storeIndex + 1; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /双指针/027. 移除元素.cpp: -------------------------------------------------------------------------------- 1 | 27. 移除元素 2 | 3 | https://leetcode-cn.com/problems/remove-element/ 4 | 5 | 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素, 6 | 并返回移除后数组的新长度。 7 | 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 8 | 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。 9 | 10 | 你不需要考虑数组中超出新长度后面的元素。 11 |   12 | class Solution { 13 | public: 14 | int removeElement(vector& nums, int val) { 15 | int i = 0; 16 | for (int j = 0; j < nums.size(); ++j) { 17 | if (nums[j] != val) { 18 | nums[i] = nums[j]; 19 | ++i; 20 | } 21 | } 22 | return i; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /双指针/1004. 最大连续1的个数 III.cpp: -------------------------------------------------------------------------------- 1 | 1004. 最大连续1的个数 III 2 | 3 | 给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。 4 | 5 | 返回仅包含 1 的最长(连续)子数组的长度。 6 | 7 | class Solution { 8 | public: 9 | int longestOnes(vector& nums, int k) { 10 | int res = 0, zero = 0, left = 0; 11 | for (int right = 0; right < nums.size(); ++right) { 12 | if (nums[right] == 0) ++zero; 13 | while (zero > k) { 14 | if (nums[left++] == 0) --zero; 15 | } 16 | res = max(res, right - left + 1); 17 | } 18 | return res; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /双指针/159. 至多包含两个不同字符的最长子串.cpp: -------------------------------------------------------------------------------- 1 | 159. 至多包含两个不同字符的最长子串 2 | 3 | 给定一个字符串 s ,找出 至多 包含两个不同字符的最长子串 t ,并返回该子串的长度。 4 | 5 | class Solution { 6 | public: 7 | int lengthOfLongestSubstringTwoDistinct(string s) { 8 | int ans = 0, left = 0; 9 | unordered_map m; 10 | for (int i = 0; i < s.size(); ++i) { 11 | m[s[i]]++; 12 | while (m.size() > 2) { 13 | if (--m[s[left]] == 0) m.erase(s[left]); 14 | ++left; 15 | } 16 | ans = max(ans, i - left + 1); 17 | } 18 | return ans; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /双指针/167. 两数之和 II - 输入有序数组.cpp: -------------------------------------------------------------------------------- 1 | 167. 两数之和 II - 输入有序数组 2 | 3 | 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 4 | 5 | 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。 6 | 7 | 说明: 8 | 9 | 返回的下标值(index1 和 index2)不是从零开始的。 10 | 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 11 | 示例: 12 | 13 | 输入: numbers = [2, 7, 11, 15], target = 9 14 | 输出: [1,2] 15 | 解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。 16 | 17 | class Solution { 18 | public: 19 | vector twoSum(vector& numbers, int target) { 20 | int l = 0, r = numbers.size() - 1; 21 | int sum = 0; 22 | while (l < r) { 23 | sum = numbers[l] + numbers[r]; 24 | if (target == sum) return {l + 1, r + 1}; 25 | else if (sum < target) 26 | ++l; 27 | else 28 | --r; 29 | } 30 | return {}; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /双指针/941. 有效的山脉数组.cpp: -------------------------------------------------------------------------------- 1 | 941. 有效的山脉数组 2 | 3 | 给定一个整数数组 A,如果它是有效的山脉数组就返回 true,否则返回 false。 4 | 5 | 让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组: 6 | 7 | A.length >= 3 8 | 在 0 < i < A.length - 1 条件下,存在 i 使得: 9 | A[0] < A[1] < ... A[i-1] < A[i] 10 | A[i] > A[i+1] > ... > A[A.length - 1] 11 |   12 | 示例 1: 13 | 输入:[2,1] 14 | 输出:false 15 | 16 | 示例 2: 17 | 输入:[3,5,5] 18 | 输出:false 19 | 20 | 示例 3: 21 | 输入:[0,3,2,1] 22 | 输出:true 23 | 24 | class Solution { 25 | public: 26 | bool validMountainArray(vector& arr) { 27 | int left = 0, right = arr.size() - 1; 28 | while (left < arr.size() - 1 && arr[left] < arr[left + 1]) ++left; 29 | while (right > 0 && arr[right] < arr[right - 1]) --right; 30 | return left > 0 && right < arr.size() - 1 && left == right; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /双指针/剑指 Offer 21. 调整数组顺序使奇数位于偶数前面.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 2 | 3 | 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。 4 | 5 | class Solution { 6 | public: 7 | vector exchange(vector& nums) { 8 | int left = 0, right = nums.size() - 1; 9 | while (left < right) { 10 | while (left < right && (nums[left] & 1) == 1) { 11 | ++left; 12 | } 13 | while (left < right && (nums[right] & 1) == 0) { 14 | --right; 15 | } 16 | swap(nums[left], nums[right]); 17 | } 18 | return nums; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /双指针/剑指 Offer 48. 最长不含重复字符的子字符串.cpp: -------------------------------------------------------------------------------- 1 | https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/ 2 | 3 | ### 解题思路 4 | 5 | 解释下程序中那个 if 条件语句中的两个条件 m.count(s[i]) && m[s[i]] > left, 6 | 因为一旦当前字符 s[i] 在 HashMap 已经存在映射,说明当前的字符已经出现过了, 7 | 而若 m[s[i]] > left 成立,说明之前出现过的字符在窗口内,那么如果要加上当前这个重复的字符,就要移除之前的那个, 8 | 所以让 left 赋值为 m[s[i]],由于 left 是窗口左边界的前一个位置 9 | (这也是 left 初始化为 -1 的原因,因为窗口左边界是从0开始遍历的), 10 | 所以相当于已经移除出滑动窗口了。 11 | 12 | 用left指针表示左边界,如果字符子串出现重复left的字符,说明left左边包括left自己都可以被放弃掉。 13 | 这时候就需要把left标记为新的下标。(注意这个字符和left的字符其实是一样) 14 | 不管有没有重复字符,我们每次都记录m[s[i]]下来。 15 | 16 | ### 代码 17 | 18 | class Solution { 19 | public: 20 | 21 | int lengthOfLongestSubstring(string s) { 22 | int res = 0, left = -1, n = s.size(); 23 | unordered_map m; // 字符:下标 24 | for (int i = 0; i < n; ++i) { 25 | if (m.count(s[i]) && m[s[i]] > left) { 26 | left = m[s[i]]; // m[s[i]]表示字符为s[i]的下标 27 | } 28 | m[s[i]] = i; 29 | res = max(res, i - left); 30 | } 31 | return res; 32 | } 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /双指针/剑指 Offer 57. 和为s的两个数字.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 57. 和为s的两个数字 2 | 3 | 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。 4 | 5 | 示例 1: 6 | 7 | 输入:nums = [2,7,11,15], target = 9 8 | 输出:[2,7] 或者 [7,2] 9 | 10 | class Solution { 11 | public: 12 | vector twoSum(vector& nums, int target) { 13 | int left = 0, right = nums.size() - 1; 14 | int sum = 0; 15 | while (right > left) { 16 | if (nums[left] + nums[right] == target) return vector{nums[left], nums[right]}; 17 | else if (nums[left] + nums[right] < target) ++left; 18 | else --right; 19 | } 20 | return {0, 0}; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /双指针/面试题 10.01. 合并排序的数组.cpp: -------------------------------------------------------------------------------- 1 | 面试题 10.01. 合并排序的数组 2 | 3 | 给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 4 | 编写一个方法,将 B 合并入 A 并排序。 5 | 6 | 解题思路: 7 | 8 | 尝试从数组的末端向前端移动。先选出A和B中较大的元素放到A的末尾。 9 | 10 | class Solution { 11 | public: 12 | void merge(vector& A, int m, vector& B, int n) { 13 | int index = m + n - 1; 14 | int pa = m - 1, pb = n - 1; 15 | while (pa >= 0 || pb >= 0) { 16 | int cur; 17 | if (pa == -1) cur = B[pb--]; 18 | else if (pb == -1) cur = A[pa--]; 19 | else if (A[pa] < B[pb]) cur = B[pb--]; 20 | else cur = A[pa--]; 21 | A[index--] = cur; 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /双指针/面试题 17.11. 单词距离.cpp: -------------------------------------------------------------------------------- 1 | 面试题 17.11. 单词距离 2 | 3 | 有个内含单词的超大文本文件,给定任意两个单词, 4 | 找出在这个文件中这两个单词的最短距离(相隔单词数)。 5 | 如果寻找过程在这个文件中会重复多次,而每次寻找的单词不同,你能对此优化吗? 6 | 7 | 示例: 8 | 9 | 输入:words = ["I","am","a","student","from","a","university","in","a","city"], word1 = "a", word2 = "student" 10 | 11 | 输出:1 12 | 13 | class Solution { 14 | public: 15 | int findClosest(vector& words, string word1, string word2) { 16 | int t1 = -1, t2 = -1, ans = words.size(); 17 | for (int i = 0; i < words.size(); ++i) { 18 | if (word1 == words[i]) t1 = i; 19 | else if (word2 == words[i]) t2 = i; 20 | if (t1 != -1 && t2 != -1) ans = min(ans, abs(t1 - t2)); 21 | if (ans == 1) break; 22 | } 23 | return ans; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /回溯算法/*093. 复原IP地址.cpp: -------------------------------------------------------------------------------- 1 | 93. 复原IP地址 2 | 3 | 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。 4 | 有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。 5 | 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效的 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效的 IP 地址。 6 | 7 |   8 | -------------------------------------------------------------------------------- /回溯算法/077. 组合.cpp: -------------------------------------------------------------------------------- 1 | 77. 组合 2 | 3 | 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 4 | 5 | 示例: 6 | 7 | 输入: n = 4, k = 2 8 | 输出: 9 | [ 10 | [2,4], 11 | [3,4], 12 | [2,3], 13 | [1,2], 14 | [1,3], 15 | [1,4], 16 | ] 17 | 18 | 题解: 19 | 20 | k即depth用来控制结果长度。 21 | 22 | class Solution { 23 | public: 24 | vector> combine(int n, int k) { 25 | vector out; 26 | vector> res; 27 | helper(n, res, out, 1, k); 28 | return res; 29 | } 30 | 31 | void helper(int n, vector>& res, vector& out, int level, int depth) { 32 | if (out.size() == depth) res.push_back(out); 33 | for (int i = level; i <= n; ++i) { 34 | out.push_back(i); 35 | helper(n, res, out, i + 1, depth); // 犯过错误: i + 1写成level + 1 36 | out.pop_back(); 37 | } 38 | 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /回溯算法/090. 子集 II.cpp: -------------------------------------------------------------------------------- 1 | 90. 子集 II 2 | 3 | 给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 4 | 5 | 说明:解集不能包含重复的子集。 6 | 7 | 示例: 8 | 9 | 输入: [1,2,2] 10 | 输出: 11 | [ 12 | [2], 13 | [1], 14 | [1,2,2], 15 | [2,2], 16 | [1,2], 17 | [] 18 | ] 19 | 20 | 题解: 21 | 22 | 子集II这道题一定要先排序。 23 | 24 | class Solution { 25 | public: 26 | vector> subsetsWithDup(vector& nums) { 27 | vector> ans; 28 | vector out; 29 | sort(nums.begin(), nums.end()); 30 | helper(ans, out, nums, 0); 31 | return ans; 32 | } 33 | 34 | void helper(vector>& ans, vector& out, vector& nums, int level) { 35 | ans.push_back(out); 36 | for (int i = level; i < nums.size(); ++i) { 37 | out.push_back(nums[i]); 38 | helper(ans, out, nums, i + 1); 39 | out.pop_back(); 40 | while (i + 1 < nums.size() && nums[i] == nums[i + 1]) ++i; 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /回溯算法/剑指 Offer 10- II. 青蛙跳台阶问题.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 10- II. 青蛙跳台阶问题 2 | 3 | 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 4 | 5 | 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 6 | 7 | 示例 1: 8 | 9 | 输入:n = 2 10 | 输出:2 11 | 示例 2: 12 | 13 | 输入:n = 7 14 | 输出:21 15 | 示例 3: 16 | 17 | 输入:n = 0 18 | 输出:1 19 | 20 | class Solution { 21 | public: 22 | int numWays(int n) { 23 | int a = 1, b = 1, sum = 0; 24 | if (n < 2) return 1; 25 | for (int i = 1; i < n; ++i) { 26 | sum = (a + b) % 1000000007; 27 | a = b; 28 | b = sum; 29 | } 30 | return sum; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /回溯算法/面试题 08.04. 幂集.cpp: -------------------------------------------------------------------------------- 1 | 面试题 08.04. 幂集 2 | 3 | 幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。 4 | 5 | 说明:解集不能包含重复的子集。 6 | 7 | class Solution { 8 | public: 9 | vector> ans; 10 | vector out; 11 | 12 | vector> subsets(vector& nums) { 13 | sort(nums.begin(), nums.end()); 14 | helper(nums, 0); 15 | return ans; 16 | } 17 | 18 | void helper(vector& nums, int level) { 19 | ans.push_back(out); 20 | for (int i = level; i < nums.size(); ++i) { 21 | out.push_back(nums[i]); 22 | helper(nums, i + 1); 23 | out.pop_back(); 24 | if (i < nums.size() - 1 && nums[i] == nums[i + 1]) continue; 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /回溯算法/面试题 08.07. 无重复字符串的排列组合.cpp: -------------------------------------------------------------------------------- 1 | 面试题 08.07. 无重复字符串的排列组合 2 | 3 | 无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。 4 | 5 | 示例1: 6 | 7 | 输入:S = "qwe" 8 | 输出:["qwe", "qew", "wqe", "weq", "ewq", "eqw"] 9 | 10 | 示例2: 11 | 12 | 输入:S = "ab" 13 | 输出:["ab", "ba"] 14 | 15 | class Solution { 16 | public: 17 | vector permutation(string S) { 18 | vector ans; 19 | string out; 20 | vector visited(S.size(), 0); 21 | helper(ans, out, S, visited, 0); 22 | return ans; 23 | } 24 | 25 | void helper(vector& ans, string& out, string& S, vector& visited, int depth) { 26 | if (depth == S.size()) ans.push_back(out); 27 | for (int i = 0; i < S.size(); ++i) { 28 | if (visited[i] == 0) { 29 | out.push_back(S[i]); 30 | visited[i] = 1; 31 | helper(ans, out, S, visited, depth + 1); 32 | out.pop_back(); 33 | visited[i] = 0; 34 | } 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /回溯算法/面试题 08.09. 括号.cpp: -------------------------------------------------------------------------------- 1 | 面试题 08.09. 括号 2 | 3 | 括号。设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。 4 | 5 | 说明:解集不能包含重复的子集。 6 | 7 | class Solution { 8 | public: 9 | vector generateParenthesis(int n) { 10 | vector ans; 11 | dfs(ans, "", n, n); 12 | return ans; 13 | } 14 | 15 | void dfs(vector& ans, string out, int left, int right) { 16 | if (left > right) return; 17 | if (left == 0 && right == 0) ans.push_back(out); 18 | else { 19 | if (left > 0) dfs(ans, out + '(', left - 1, right); 20 | if (right > 0) dfs(ans, out + ')', left, right - 1); 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /堆/347. 前 K 个高频元素.cpp: -------------------------------------------------------------------------------- 1 | 347. 前 K 个高频元素 2 | https://leetcode-cn.com/problems/top-k-frequent-elements/ 3 | 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 4 | 5 | 示例 1: 6 | 输入: nums = [1,1,1,2,2,3], k = 2 7 | 输出: [1,2] 8 | 9 | 示例 2: 10 | 输入: nums = [1], k = 1 11 | 输出: [1] 12 | 13 | class Solution { 14 | public: 15 | vector topKFrequent(vector& nums, int k) { 16 | unordered_map m; // val->freq; 17 | priority_queue> q; 18 | vector res; 19 | for (auto a : nums) ++m[a]; 20 | for (auto it : m) q.push({it.second, it.first}); 21 | for (int i = 0; i < k; ++i) { 22 | res.push_back(q.top().second); 23 | q.pop(); 24 | } 25 | return res; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /堆/面试题 17.14. 最小K个数.cpp: -------------------------------------------------------------------------------- 1 | 面试题 17.14. 最小K个数 2 | 3 | 设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。 4 | 5 | 题解: 6 | 7 | 找最小的K个数要建立一个大小为K的大顶堆,因为每次pop出去的数都比这个大顶堆中的任意一个数要大。 8 | 所以最后得到大顶堆中就是最小的K个数。 9 | 顺便复习一下priority_queued的API: 10 | top(), push(), emplace(), pop(), size(), empty() 11 | 12 | class Solution { 13 | public: 14 | vector smallestK(vector& arr, int k) { 15 | priority_queue, less> q; 16 | vector ans; 17 | for (int i = 0; i < arr.size(); ++i) { 18 | q.push(arr[i]); 19 | if (q.size() > k) q.pop(); 20 | } 21 | for (int i = 0; i < k; ++i) { 22 | ans.push_back(q.top()); 23 | q.pop(); 24 | } 25 | return ans; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /字符串/*290. 单词规律.cpp: -------------------------------------------------------------------------------- 1 | 290. 单词规律 2 | 3 | 给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。 4 | 5 | 这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。 6 | 7 | 示例1: 8 | 9 | 输入: pattern = "abba", str = "dog cat cat dog" 10 | 输出: true 11 | 示例 2: 12 | 13 | 输入:pattern = "abba", str = "dog cat cat fish" 14 | 输出: false 15 | 16 | ================================ 17 | 相似题目: 18 | 同构字符串 简单 19 | 单词规律 II 困难 20 | 21 | 22 | class Solution { 23 | public: 24 | bool wordPattern(string pattern, string s) { 25 | 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /字符串/006. Z 字形变换.cpp: -------------------------------------------------------------------------------- 1 | 6. Z 字形变换 2 | 3 | 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。 4 | 5 | 思路: 6 | 7 | 构造一个字符串数组,模拟打印过程依次把字母加到数组中去。 8 | 然后最后整理打印出来。 9 | 10 | class Solution { 11 | public: 12 | string convert(string s, int numRows) { 13 | if (numRows <= 1) return s; 14 | int n = s.length(), i = 0; 15 | string ans; 16 | vector vec(numRows); 17 | while (i < n) { 18 | for (int pos = 0; pos < numRows && i < n; ++pos) { 19 | vec[pos] += s[i++]; 20 | } 21 | for (int pos = numRows - 2; pos >= 1 && i < n; --pos) { 22 | vec[pos] += s[i++]; 23 | } 24 | } 25 | for (auto& str : vec) ans += str; 26 | return ans; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /字符串/008. 字符串转换整数 (atoi).cpp: -------------------------------------------------------------------------------- 1 | 8. 字符串转换整数 (atoi) 2 | 3 | 请你来实现一个 atoi 函数,使其能将字符串转换成整数。 4 | 在任何情况下,若函数不能进行有效的转换时,请返回 0 。 5 | 6 | class Solution { 7 | public: 8 | int myAtoi(string s) { 9 | int i = 0; 10 | int sign = 1; 11 | while (s[i] == ' ') ++i; 12 | if (s[i] == '+' || s[i] == '-') { 13 | sign = (s[i] == '+') ? 1 : -1; 14 | ++i; 15 | } 16 | long num = 0; 17 | while (s[i] <= '9' && s[i] >= '0' && i < s.size()) { 18 | num = num * 10 + s[i] - '0'; 19 | if (num * sign >= INT_MAX) return INT_MAX; 20 | else if (num * sign <= INT_MIN) return INT_MIN; 21 | ++i; 22 | } 23 | return num * sign; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /字符串/010. 正则表达式匹配.cpp: -------------------------------------------------------------------------------- 1 | 10. 正则表达式匹配 2 | 3 | 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。 4 | 5 | class Solution { 6 | public: 7 | bool isMatch(string s, string p) { 8 | if (p.empty()) return s.empty(); 9 | if (p.size() > 1 && p[1] == '*') { 10 | return isMatch(s, p.substr(2)) || (!s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p)); 11 | } else { 12 | return !s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1)); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /字符串/014. 最长公共前缀.cpp: -------------------------------------------------------------------------------- 1 | 14. 最长公共前缀 2 | 3 | 输入: ["flower","flow","flight"] 4 | 输出: "fl" 5 | 6 | 输入: ["dog","racecar","car"] 7 | 输出: "" 8 | 解释: 输入不存在公共前缀。 9 | 10 | class Solution { 11 | public: 12 | string longestCommonPrefix(vector& strs) { 13 | if (strs.size() == 0) return ""; 14 | string res; 15 | for (int j = 0; j < strs[0].size(); ++j) { // strs[0]的第j个字符 16 | char c = strs[0][j]; 17 | for (int i = 1; i < strs.size(); ++i) { //strs[i]个字符串 18 | if (strs[i][j] != c) { 19 | return res; 20 | } 21 | } 22 | res.push_back(c); 23 | } 24 | return res; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /字符串/043. 字符串相乘.cpp: -------------------------------------------------------------------------------- 1 | 43. 字符串相乘 2 | 3 | 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 4 | 5 | class Solution { 6 | public: 7 | string multiply(string num1, string num2) { 8 | int m = num1.size(), n = num2.size(); 9 | // 结果最多为m + n位数 10 | vector ans(m + n, 0); 11 | // 从个位数开始逐位相乘 12 | for (int i = m - 1; i >= 0; --i) 13 | for (int j = n - 1; j >= 0; --j) { 14 | int mul = (num1[i] - '0') * (num2[j] - '0'); 15 | int p1 = i + j, p2 = i + j + 1; 16 | int sum = mul + ans[p2]; 17 | ans[p2] = sum % 10; 18 | ans[p1] += sum / 10; 19 | } 20 | // 结果前缀可能存的0(未使用的位) 21 | int i = 0; 22 | while (i < ans.size() && ans[i] == 0) ++i; 23 | // 将计算结果转化为字符串 24 | string str; 25 | for (; i < ans.size(); ++i) str.push_back('0' + ans[i]); 26 | return str.size() == 0 ? "0" : str; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /字符串/049. 字母异位词分组.cpp: -------------------------------------------------------------------------------- 1 | 49. 字母异位词分组 2 | 3 | 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 4 | 5 | 示例: 6 | 7 | 输入: ["eat", "tea", "tan", "ate", "nat", "bat"] 8 | 输出: 9 | [ 10 | ["ate","eat","tea"], 11 | ["nat","tan"], 12 | ["bat"] 13 | ] 14 | 15 | 解题思路: 16 | 17 | 这道题的关键是构造一个map,然后sort str。 18 | 19 | class Solution { 20 | public: 21 | vector> groupAnagrams(vector& strs) { 22 | unordered_map> m; // str -> ans; 23 | vector> ans; 24 | for (auto& iter : strs) { 25 | string str = iter; 26 | sort(str.begin(), str.end()); // 关键是sort这个str 27 | m[str].push_back(iter); 28 | } 29 | for (auto& iter : m) { 30 | ans.push_back(iter.second); 31 | } 32 | return ans; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /字符串/058. 最后一个单词的长度.cpp: -------------------------------------------------------------------------------- 1 | 58. 最后一个单词的长度 2 | 3 | 给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。 4 | 5 | 如果不存在最后一个单词,请返回 0 。 6 | 7 | class Solution { 8 | public: 9 | int lengthOfLastWord(string s) { 10 | reverse(s.begin(), s.end()); 11 | for (int i = 0; i < s.size(); ++i) { 12 | if (s[i] != ' ') { 13 | int j = i; 14 | while (j < s.size() && s[j] != ' ') ++j; 15 | return j - i; 16 | } 17 | } 18 | return 0; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /字符串/067. 二进制求和.cpp: -------------------------------------------------------------------------------- 1 | 67. 二进制求和 2 | 3 | 给你两个二进制字符串,返回它们的和(用二进制表示)。 4 | 输入为 非空 字符串且只包含数字 1 和 0。 5 | 6 | class Solution { 7 | public: 8 | string addBinary(string a, string b) { 9 | int m = a.size() - 1, n = b.size() - 1, carry = 0; 10 | string ans; 11 | while (m >= 0 || n >= 0) { 12 | int v1 = m >= 0 ? a[m--] - '0' : 0; 13 | int v2 = n >= 0 ? b[n--] - '0' : 0; 14 | int sum = v1 + v2 + carry; 15 | ans = to_string(sum % 2) + ans; 16 | carry = sum / 2; 17 | } 18 | if (carry > 0) ans = '1' + ans; 19 | return ans; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /字符串/071. 简化路径.cpp: -------------------------------------------------------------------------------- 1 | 71. 简化路径 2 | 3 | 以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。 4 | 5 | class Solution { 6 | public: 7 | string simplifyPath(string path) { 8 | vector v; 9 | string ans; 10 | int i = 0; 11 | while (i < path.size()) { 12 | while (path[i] == '/' && i < path.size()) ++i; 13 | if (i == path.size()) break; 14 | int start = i, end = 0; 15 | while (i < path.size() && path[i] != '/') { 16 | ++i; 17 | } 18 | end = i - 1; 19 | string s = path.substr(start, end - start + 1); 20 | if (s == "..") { 21 | if (!v.empty()) v.pop_back(); 22 | } else if (s != ".") { 23 | v.push_back(s); 24 | } 25 | } 26 | if (v.empty()) return "/"; 27 | for (int i = 0; i < v.size(); ++i) { 28 | ans += '/' + v[i]; 29 | } 30 | return ans; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /字符串/093. 复原IP地址.cpp: -------------------------------------------------------------------------------- 1 | 93. 复原IP地址 2 | 3 | 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。 4 | 5 | class Solution { 6 | public: 7 | vector restoreIpAddresses(string s) { 8 | vector ans; 9 | for (int a = 1; a < 4; ++a) 10 | for (int b = 1; b < 4; ++b) 11 | for (int c = 1; c < 4; ++c) 12 | for (int d = 1; d < 4; ++d) { 13 | if (a + b + c + d == s.size()) { 14 | int A = stoi(s.substr(0, a)); 15 | int B = stoi(s.substr(a, b)); 16 | int C = stoi(s.substr(a + b, c)); 17 | int D = stoi(s.substr(a + b + c, d)); 18 | if (A <= 255 && B <= 255 && C <= 255 && D <= 255) { 19 | string t = to_string(A) + '.' + to_string(B) + '.' + to_string(C) + '.' + to_string(D); 20 | if (t.size() == s.size() + 3) ans.push_back(t); 21 | } 22 | } 23 | } 24 | return ans; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /字符串/125. 验证回文串.cpp: -------------------------------------------------------------------------------- 1 | 2 | 125. 验证回文串 3 | 4 | 5 | class Solution { 6 | public: 7 | bool isPalindrome(string s) { 8 | string sgood; 9 | for (char ch: s) { 10 | if (isalnum(ch)) { 11 | sgood += tolower(ch); 12 | } 13 | } 14 | int n = sgood.size(); 15 | int left = 0, right = n - 1; 16 | while (left < right) { 17 | if (sgood[left++] != sgood[right--]) return false; 18 | } 19 | return true; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /字符串/345. 反转字符串中的元音字母.cpp: -------------------------------------------------------------------------------- 1 | 345. 反转字符串中的元音字母 2 | 3 | https://leetcode-cn.com/problems/reverse-vowels-of-a-string/ 4 | 5 | 编写一个函数,以字符串作为输入,反转该字符串中的元音字母。 6 | 7 | class Solution { 8 | public: 9 | string reverseVowels(string s) { 10 | int left = 0, right = s.size() - 1; 11 | while (left < right) { 12 | while (left < right && !isVowel(s[left])) ++left; 13 | while (left < right && !isVowel(s[right])) --right; 14 | swap(s[left], s[right]); 15 | ++left; --right; 16 | } 17 | return s; 18 | } 19 | 20 | bool isVowel(char c) { 21 | set dict={'a','e','i','o','u','A','E','I','O','U'}; 22 | if (dict.count(c)) return true; 23 | return false; 24 | } 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /字符串/392. 判断子序列.cpp: -------------------------------------------------------------------------------- 1 | 392. 判断子序列 2 | 3 | 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。 4 | 5 | 你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。 6 | 7 | 示例 1: 8 | s = "abc", t = "ahbgdc" 9 | 10 | 返回 true. 11 | 12 | 示例 2: 13 | s = "axc", t = "ahbgdc" 14 | 15 | 返回 false. 16 | 17 | ======================================== 18 | 双指针i和j分别指向两个string。 19 | 20 | class Solution { 21 | public: 22 | bool isSubsequence(string s, string t) { 23 | int i = 0, j = 0; 24 | while (i < s.size() && j < t.size()) { 25 | if (s[i] == t[j]) { 26 | ++i; 27 | ++j; 28 | } else { 29 | ++j; 30 | } 31 | } 32 | return i == s.size(); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /字符串/415. 字符串相加.cpp: -------------------------------------------------------------------------------- 1 | 415. 字符串相加 2 | 3 | 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。 4 | 5 | class Solution { 6 | public: 7 | string addStrings(string num1, string num2) { 8 | string ans; 9 | int i = num1.size()- 1, j = num2.size() - 1, carry = 0; 10 | while (i >= 0 || j >= 0) { 11 | int n1 = i >= 0 ? num1[i] - '0' : 0; 12 | int n2 = j >= 0 ? num2[j] - '0' : 0; 13 | int tmp = n1 + n2 + carry; 14 | carry = tmp / 10; 15 | ans += to_string(tmp % 10); 16 | --i; --j; 17 | } 18 | if (carry) ans += "1"; 19 | reverse(ans.begin(), ans.end()); 20 | return ans; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /字符串/443. 压缩字符串.cpp: -------------------------------------------------------------------------------- 1 | 443. 压缩字符串 2 | 3 | 输入: 4 | ["a","a","b","b","c","c","c"] 5 | 6 | 输出: 7 | 返回 6 ,输入数组的前 6 个字符应该是:["a","2","b","2","c","3"] 8 | 9 | 说明: 10 | "aa" 被 "a2" 替代。"bb" 被 "b2" 替代。"ccc" 被 "c3" 替代。 11 | 12 | class Solution { 13 | public: 14 | int compress(vector& chars) { 15 | int curr = 0, res = 0; 16 | for (int start = 0, end = 0; start < chars.size(); start = end) { 17 | while (end < chars.size() && chars[start] == chars[end]) end++; 18 | chars[curr++] = chars[start]; 19 | 20 | if (end - start == 1) continue; 21 | 22 | for (auto c : to_string(end - start)) { 23 | chars[curr++] = c; 24 | } 25 | } 26 | return curr; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /字符串/541. 反转字符串 II.cpp: -------------------------------------------------------------------------------- 1 | 541. 反转字符串 II 2 | 3 | https://leetcode-cn.com/problems/reverse-string-ii/ 4 | 5 | 给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。 6 | 7 | 如果剩余字符少于 k 个,则将剩余字符全部反转。 8 | 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。 9 |   10 | 11 | 示例: 12 | 13 | 输入: s = "abcdefg", k = 2 14 | 输出: "bacdfeg" 15 | 16 | class Solution { 17 | public: 18 | string reverseStr(string s, int k) { 19 | for (int i = 0; i < s.size(); i += (2 * k)) { 20 | // 1. 每隔 2k 个字符的前 k 个字符进行反转 21 | // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符 22 | if (i + k <= s.size()) { 23 | reverse(s.begin() + i, s.begin() + i + k); 24 | continue; 25 | } 26 | // 3. 剩余字符少于 k 个,则将剩余字符全部反转。 27 | reverse(s.begin() + i, s.begin() + s.size()); 28 | } 29 | return s; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /字符串/剑指 Offer 05. 替换空格.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 05. 替换空格 2 | 3 | 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 4 | 5 | class Solution { 6 | public: 7 | string replaceSpace(string s) { 8 | int cnt = 0, len = s.size(); 9 | for (auto c : s) { 10 | if (c == ' ') ++cnt; 11 | } 12 | s.resize(len + cnt * 2); 13 | for (int i = len - 1, j = s.size() - 1; i >= 0; --i, --j) { 14 | if (s[i] != ' ') s[j] = s[i]; 15 | else { 16 | s[j - 2] = '%'; s[j - 1] = '2'; s[j] = '0'; 17 | j -= 2; 18 | } 19 | } 20 | return s; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /排序/088. 合并两个有序数组.cpp: -------------------------------------------------------------------------------- 1 | 88. 合并两个有序数组 2 | 3 | 给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。 4 | 5 | class Solution { 6 | public: 7 | void merge(vector& nums1, int m, vector& nums2, int n) { 8 | int i = m - 1, j = n - 1, k = m + n - 1; 9 | while (i >= 0 && j >=0) { 10 | if (nums1[i] < nums2[j]) nums1[k--] = nums2[j--]; 11 | else nums1[k--] = nums1[i--]; 12 | } 13 | while (j >= 0) nums1[k--] = nums2[j--]; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /排序/179. 最大数.cpp: -------------------------------------------------------------------------------- 1 | 179. 最大数 2 | 3 | 给定一组非负整数 nums,重新排列它们每位数字的顺序使之组成一个最大的整数。 4 | 注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。 5 | 6 | 示例 1: 7 | 输入:nums = [10,2] 8 | 输出:"210" 9 | 10 | 示例 2: 11 | 输入:nums = [3,30,34,5,9] 12 | 输出:"9534330" 13 | 14 | 示例 3: 15 | 输入:nums = [1] 16 | 输出:"1" 17 | 18 | 示例 4: 19 | 输入:nums = [10] 20 | 输出:"10" 21 | 22 | class Solution { 23 | public: 24 | string largestNumber(vector& nums) { 25 | string s; 26 | sort(nums.begin(), nums.end(), [](int a, int b) { 27 | return to_string(a) + to_string(b) > to_string(b) + to_string(a); 28 | }); 29 | 30 | for (int i = 0; i < nums.size(); ++i) { 31 | s += to_string(nums[i]); 32 | } 33 | 34 | if (s[0] == '0') return "0"; 35 | return s; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /排序/215. 数组中的第K个最大元素.cpp: -------------------------------------------------------------------------------- 1 | 215. 数组中的第K个最大元素 2 | 3 | class Solution { 4 | public: 5 | int findKthLargest(vector& nums, int k) { 6 | int left = 0, right = nums.size() - 1; 7 | while (true) { 8 | int pos = partition(nums, left, right); 9 | if (pos == k - 1) return nums[pos]; 10 | else if (pos > k - 1) { 11 | right = pos - 1; 12 | } else { 13 | left = pos + 1; 14 | } 15 | } 16 | } 17 | 18 | int partition(vector& nums, int left, int right) { 19 | int pivotal = nums[right]; 20 | while (left < right) { 21 | while (left < right && nums[left] >= pivotal) ++left; 22 | swap(nums[left], nums[right]); 23 | while (left < right && nums[right] <= pivotal) --right; 24 | swap(nums[left], nums[right]); 25 | } 26 | return right; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /排序/274. H 指数.cpp: -------------------------------------------------------------------------------- 1 | 274. H 指数 2 | 3 | 给定一位研究者论文被引用次数的数组(被引用次数是非负整数)。编写一个方法,计算出研究者的 h 指数。 4 | h 指数的定义:h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中) 5 | 总共有 h 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数 不超过 h 次。) 6 | 例如:某人的 h 指数是 20,这表示他已发表的论文中,每篇被引用了至少 20 次的论文总共有 20 篇。 7 | 8 | 示例: 9 | 10 | 输入:citations = [3,0,6,1,5] 11 | 输出:3 12 | 解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。 13 |   由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。 14 | 15 | class Solution { 16 | public: 17 | int hIndex(vector& citations) { 18 | 19 | // 0 1 3 5 6 20 | // N 篇论文中,有h篇每篇被引用次数大于等于h, 21 | // 剩下的N - h 篇每篇被引用次数小于等于h。 有多个符合条件的h的话取最大的那个。 22 | 23 | // 逆序枚举h比较容易理解 24 | // 当h=5时,说明第一篇论文的引用次数就应该大于等于5 25 | // 若不大于5,我们尝试h=4,那就要求第二篇论文引用次数大于等4(这样第2,3,4,5篇论文都大于等于4) 26 | 27 | int n = citations.size(); 28 | sort(citations.begin(), citations.end()); 29 | for (int i = n; i > 0; --i) { 30 | if (citations[n - i] >= i) return i; 31 | } 32 | return 0; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /排序/324. 摆动排序 II.cpp: -------------------------------------------------------------------------------- 1 | 324. 摆动排序 II 2 | 3 | 给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。 4 | 5 | class Solution { 6 | public: 7 | void wiggleSort(vector& nums) { 8 | vector tmp = nums; 9 | sort(tmp.begin(), tmp.end()); 10 | int n = nums.size(), j = (n + 1) / 2 - 1, k = n - 1; 11 | for (int i = 0; i < tmp.size(); ++i) { 12 | nums[i] = i & 1 ? tmp[k--] : tmp[j--]; 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /排序/905. 按奇偶排序数组.cpp: -------------------------------------------------------------------------------- 1 | 905. 按奇偶排序数组 2 | 3 | 给定一个非负整数数组 A,返回一个数组,在该数组中, A 的所有偶数元素之后跟着所有奇数元素。 4 | 你可以返回满足此条件的任何数组作为答案。 5 | 6 | class Solution { 7 | public: 8 | vector sortArrayByParity(vector& A) { 9 | for (int i = 0, j = 0; j < A.size(); ++j) { 10 | if (A[j] % 2 == 0) swap(A[i++], A[j]); 11 | } 12 | return A; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /排序/912. 排序数组.cpp: -------------------------------------------------------------------------------- 1 | 912. 排序数组 2 | 3 | 给你一个整数数组 nums,请你将该数组升序排列。 4 | 5 | class Solution { 6 | public: 7 | vector sortArray(vector& nums) { 8 | quicksort(nums, 0, nums.size() - 1); 9 | return nums; 10 | } 11 | void quicksort(vector& nums, int left, int right) { 12 | if (left < right) { 13 | int mid = partition(nums, left, right); 14 | quicksort(nums, left, mid - 1); 15 | quicksort(nums, mid + 1, right); 16 | } 17 | } 18 | int partition(vector& nums, int left, int right) { 19 | int pivotal = nums[right]; 20 | while (left < right) { 21 | while (left < right && nums[left] <= pivotal) ++left; 22 | swap(nums[left], nums[right]); 23 | while (left < right && nums[right] >= pivotal) --right; 24 | swap(nums[left], nums[right]); 25 | } 26 | return right; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /排序/922. 按奇偶排序数组 II.cpp: -------------------------------------------------------------------------------- 1 | 922. 按奇偶排序数组 II 2 | 3 | 给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。 4 | 5 | 对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。 6 | 7 | 你可以返回任何满足上述条件的数组作为答案。 8 | 9 | 示例: 10 | 11 | 输入:[4,2,5,7] 12 | 输出:[4,5,2,7] 13 | 解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。 14 | 15 | class Solution { 16 | public: 17 | vector sortArrayByParityII(vector& A) { 18 | int j = 1; 19 | for (int i = 0; i < A.size(); i += 2) { 20 | if (A[i] % 2 == 1) { 21 | while (A[j] % 2 == 1) j += 2; 22 | swap(A[i], A[j]); 23 | } 24 | } 25 | return A; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /排序/快速排序.cpp: -------------------------------------------------------------------------------- 1 | void quick_sort(int b[], int left, int right) { 2 | if (left < right) { 3 | int mid = get_mid(b, left, right); 4 | quick_sort(b, left, mid - 1); 5 | quick_sort(b, mid + 1, right); 6 | } 7 | } 8 | 9 | int get_mid(int b[], int left, int right) { 10 | int pivot = b[left]; 11 | while (left < right) { 12 | while(b[right] >= pivot && left < right) --right; 13 | b[left] = b[right]; 14 | while(b[left] <= pivot && left < right) ++left; 15 | b[right] = b[left]; 16 | } 17 | b[left] = pivot; 18 | return left; 19 | } 20 | -------------------------------------------------------------------------------- /排序/面试题 10.11. 峰与谷.cpp: -------------------------------------------------------------------------------- 1 | 面试题 10.11. 峰与谷 2 | 3 | 在一个整数数组中,“峰”是大于或等于相邻整数的元素,相应地,“谷”是小于或等于相邻整数的元素。 4 | 例如,在数组{5, 8, 4, 2, 3, 4, 6}中,{8, 6}是峰, {5, 2}是谷。现在给定一个整数数组,将该数组按峰与谷的交替顺序排序。 5 | 6 | class Solution { 7 | public: 8 | void wiggleSort(vector& nums) { 9 | vector tmp = nums; 10 | sort(tmp.begin(), tmp.end()); 11 | int n = nums.size(), j = (n + 1) / 2 - 1, k = n - 1; 12 | for (int i = 0; i < n; ++i) { 13 | if (i % 2 == 0) nums[i] = tmp[j--]; 14 | else nums[i] = tmp[k--]; 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /排序/面试题 16.16. 部分排序.cpp: -------------------------------------------------------------------------------- 1 | 面试题 16.16. 部分排序 2 | 3 | 给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。 4 | 注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。 5 | 6 | 思路: 7 | 8 | 只要考虑把两个边界值找出来。而且发现边界其实有规律,比如左边界一定是比它左边的值还要小,同理右边界是比它右边的值还要大。 9 | 这道题也可以考虑在一次循环中做出,只要考虑把 i 改成 array[i] - 1 - i 就可以了,但是影响可读性。写成两个循环关系不大。 10 | 11 | class Solution { 12 | public: 13 | vector subSort(vector& array) { 14 | int maxValue = INT_MIN, minValue = INT_MAX; 15 | int first = -1, last = -1; 16 | for (int i = 0; i < array.size(); ++i) { 17 | if (array[i] < maxValue) last = i; 18 | maxValue = max(maxValue, array[i]); 19 | } 20 | for (int i = array.size() - 1; i >= 0; --i) { 21 | if (array[i] > minValue) first = i; 22 | minValue = min(minValue, array[i]); 23 | } 24 | return {first, last}; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /数学/*470. 用 Rand7() 实现 Rand10().cpp: -------------------------------------------------------------------------------- 1 | 470. 用 Rand7() 实现 Rand10() 2 | 3 | 已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。 4 | 5 | 不要使用系统的 Math.random() 方法。 6 | 7 | class Solution { 8 | public: 9 | int rand10() { 10 | while (true) { 11 | int num = (rand7() - 1) * 7 + rand7(); // 等概率生成[1,49]范围的随机数 12 | if (num <= 40) return num % 10 + 1; // 拒绝采样,并返回[1,10]范围的随机数 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /数学/*剑指 Offer 20. 表示数值的字符串.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 20. 表示数值的字符串 2 | 3 | https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/ 4 | 5 | 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 6 | 例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"-1E-16"、"0123"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。 7 | 8 | class Solution { 9 | public: 10 | bool isNumber(string s) { 11 | 12 | } 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /数学/007. 整数反转.cpp: -------------------------------------------------------------------------------- 1 | 7. 整数反转 2 | 3 | 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。 4 | 5 | class Solution { 6 | public: 7 | int reverse(int x) { 8 | if (x / 10 == 0) return x; 9 | int y = 0; 10 | while (x) { 11 | if (y > INT_MAX / 10 || y < INT_MIN / 10) return 0; 12 | y = y * 10 + x % 10; 13 | x /= 10; 14 | } 15 | return y; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /数学/172. 阶乘后的零.cpp: -------------------------------------------------------------------------------- 1 | 172. 阶乘后的零 2 | 3 | https://leetcode-cn.com/problems/factorial-trailing-zeroes/ 4 | 5 | 给定一个整数 n,返回 n! 结果尾数中零的数量。 6 | 7 | 思路: 8 | 9 | 其实就是看有多少个5的因子和2的因子。又因为每包含一个5的因子必然包含2的因子。所以只要查看有多少5的因子即可。 10 | 11 | class Solution { 12 | public: 13 | int trailingZeroes(int n) { 14 | int ans = 0; 15 | while (n) { 16 | ans += n / 5; 17 | n /= 5; 18 | } 19 | return ans; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /数学/204. 计数质数: -------------------------------------------------------------------------------- 1 | 204. 计数质数 2 | 3 | 统计所有小于非负整数 n 的质数的数量。<<<<<<< 注意是小于n,不包括n。 4 | 5 | 示例 1: 6 | 7 | 输入:n = 10 8 | 输出:4 9 | 解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。 10 | 11 | https://leetcode-cn.com/problems/count-primes/ 12 | 13 | class Solution { 14 | public: 15 | int countPrimes(int n) { 16 | int res = 0; 17 | bool sign; 18 | if (n <= 1) return 0; 19 | for (int i = 2; i < n; ++i) { 20 | sign = true; 21 | for (int j = 2; j * j <= i; ++j) { 22 | if (i % j == 0) { 23 | sign = false; 24 | break; 25 | } 26 | } 27 | if (sign) ++res; 28 | } 29 | return res; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /数学/231. 2的幂.cpp: -------------------------------------------------------------------------------- 1 | 231. 2的幂 2 | 3 | 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 4 | 5 | 如何获取二进制中最右边的1:x & (-x) 6 | 如何将二进制中最右边的1设置为0:x & (x - 1) 7 | 8 | 如果一个二进制数是由一个1和若干个0组成,它一定是一个2的幂次方。 9 | 10 | class Solution { 11 | public: 12 | bool isPowerOfTwo(int n) { 13 | if (n == 0) return false; 14 | long x = n; 15 | return (x & (x - 1)) == 0; 16 | } 17 | }; 18 | 19 | 方法二: 20 | class Solution { 21 | public: 22 | bool isPowerOfTwo(int n) { 23 | if (n == 0) return false; 24 | long x = n; 25 | return (x & (-x)) == x; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /数学/258. 各位相加.cpp: -------------------------------------------------------------------------------- 1 | 258. 各位相加 2 | 3 | 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。 4 | 5 | 示例: 6 | 7 | 输入: 38 8 | 输出: 2 9 | 解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2。 由于 2 是一位数,所以返回 2。 10 | 11 | class Solution { 12 | public: 13 | int addDigits(int num) { 14 | while (num >= 10) { 15 | num = num / 10 + num % 10; 16 | } 17 | return num; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /数学/263. 丑数.cpp: -------------------------------------------------------------------------------- 1 | 263. 丑数 2 | 3 | 编写一个程序判断给定的数是否为丑数。 4 | 5 | 丑数就是只包含质因数 2, 3, 5 的正整数。 6 | 7 | 示例 1: 8 | 9 | 输入: 6 10 | 输出: true 11 | 解释: 6 = 2 × 3 12 | 示例 2: 13 | 14 | 输入: 8 15 | 输出: true 16 | 解释: 8 = 2 × 2 × 2 17 | 示例 3: 18 | 19 | 输入: 14 20 | 输出: false 21 | 解释: 14 不是丑数,因为它包含了另外一个质因数 7。 22 | 说明: 23 | 24 | 1 是丑数。 <<<<<<<<<<< 注意审题 25 | 输入不会超过 32 位有符号整数的范围: [−2^31, 2^31 − 1]。 26 | 27 | https://leetcode-cn.com/problems/ugly-number/ 28 | 29 | ==================================================== 30 | 31 | class Solution { 32 | public: 33 | bool isUgly(int num) { 34 | if (num == 0) return false; 35 | if (num == 1) return true; 36 | while (num != 0 && num % 2 == 0) { 37 | num /= 2; 38 | } 39 | while (num != 0 && num % 3 == 0) { 40 | num /= 3; 41 | } 42 | while (num != 0 && num % 5 == 0) { 43 | num /= 5; 44 | } 45 | return num == 1; 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /数学/397. 整数替换.cpp: -------------------------------------------------------------------------------- 1 | 397. 整数替换 2 | 3 | https://leetcode-cn.com/problems/integer-replacement/ 4 | 5 | 给定一个正整数 n,你可以做如下操作: 6 | 7 | 1. 如果 n 是偶数,则用 n / 2替换 n。 8 | 2. 如果 n 是奇数,则可以用 n + 1或n - 1替换 n。 9 | n 变为 1 所需的最小替换次数是多少? 10 | 11 | 示例 1: 12 | 13 | 输入: 14 | 8 15 | 16 | 输出: 17 | 3 18 | 19 | 解释: 20 | 8 -> 4 -> 2 -> 1 21 | 示例 2: 22 | 23 | 输入: 24 | 7 25 | 26 | 输出: 27 | 4 28 | 29 | 解释: 30 | 7 -> 8 -> 4 -> 2 -> 1 31 | 或 32 | 7 -> 6 -> 3 -> 2 -> 1 33 | 34 | class Solution { 35 | public: 36 | int integerReplacement(int n) { 37 | 38 | } 39 | }; 40 | 41 | 42 | -------------------------------------------------------------------------------- /数学/400. 第N个数字.cpp: -------------------------------------------------------------------------------- 1 | 400. 第N个数字 2 | 3 | 在无限的整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...中找到第 n 个数字。 4 | 5 | 注意: 6 | n 是正数且在32位整数范围内 ( n < 2^31)。 7 | 8 | -------------------------------------------------------------------------------- /数学/441. 排列硬币.cpp: -------------------------------------------------------------------------------- 1 | 441. 排列硬币 2 | 3 | 你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。 4 | 5 | 给定一个数字 n,找出可形成完整阶梯行的总行数。 6 | 7 | n 是一个非负整数,并且在32位有符号整型的范围内。 8 | 9 | 示例 1: 10 | 11 | n = 5 12 | 13 | 硬币可排列成以下几行: 14 | ¤ 15 | ¤ ¤ 16 | ¤ ¤ 17 | 18 | 因为第三行不完整,所以返回2. 19 | 20 | class Solution { 21 | public: 22 | int arrangeCoins(int n) { 23 | int i; 24 | for (i = 1; i <= n; ++i) { 25 | n -= i; 26 | } 27 | return i - 1; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /数学/507. 完美数.cpp: -------------------------------------------------------------------------------- 1 | 507. 完美数 2 | 3 | 对于一个 正整数,如果它和除了它自身以外的所有 正因子 之和相等,我们称它为 「完美数」。 4 | 5 | 给定一个 整数 n, 如果是完美数,返回 true,否则返回 false 6 | 7 | 思路: 8 | 9 | 对于n如果是平方数的话,那么我们此时相同的因子加来两次,所以我们要判断一下,如果相等,就不再加 num/i。 10 | 11 | class Solution { 12 | public: 13 | bool checkPerfectNumber(int num) { 14 | int sum = 1; 15 | for (int i = 2; i * i <= num; ++i) { 16 | if (num % i == 0) 17 | sum += i + (num / i == i ? 0 : num / i); 18 | } 19 | return num != 1 && sum == num; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /数学/剑指 Offer 16. 数值的整数次方.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 16. 数值的整数次方 2 | 3 | 实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。 4 | 5 | 很多细节需要考虑: 6 | 1. N需要是long long类型 7 | 2. quickPow()中,当N为0的时候要注意返回1.0 8 | 9 | class Solution { 10 | public: 11 | double myPow(double x, int n) { 12 | if (n == 0) return 1.0; 13 | if (n < 0) return 1.0 / quickPow(x, -(long long)n); 14 | return quickPow(x, (long long)n); 15 | } 16 | 17 | double quickPow(double x, long long N) { 18 | if (N == 0) return 1.0; 19 | double y = quickPow(x, N / 2); 20 | return (N % 2 == 0) ? y * y : y * y * x; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /数学/剑指 Offer 17. 打印从1到最大的n位数.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 17. 打印从1到最大的n位数 2 | 3 | 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。 4 | 5 | 示例 1: 6 | 7 | 输入: n = 1 8 | 输出: [1,2,3,4,5,6,7,8,9] 9 | 10 | ====================================================== 11 | dfs算法 12 | 13 | class Solution { 14 | public: 15 | vector printNumbers(int n) { 16 | vector res; 17 | string str; 18 | str.resize(n); 19 | printNumbers(n, 0, str, res); 20 | return res; 21 | } 22 | 23 | void printNumbers(int n, int index, string &str, vector &res) { 24 | if (index == n) { 25 | int num = atoi(str.c_str()); 26 | if (num != 0) res.push_back(num); 27 | return; 28 | } 29 | for (int i = 0; i <= 9; ++i) { 30 | str[index] = i + '0'; 31 | printNumbers(n, index + 1, str, res); 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /数学/剑指 Offer 65. 不用加减乘除做加法.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 65. 不用加减乘除做加法 2 | 3 | class Solution { 4 | public: 5 | int add(int a, int b) { 6 | while (b != 0) { 7 | int c = (unsigned int) (a & b) << 1; // c是进位 8 | a = a ^ b; // 非进位和 9 | b = c; 10 | } 11 | return a; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /数学/面试题 16.07. 最大数值.cpp: -------------------------------------------------------------------------------- 1 | 面试题 16.07. 最大数值 2 | 3 | 编写一个方法,找出两个数字a和b中最大的那一个。不得使用if-else或其他比较运算符。 4 | 5 | 示例: 6 | 7 | 输入: a = 1, b = 2 8 | 输出: 2 9 | 10 | class Solution { 11 | public: 12 | int maximum(int a, int b) { 13 | long c = a; 14 | long d = b; 15 | return (long) (abs(c - d) + c + d) / 2; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /数学/面试题 17.09. 第 k 个数.cpp: -------------------------------------------------------------------------------- 1 | 面试题 17.09. 第 k 个数 2 | 3 | 有些数的素因子只有 3,5,7,请设计一个算法找出第 k 个数。注意,不是必须有这些素因子,而是必须不包含其他的素因子。例如,前几个数按顺序应该是 1,3,5,7,9,15,21。 4 | 5 | 题解分析: 6 | 7 | 这道题就是丑数II。需要注意的是要用long类型,不然会溢出。这道题最重要的步骤是去重。自己做的这次用了另外一个set来记录结果,主要是为了去重。 8 | 9 | 而丑数II这道题的解法用的是 10 | while (!q.empty() && q.top() == ans) q.pop(); 11 | 来去重。殊途同归。丑数II方法空间复杂度低,更好一点。 12 | 13 | class Solution { 14 | public: 15 | int getKthMagicNumber(int k) { 16 | priority_queue, greater> q; 17 | set m; 18 | q.push(1); 19 | while (true) { 20 | long val = q.top(); q.pop(); 21 | if (!m.count(val)) { 22 | m.insert(val); 23 | q.push(val * 3); 24 | q.push(val * 5); 25 | q.push(val * 7); 26 | } 27 | 28 | if (m.size() == k) return val; 29 | } 30 | return -1; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /数组/001. 两数之和.cpp: -------------------------------------------------------------------------------- 1 | 1. 两数之和 2 | 3 | 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 4 | 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 5 | 6 | class Solution { 7 | public: 8 | vector twoSum(vector& nums, int target) { 9 | unordered_map m; // val -> index 10 | for (int i = 0; i < nums.size(); ++i) { 11 | if (m.find(target - nums[i]) != m.end()) return {m[target - nums[i]], i}; 12 | m[nums[i]] = i; 13 | } 14 | return {}; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /数组/011. 盛最多水的容器.cpp: -------------------------------------------------------------------------------- 1 | 11. 盛最多水的容器 2 | https://leetcode-cn.com/problems/container-with-most-water/ 3 | 4 | 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。 5 | 在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 6 | 7 | 说明:你不能倾斜容器,且 n 的值至少为 2。 8 | 9 | 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 10 | 11 | 示例: 12 | 13 | 输入:[1,8,6,2,5,4,8,3,7] 14 | 输出:49 15 | 16 | ### 代码 17 | 18 | class Solution { 19 | public: 20 | int maxArea(vector& height) { 21 | int res = 0; 22 | int left = 0, right = height.size() - 1; 23 | 24 | while (left < right) { 25 | res = max(res, min(height[left], height[right]) * (right - left)); 26 | (height[left] < height[right]) ? left++ : right--; 27 | } 28 | 29 | return res; 30 | } 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /数组/026. 删除排序数组中的重复项.cpp: -------------------------------------------------------------------------------- 1 | 26. 删除排序数组中的重复项 2 | 3 | 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 4 | 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 5 | 6 | class Solution { 7 | public: 8 | int removeDuplicates(vector& nums) { 9 | if (nums.empty()) return 0; 10 | int slow = 0; 11 | for (int i = 1; i < nums.size(); ++i) { 12 | if (nums[i] != nums[slow]) { 13 | nums[++slow] = nums[i]; 14 | } 15 | } 16 | return slow + 1; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /数组/027. 移除元素.cpp: -------------------------------------------------------------------------------- 1 | 27. 移除元素 2 | 3 | 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素, 4 | 并返回移除后数组的新长度。 5 | 6 | class Solution { 7 | public: 8 | int removeElement(vector& nums, int val) { 9 | int slow = 0; 10 | for (int i = 0; i < nums.size(); ++i) { 11 | if (nums[i] != val) nums[slow++] = nums[i]; 12 | } 13 | return slow; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /数组/041. 缺失的第一个正数.cpp: -------------------------------------------------------------------------------- 1 | 41. 缺失的第一个正数 2 | 3 | 给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。 4 | 5 | 示例 1: 6 | 输入: [1,2,0] 7 | 输出: 3 8 | 9 | 示例 2: 10 | 输入: [3,4,-1,1] 11 | 输出: 2 12 | 13 | 示例 3: 14 | 输入: [7,8,9,11,12] 15 | 输出: 1 16 |   17 | 提示: 18 | 你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间。 19 | 20 | class Solution { 21 | public: 22 | int firstMissingPositive(vector& nums) { 23 | for (int i = 0; i < nums.size(); ++i) { 24 | while (nums[i] > 0 && nums[i] <= nums.size() && nums[i] != nums[nums[i] - 1]) { 25 | swap(nums[i], nums[nums[i] - 1]); 26 | } 27 | } 28 | for (int i = 0; i < nums.size(); ++i) { 29 | if (nums[i] != i + 1) return i + 1; 30 | } 31 | return nums.size() + 1; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /数组/042. 接雨水.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int trap(vector& height) { 4 | int res = 0; 5 | vector dp1(height.size(), 0); // dp1[i]表示第i位的左边的最大值 6 | vector dp2(height.size(), 0); // dp2[i]表示第i位的右边的最大值 7 | int mx = 0; 8 | for (int i = 0; i < height.size(); ++i) { 9 | dp1[i] = mx; 10 | mx = max(mx, height[i]); 11 | } 12 | mx = 0; 13 | for (int i = height.size() - 1; i >= 0; --i) { 14 | dp2[i] = mx; 15 | mx = max(mx, height[i]); 16 | } 17 | 18 | for (int i = 0; i < height.size(); ++i) { 19 | if (min(dp1[i], dp2[i]) - height[i] > 0) 20 | res += min(dp1[i], dp2[i]) - height[i]; 21 | } 22 | 23 | return res; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /数组/048. 旋转图像.cpp: -------------------------------------------------------------------------------- 1 | 48. 旋转图像 2 | 3 | 给定一个 n × n 的二维矩阵表示一个图像。 4 | 将图像顺时针旋转 90 度。 5 | 6 | 示例 1: 7 | 给定 matrix = 8 | [ 9 | [1,2,3], 10 | [4,5,6], 11 | [7,8,9] 12 | ], 13 | 使其变为: 14 | [ 15 | [7,4,1], 16 | [8,5,2], 17 | [9,6,3] 18 | ] 19 | 20 | class Solution { 21 | public: 22 | void rotate(vector>& matrix) { 23 | int n = matrix.size(); 24 | for (int i = 0; i < n; ++i) { 25 | for (int j = 0; j < n - i; ++j) { 26 | swap(matrix[i][j], matrix[n - j - 1][n - i - 1]); 27 | } 28 | } 29 | reverse(matrix.begin(), matrix.end()); 30 | } 31 | }; 32 | 33 | class Solution { 34 | public: 35 | void rotate(vector>& matrix) { 36 | for (int i = 0; i < matrix.size(); ++i) { 37 | for (int j = i + 1; j < matrix[0].size(); ++j) { 38 | swap(matrix[i][j], matrix[j][i]); 39 | } 40 | reverse(matrix[i].begin(), matrix[i].end()); 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /数组/054. 螺旋矩阵.cpp: -------------------------------------------------------------------------------- 1 | 54. 螺旋矩阵 2 | 3 | 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。 4 | 5 | 示例 1: 6 | 7 | 输入: 8 | [ 9 | [ 1, 2, 3 ], 10 | [ 4, 5, 6 ], 11 | [ 7, 8, 9 ] 12 | ] 13 | 输出: [1,2,3,6,9,8,7,4,5] 14 | 15 | class Solution { 16 | public: 17 | vector spiralOrder(vector>& matrix) { 18 | vector ans; 19 | int left = 0, right = matrix[0].size() - 1; 20 | int up = 0, down = matrix.size() - 1; 21 | while (true) { 22 | for (int j = left; j <= right; ++j) ans.push_back(matrix[up][j]); 23 | if (++up > down) break; 24 | for (int i = up; i <= down; ++i) ans.push_back(matrix[i][right]); 25 | if (--right < left) break; 26 | for (int j = right; j >= left; --j) ans.push_back(matrix[down][j]); 27 | if (--down < up) break; 28 | for (int i = down; i >= up; --i) ans.push_back(matrix[i][left]); 29 | if (++left > right) break; 30 | } 31 | return ans; 32 | } 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /数组/055. 跳跃游戏.cpp: -------------------------------------------------------------------------------- 1 | 55. 跳跃游戏 2 | 3 | 给定一个非负整数数组,你最初位于数组的第一个位置。 4 | 5 | 数组中的每个元素代表你在该位置可以跳跃的最大长度。 6 | 7 | 判断你是否能够到达最后一个位置。 8 | 9 | class Solution { 10 | public: 11 | bool canJump(vector& nums) { 12 | int reach = nums[0]; 13 | for (int i = 0; i <= reach; ++i) { 14 | reach = max(reach, i + nums[i]); 15 | if (reach >= nums.size() - 1) return true; 16 | } 17 | return false; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /数组/066. 加一.cpp: -------------------------------------------------------------------------------- 1 | 66. 加一 2 | 3 | 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 4 | 5 | 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 6 | 7 | 你可以假设除了整数 0 之外,这个整数不会以零开头。 8 | 9 | 示例 1: 10 | 11 | 输入: [1,2,3] 12 | 输出: [1,2,4] 13 | 解释: 输入数组表示数字 123。 14 | 15 | class Solution { 16 | public: 17 | vector plusOne(vector& digits) { 18 | int n = digits.size() - 1; 19 | for (int i = n; i >= 0; --i) { 20 | if (digits[i] == 9) { 21 | digits[i] = 0; 22 | } else { 23 | digits[i]++; 24 | return digits; 25 | } 26 | } 27 | if (digits[0] == 0) digits.insert(digits.begin(), 1); 28 | return digits; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /数组/084. 柱状图中最大的矩形.cpp: -------------------------------------------------------------------------------- 1 | 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 2 | 3 | 求在该柱状图中,能够勾勒出来的矩形的最大面积。 4 | 5 | 以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。 6 | 图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。 7 | 8 | 示例: 9 | 10 | 输入: [2,1,5,6,2,3] 11 | 输出: 10 12 | 13 | ============================================================================================= 14 | 15 | class Solution { 16 | public: 17 | int largestRectangleArea(vector& heights) { 18 | int res = 0; 19 | stack st; 20 | heights.push_back(0); 21 | for (int i = 0; i < heights.size(); ++i) { 22 | if (st.empty() || heights[st.top()] < heights[i]) { 23 | st.push(i); 24 | } else { 25 | int cur = st.top(); st.pop(); 26 | res = max(res, heights[cur] * (st.empty() ? i : (i - st.top() - 1))); 27 | --i; 28 | } 29 | } 30 | 31 | return res; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /数组/205. 同构字符串.cpp: -------------------------------------------------------------------------------- 1 | 205. 同构字符串 2 | 3 | 给定两个字符串 s 和 t,判断它们是否是同构的。 4 | 5 | 如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。 6 | 7 | 所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。 8 | 9 | class Solution { 10 | public: 11 | bool isIsomorphic(string s, string t) { 12 | if (s.size() != t.size()) return false; 13 | unordered_map m; // t -> s 14 | unordered_map n; // s -> t 15 | for (int i = 0; i < s.size(); ++i) { 16 | if (n.count(s[i]) == 0 && m.count(t[i]) == 0) { 17 | n[s[i]] = t[i]; 18 | m[t[i]] = s[i]; 19 | } else { 20 | if (m[t[i]] != s[i]) return false; 21 | } 22 | } 23 | return true; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /数组/209. 长度最小的子数组.cpp: -------------------------------------------------------------------------------- 1 | 209. 长度最小的子数组 2 | 3 | 给定一个含有 n 个正整数的数组和一个正整数 s , 4 | 找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。 5 | 如果不存在符合条件的子数组,返回 0。 6 | 7 | class Solution { 8 | public: 9 | int minSubArrayLen(int s, vector& nums) { 10 | int sum = 0, left = 0, ans = INT_MAX; 11 | for (int i = 0; i < nums.size(); ++i) { 12 | sum += nums[i]; 13 | while (sum >= s) { 14 | ans = min(ans, i - left + 1); 15 | sum -= nums[left]; 16 | left++; 17 | } 18 | } 19 | return ans == INT_MAX ? 0 : ans; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /数组/217. 存在重复元素.cpp: -------------------------------------------------------------------------------- 1 | 217. 存在重复元素 2 | 3 | 给定一个整数数组,判断是否存在重复元素。 4 | 5 | 如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。 6 | 7 | 示例 1: 8 | 9 | 输入: [1,2,3,1] 10 | 输出: true 11 | 示例 2: 12 | 13 | 输入: [1,2,3,4] 14 | 输出: false 15 | 示例 3: 16 | 17 | 输入: [1,1,1,3,3,4,3,2,4,2] 18 | 输出: true 19 | 20 | class Solution { 21 | public: 22 | bool containsDuplicate(vector& nums) { 23 | unordered_map m; 24 | for (int i = 0; i < nums.size(); ++i) { 25 | if (m.find(nums[i]) == m.end()) ++m[nums[i]]; 26 | else return true; 27 | } 28 | return false; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /数组/218. 天际线问题.cpp: -------------------------------------------------------------------------------- 1 | 218. 天际线问题 2 | 3 | 城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度,请返回由这些建筑物形成的 天际线 。 4 | 5 | 每个建筑物的几何信息由数组 buildings 表示,其中三元组 buildings[i] = [lefti, righti, heighti] 表示: 6 | 7 | lefti 是第 i 座建筑物左边缘的 x 坐标。 8 | righti 是第 i 座建筑物右边缘的 x 坐标。 9 | heighti 是第 i 座建筑物的高度。 10 | 天际线 应该表示为由 “关键点” 组成的列表,格式 [[x1,y1],[x2,y2],...] ,并按 x 坐标 进行 排序 。关键点是水平线段的左端点。 11 | 列表中最后一个点是最右侧建筑物的终点,y 坐标始终为 0 ,仅用于标记天际线的终点。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。 12 | 13 | 注意:输出天际线中不得有连续的相同高度的水平线。例如 [...[2 3], [4 5], [7 5], [11 5], [12 7]...] 是不正确的答案; 14 | 三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...] 15 | 16 | 示例 1: 17 | 18 | 输入:buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]] 19 | 输出:[[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]] 20 | 解释: 21 | 图 A 显示输入的所有建筑物的位置和高度, 22 | 图 B 显示由这些建筑物形成的天际线。图 B 中的红点表示输出列表中的关键点。 23 | 示例 2: 24 | 25 | 输入:buildings = [[0,2,3],[2,5,3]] 26 | 输出:[[0,3],[5,0]] 27 | -------------------------------------------------------------------------------- /数组/219. 存在重复元素 II.cpp: -------------------------------------------------------------------------------- 1 | 219. 存在重复元素 II 2 | 3 | https://leetcode-cn.com/problems/contains-duplicate-ii/ 4 | 5 | 给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。 6 | 7 | ====================================== 8 | 9 | 这道题目设计一个hashmap,建立value到index的映射。 10 | 每轮循环对num[i]找map中是否存在,如果不存在,则把下标记录下来。 11 | 如果找到,检查它的下标和当前下标i是否相差k以内,如果找到返回成功,否则记录当前的nums[i]和下标i到map中。 12 | 13 | class Solution { 14 | public: 15 | bool containsNearbyDuplicate(vector& nums, int k) { 16 | unordered_map m; // value->location 17 | for (int i = 0; i < nums.size(); ++i) { 18 | if (!m.count(nums[i])) { 19 | m[nums[i]] = i; 20 | } else { 21 | if (abs(m[nums[i]] - i) <= k) return true; 22 | else m[nums[i]] = i; 23 | } 24 | } 25 | return false; 26 | } 27 | }; 28 | 29 | 30 | -------------------------------------------------------------------------------- /数组/220. 存在重复元素 III.cpp: -------------------------------------------------------------------------------- 1 | 220. 存在重复元素 III 2 | 3 | https://leetcode-cn.com/problems/contains-duplicate-iii/ 4 | 5 | 在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。 6 | 7 | 如果存在则返回 true,不存在返回 false。 8 | 9 | 题解: 10 | 11 | 这里我们使用map数据结构来解,用来记录数字和其下标之间的映射。 12 | 这里需要两个指针i和j,刚开始i和j都指向0,然后i开始向右走遍历数组,如果i和j之差大于k,且m中有nums[j],则删除并j加一。 13 | 这样保证了m中所有的数的下标之差都不大于k。 14 | 然后我们用map数据结构的lower_bound()函数来找一个特定范围,就是大于或等于nums[i] - t的范围,所有小于这个阈值的数和nums[i]的差的绝对值会大于t。 15 | 然后检测后面的所有的数字,如果最小的数和nums[i]的差的绝对值小于等于t,则返回true。最后遍历完整个数组返回false。代码如下: 16 | 17 | class Solution { 18 | public: 19 | bool containsNearbyAlmostDuplicate(vector& nums, int k, int t) { 20 | map m; // value -> index 21 | int j = 0; 22 | for (int i = 0; i < nums.size(); ++i) { 23 | if (i - j > k) m.erase(nums[j++]); 24 | auto a = m.lower_bound((long long)nums[i] - t); 25 | if (a != m.end() && abs(a->first - nums[i]) <= t) return true; 26 | m[nums[i]] = i; 27 | } 28 | return false; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /数组/228. 汇总区间.cpp: -------------------------------------------------------------------------------- 1 | 给定一个无重复元素的有序整数数组 nums 。 2 | 3 | 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。 4 | 5 | 列表中的每个区间范围 [a,b] 应该按如下格式输出: 6 | 7 | "a->b" ,如果 a != b 8 | "a" ,如果 a == b 9 |   10 | 11 | class Solution { 12 | public: 13 | vector summaryRanges(vector& nums) { 14 | vector res; 15 | for (int i = 0; i < nums.size(); ++i) { 16 | int pos = i; 17 | string str = to_string(nums[i]); 18 | while (i < nums.size() - 1 && nums[i] + 1 == nums[i + 1]) ++i; 19 | if (pos != i) { 20 | str += "->" + to_string(nums[i]); 21 | } 22 | res.push_back(str); 23 | } 24 | return res; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /数组/238. 除自身以外数组的乘积.cpp: -------------------------------------------------------------------------------- 1 | 给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。 2 | 3 | 示例: 4 | 5 | 输入: [1,2,3,4] 6 | 输出: [24,12,8,6] 7 | 8 | 构建正反两个数组记录左边数字的乘积和以及右边数字的乘积和。 9 | 然后构造一个数组把左边乘积乘以右边乘积。 10 | 注意初始化的时候把左右两侧的数字初始化成1。 11 | 12 | class Solution { 13 | public: 14 | vector productExceptSelf(vector& nums) { 15 | int n = nums.size(); 16 | vector forward(n, 1); 17 | vector backward(n, 1); 18 | vector ans; 19 | 20 | for (int i = 0; i < n - 1; ++i) { 21 | forward[i + 1] = nums[i] * forward[i]; 22 | } 23 | for (int i = n - 1; i > 0; --i) { 24 | backward[i - 1] = nums[i] * backward[i]; 25 | } 26 | 27 | for (int i = 0; i < nums.size(); ++i) { 28 | ans.push_back(forward[i] * backward[i]); 29 | } 30 | return ans; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /数组/253. 会议室 II.cpp: -------------------------------------------------------------------------------- 1 | 253. 会议室 II 2 | 3 | 给你一个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 4 | intervals[i] = [starti, endi] ,为避免会议冲突,同时要考虑充分利用会议室资源, 5 | 请你计算至少需要多少间会议室,才能满足这些会议安排。 6 | 7 | 构造一个最小堆,把会议的结束时间进堆,如果当前堆顶的结束时间比要进堆会议的开始 8 | 时间小,那么直接退出堆顶元素,说明不需要加会议室。然后把当前会议结束时间入堆。 9 | 10 | class Solution { 11 | public: 12 | int minMeetingRooms(vector>& intervals) { 13 | sort(intervals.begin(), intervals.end()); 14 | priority_queue, greater> q; 15 | for (int i = 0; i < intervals.size(); ++i) { 16 | if (!q.empty() && q.top() <= intervals[i][0]) q.pop(); 17 | q.push(intervals[i][1]); 18 | } 19 | return q.size(); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /数组/268. 丢失的数字.cpp: -------------------------------------------------------------------------------- 1 | 268. 丢失的数字 2 | 3 | 给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。 4 | 5 | 思路: 6 | 7 | 解法一:位置交换法 8 | class Solution { 9 | public: 10 | int missingNumber(vector& nums) { 11 | for (int i = 0; i < nums.size(); ++i) { 12 | while (nums[i] != nums[nums[i] - 1]) { 13 | swap(nums[i], nums[nums[i] - 1]); 14 | } 15 | } 16 | for (int i = 0; i < nums.size(); ++i) { 17 | if (nums[i] != i + 1) return i + 1; 18 | } 19 | return 0; 20 | } 21 | }; 22 | 23 | 解法二:位运算 24 | class Solution { 25 | public: 26 | int missingNumber(vector& nums) { 27 | int ans = 0; 28 | for (int i = 0; i < nums.size(); ++i) { 29 | ans ^= (i + 1) ^ nums[i]; 30 | } 31 | return ans; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /数组/290. 单词规律.cpp: -------------------------------------------------------------------------------- 1 | 290. 单词规律 2 | 3 | 给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。 4 | 这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。 5 | 6 | 示例1: 7 | 8 | 输入: pattern = "abba", str = "dog cat cat dog" 9 | 输出: true 10 | 11 | 题解: 12 | 13 | 设置两个map,value为下标加1. 14 | 这里需要加1就是为了避免默认映射值0,因为 C++ 中的 HashMap 的机制是若访问一个不存在的 key 值,会默认建立一个映射值为0的映射。 15 | 那么我们在遇到新字符和单词时,首先看 i 是否已经是 n 了,若相等了,说明此时 pattern 中的字符已经用完了。 16 | 而 str 中还有多余的单词,这样是无法建立一对一映射的,直接返回 false。 17 | 18 | class Solution { 19 | public: 20 | bool wordPattern(string pattern, string s) { 21 | unordered_map char_map; // char -> index + 1 22 | unordered_map word_map; // word -> index + 1 23 | istringstream in(s); 24 | int i = 0, n = pattern.size(); 25 | for (string word; in >> word; ++i) { 26 | if (i == n || char_map[pattern[i]] != word_map[word]) return false; 27 | char_map[pattern[i]] = word_map[word] = i + 1; 28 | } 29 | return i == n; 30 | } 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /数组/325. 和等于 k 的最长子数组长度.cpp: -------------------------------------------------------------------------------- 1 | 325. 和等于 k 的最长子数组长度 2 | 3 | 给定一个数组 nums 和一个目标值 k,找到和等于 k 的最长子数组长度。 4 | 如果不存在任意一个符合要求的子数组,则返回 0。 5 | 6 | 用hashmap m表示sum和index的映射关系。 7 | 如果sum=k,记录答案。 8 | 如果sum-k的和存在,说明k的和也存在。直接比较答案。 9 | 10 | class Solution { 11 | public: 12 | int maxSubArrayLen(vector& nums, int k) { 13 | int ans = 0, sum = 0; 14 | unordered_map m; // sum -> index 15 | for (int i = 0; i < nums.size(); ++i) { 16 | sum += nums[i]; 17 | if (sum == k) ans = i + 1; 18 | if (m.count(sum - k)) ans = max(ans, i - m[sum - k]); 19 | if (!m.count(sum)) m[sum] = i; 20 | } 21 | return ans; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /数组/350. 两个数组的交集 II.cpp: -------------------------------------------------------------------------------- 1 | 350. 两个数组的交集 II 2 | 3 | 给定两个数组,编写一个函数来计算它们的交集。 4 | 5 | 说明: 6 | 7 | 输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。 8 | 我们可以不考虑输出结果的顺序。 9 | 10 | class Solution { 11 | public: 12 | vector intersect(vector& nums1, vector& nums2) { 13 | unordered_map m; // num->appear_times 14 | vector res; 15 | for (int i = 0; i < nums1.size(); ++i) { 16 | m[nums1[i]]++; 17 | } 18 | for (int i = 0; i < nums2.size(); ++i) { 19 | if (m.find(nums2[i]) != m.end() && m[nums2[i]] > 0) { 20 | res.push_back(nums2[i]); 21 | --m[nums2[i]]; 22 | } 23 | } 24 | return res; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /数组/414. 第三大的数.cpp: -------------------------------------------------------------------------------- 1 | 414. 第三大的数 2 | 3 | 给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。 4 | 5 | class Solution { 6 | public: 7 | int thirdMax(vector& nums) { 8 | set s; 9 | for (int num : nums) { 10 | s.insert(num); 11 | if (s.size() > 3) s.erase(s.begin()); 12 | } 13 | return s.size() == 3 ? *s.begin() : *s.rbegin(); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /数组/442. 数组中重复的数据.cpp: -------------------------------------------------------------------------------- 1 | 442. 数组中重复的数据 2 | 3 | 给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 4 | 其中有些元素出现两次而其他元素出现一次。 5 | 找到所有出现两次的元素。 6 | 7 | class Solution { 8 | public: 9 | vector findDuplicates(vector& nums) { 10 | vector ans; 11 | for (int i = 0; i < nums.size(); ++i) { 12 | if (nums[i] != nums[nums[i] - 1]) { 13 | swap(nums[i], nums[nums[i] - 1]); 14 | --i; 15 | } 16 | } 17 | for (int i = 0; i < nums.size(); ++i) { 18 | if (nums[i] != i + 1) ans.push_back(nums[i]); 19 | } 20 | return ans; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /数组/485. 最大连续1的个数.cpp: -------------------------------------------------------------------------------- 1 | 485. 最大连续1的个数 2 | 3 | 给定一个二进制数组, 计算其中最大连续1的个数。 4 | 5 | 示例 1: 6 | 7 | 输入: [1,1,0,1,1,1] 8 | 输出: 3 9 | 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. 10 | 11 | class Solution { 12 | public: 13 | int findMaxConsecutiveOnes(vector& nums) { 14 | int global = 0; 15 | int local = 0; 16 | for (int i = 0; i < nums.size(); ++i) { 17 | if (nums[i] == 1) local++; 18 | if (nums[i] == 0) local = 0; 19 | if (local > global) global = local; 20 | } 21 | return global; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /数组/560. 和为K的子数组.cpp: -------------------------------------------------------------------------------- 1 | 560. 和为K的子数组 2 | 3 | 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。 4 | 5 | 题解分析: 6 | 7 | 这是一道非常经典的题目,思想用到了前缀和。用map记录累计和的次数。 8 | 如果存在sum[j] - sum[i] = k的话,说明j - i之间的累计和为k。 9 | sum为sum[i],这样我们只需要考虑sum - k是否存在就可以了。 10 | 11 | class Solution { 12 | public: 13 | int subarraySum(vector& nums, int k) { 14 | int ans = 0; 15 | int sum = 0; 16 | unordered_map m; // sum -> times 17 | m[0] = 1; 18 | for (int i = 0; i < nums.size(); ++i) { 19 | sum += nums[i]; 20 | ans += m[sum - k]; 21 | ++m[sum]; 22 | } 23 | return ans; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /数组/605. 种花问题.cpp: -------------------------------------------------------------------------------- 1 | 605. 种花问题 2 | 3 | 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。 4 | 给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。 5 | 6 | 思路: 7 | 8 | 题解 9 | 10 | class Solution { 11 | public: 12 | bool canPlaceFlowers(vector& flowerbed, int n) { 13 | if (flowerbed.empty()) return false; 14 | flowerbed.push_back(0); 15 | flowerbed.insert(flowerbed.begin(), 0); 16 | for (int i = 1; i < flowerbed.size() - 1; ++i) { 17 | if (n == 0) return true; 18 | if (flowerbed[i] + flowerbed[i - 1] + flowerbed[i + 1] == 0) { 19 | --n; 20 | ++i; 21 | } 22 | } 23 | return n <= 0; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /数组/830. 较大分组的位置.cpp: -------------------------------------------------------------------------------- 1 | 830. 较大分组的位置 2 | 3 | 在一个由小写字母构成的字符串 s 中,包含由一些连续的相同字符所构成的分组。 4 | 5 | 例如,在字符串 s = "abbxxxxzyy" 中,就含有 "a", "bb", "xxxx", "z" 和 "yy" 这样的一些分组。 6 | 7 | 分组可以用区间 [start, end] 表示,其中 start 和 end 分别表示该分组的起始和终止位置的下标。上例中的 "xxxx" 分组用区间表示为 [3,6] 。 8 | 9 | 我们称所有包含大于或等于三个连续字符的分组为 较大分组 。 10 | 11 | 找到每一个 较大分组 的区间,按起始位置下标递增顺序排序后,返回结果。 12 | 13 | class Solution { 14 | public: 15 | vector> largeGroupPositions(string s) { 16 | vector> ans; 17 | int left = 0; 18 | for (int i = 0; i <= s.size(); ++i) { 19 | if (i < s.size() && s[i] == s[left]) continue; 20 | if (i - left >= 3) ans.push_back({left, i - 1}); 21 | left = i; 22 | } 23 | return ans; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /数组/剑指 Offer 03. 数组中重复的数字.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 03. 数组中重复的数字 2 | 3 | 找出数组中重复的数字。 4 | 5 | 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。 6 | 数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。 7 | 请找出数组中任意一个重复的数字。 8 | 9 | 示例 1: 10 | 输入: 11 | [2, 3, 1, 0, 2, 5, 3] 12 | 输出:2 或 3 13 | 14 | 限制: 15 | 2 <= n <= 100000 16 | 17 | class Solution { 18 | public: 19 | int findRepeatNumber(vector& nums) { 20 | for (int i = 0; i < nums.size(); ++i) { 21 | while (i != nums[i]) { 22 | if (nums[i] == nums[nums[i]]) 23 | return nums[i]; 24 | int tmp = nums[nums[i]]; 25 | nums[nums[i]] = nums[i]; 26 | nums[i] = tmp; 27 | } 28 | } 29 | return -1; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /数组/剑指 Offer 62. 圆圈中最后剩下的数字.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 62. 圆圈中最后剩下的数字 2 | 3 | 0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。 4 | 5 | 例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。 6 | 7 | 解题思路: 8 | 最终剩下一个人时的安全位置肯定为0,反推安全位置在人数为n时的编号 9 | 人数为1: 0 10 | 人数为2: (0+m) % 2 11 | 人数为3: ((0+m) % 2 + m) % 3 12 | 人数为4: (((0+m) % 2 + m) % 3 + m) % 4 13 | ........ 14 | 迭代推理到n就可以得出答案 15 | 16 | class Solution { 17 | public: 18 | int lastRemaining(int n, int m) { 19 | int ans = 0; 20 | for (int i = 2; i <= n; ++i) { 21 | ans = (ans + m) % i; 22 | } 23 | return ans; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /数组/面试题 01.07. 旋转矩阵.cpp: -------------------------------------------------------------------------------- 1 | 面试题 01.07. 旋转矩阵 2 | 3 | class Solution { 4 | public: 5 | void rotate(vector>& matrix) { 6 | for (int j = 0; j < matrix[0].size(); ++j) { 7 | for (int i = j; i < matrix.size(); ++i) { 8 | swap(matrix[i][j], matrix[j][i]); 9 | } 10 | } 11 | for (int i = 0; i < matrix.size(); ++i) { 12 | reverse(matrix[i].begin(), matrix[i].end()); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /数组/面试题 16.06. 最小差.cpp: -------------------------------------------------------------------------------- 1 | 面试题 16.06. 最小差 2 | 3 | 给定两个整数数组a和b,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差。 4 | 5 | 题解分析: 6 | 7 | 先分别排序,在利用双指针逼夹比较。 8 | 9 | class Solution { 10 | public: 11 | int smallestDifference(vector& a, vector& b) { 12 | sort(a.begin(), a.end()); 13 | sort(b.begin(), b.end()); 14 | long ans = INT_MAX; 15 | for (int i = 0, j = 0; i < a.size() && j < b.size();) { 16 | ans = min(ans, abs((long)a[i] - (long)b[j])); 17 | if (a[i] > b[j]) ++j; 18 | else ++i; 19 | } 20 | return ans; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /数组/面试题 16.11. 跳水板.cpp: -------------------------------------------------------------------------------- 1 | 面试题 16.11. 跳水板 2 | 3 | 你正在使用一堆木板建造跳水板。有两种类型的木板,其中长度较短的木板长度为shorter,长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法,生成跳水板所有可能的长度。 4 | 5 | 返回的长度需要从小到大排列。 6 | 7 | 示例 1 8 | 9 | 输入: 10 | shorter = 1 11 | longer = 2 12 | k = 3 13 | 输出: [3,4,5,6] 14 | 解释: 15 | 可以使用 3 次 shorter,得到结果 3;使用 2 次 shorter 和 1 次 longer,得到结果 4 。以此类推,得到最终结果。 16 | 17 | class Solution { 18 | public: 19 | vector divingBoard(int shorter, int longer, int k) { 20 | vector ans; 21 | if (k == 0) return ans; 22 | if (shorter == longer) { 23 | ans.push_back(shorter * k); 24 | return ans; 25 | } 26 | int cnt = shorter * k; 27 | ans.push_back(cnt); 28 | for (int i = k; i > 0; --i) { 29 | cnt = cnt - shorter + longer; 30 | ans.push_back(cnt); 31 | } 32 | return ans; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /数组/面试题 16.24. 数对和.cpp: -------------------------------------------------------------------------------- 1 | 面试题 16.24. 数对和 2 | 3 | 设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。 4 | 5 | class Solution { 6 | public: 7 | vector> pairSums(vector& nums, int target) { 8 | unordered_map m; 9 | vector> ans; 10 | for (int i = 0; i < nums.size(); ++i) { 11 | if (m.count(target - nums[i]) && m[target - nums[i]] > 0) { 12 | m[target - nums[i]]--; 13 | ans.push_back({target - nums[i], nums[i]}); 14 | } else { 15 | m[nums[i]]++; 16 | } 17 | } 18 | return ans; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /数组/面试题 17.10. 主要元素.cpp: -------------------------------------------------------------------------------- 1 | 面试题 17.10. 主要元素 2 | 3 | 数组中占比超过一半的元素称之为主要元素。给定一个整数数组,找到它的主要元素。若没有,返回-1。 4 | 5 | class Solution { 6 | public: 7 | int majorityElement(vector& nums) { 8 | sort(nums.begin(), nums.end()); 9 | for(int i = 0; (i + nums.size() / 2) < nums.size(); ++i) { 10 | if(nums[i] == nums[i + nums.size() / 2]) return nums[i]; 11 | } 12 | return -1; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /栈/020. 有效的括号.cpp: -------------------------------------------------------------------------------- 1 | 20. 有效的括号 2 | 3 | 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 4 | 5 | 有效字符串需满足: 6 | 7 | 左括号必须用相同类型的右括号闭合。 8 | 左括号必须以正确的顺序闭合。 9 | 注意空字符串可被认为是有效字符串。 10 | 11 | class Solution { 12 | public: 13 | bool isValid(string s) { 14 | int n = s.size(); 15 | if (n % 2 == 1) { 16 | return false; 17 | } 18 | 19 | unordered_map pairs = { 20 | {')', '('}, 21 | {']', '['}, 22 | {'}', '{'} 23 | }; 24 | stack stk; 25 | for (char ch: s) { 26 | if (pairs.count(ch)) { 27 | if (stk.empty() || stk.top() != pairs[ch]) { 28 | return false; 29 | } 30 | stk.pop(); 31 | } 32 | else { 33 | stk.push(ch); 34 | } 35 | } 36 | return stk.empty(); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /栈/084. 柱状图中最大的矩形.cpp: -------------------------------------------------------------------------------- 1 | 84. 柱状图中最大的矩形 2 | 3 | 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 4 | 5 | 求在该柱状图中,能够勾勒出来的矩形的最大面积。 6 | 7 | 以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。 8 | 图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。 9 | 10 | 示例: 11 | 12 | 输入: [2,1,5,6,2,3] 13 | 输出: 10 14 | 15 | class Solution { 16 | public: 17 | int largestRectangleArea(vector& heights) { 18 | int res = 0; 19 | stack st; 20 | heights.push_back(0); // 注意这里先给heights队尾加入一个元素,以保证会处理最后一个高度值。 21 | for (int i = 0; i < heights.size(); ++i) { 22 | if (st.empty() || heights[st.top()] < heights[i]) { 23 | st.push(i); 24 | } else { 25 | int cur = st.top(); st.pop(); 26 | res = max(res, heights[cur] * (st.empty() ? i : (i - st.top() - 1))); 27 | --i; 28 | } 29 | } 30 | return res; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /栈/1019. 链表中的下一个更大节点.cpp: -------------------------------------------------------------------------------- 1 | 1019. 链表中的下一个更大节点 2 | 3 | 给出一个以头节点 head 作为第一个节点的链表。链表中的节点分别编号为:node_1, node_2, node_3, ... 。 4 | 5 | 每个节点都可能有下一个更大值(next larger value):对于 node_i,如果其 next_larger(node_i) 是 node_j.val, 6 | 那么就有 j > i 且  node_j.val > node_i.val,而 j 是可能的选项中最小的那个。如果不存在这样的 j,那么下一个更大值为 0 。 7 | 8 | 返回整数答案数组 answer,其中 answer[i] = next_larger(node_{i+1}) 。 9 | 10 | 注意:在下面的示例中,诸如 [2,1,5] 这样的输入(不是输出)是链表的序列化表示,其头节点的值为 2,第二个节点值为 1,第三个节点值为 5 。 11 | 12 | class Solution { 13 | public: 14 | vector nextLargerNodes(ListNode* head) { 15 | vector nums; 16 | stack st; 17 | for (auto p = head; p != nullptr; p = p->next) nums.push_back(p->val); 18 | vector ans(nums); 19 | for (int i = nums.size() - 1; i >= 0; --i) { 20 | while (!st.empty() && st.top() <= nums[i]) st.pop(); 21 | ans[i] = st.empty() ? 0 : st.top(); 22 | st.push(nums[i]); 23 | } 24 | return ans; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /栈/456. 132模式.cpp: -------------------------------------------------------------------------------- 1 | 456. 132模式 2 | 3 | 给定一个整数序列:a1, a2, ..., an, 4 | 一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。 5 | 设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。 6 | 7 | class Solution { 8 | public: 9 | bool find132pattern(vector& nums) { 10 | stack st; 11 | int third = INT_MIN; 12 | for (int i = nums.size() - 1; i >= 0; --i) { 13 | if (nums[i] < third) return true; 14 | while (!st.empty() && nums[i] > st.top()) { 15 | third = st.top(); 16 | st.pop(); 17 | } 18 | st.push(nums[i]); 19 | } 20 | return false; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /栈/946. 验证栈序列.cpp: -------------------------------------------------------------------------------- 1 | 946. 验证栈序列 2 | 3 | 此题和剑指 Offer 31. 栈的压入、弹出序列雷同 4 | https://leetcode-cn.com/problems/validate-stack-sequences/ 5 | 6 | 给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。 7 | 8 | 示例 1: 9 | 输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1] 10 | 输出:true 11 | 12 | 题解: 13 | 14 | 模拟一个stack的真实插入删除过程。 15 | 16 | class Solution { 17 | public: 18 | bool validateStackSequences(vector& pushed, vector& popped) { 19 | stack st; int popIndex = 0; 20 | for (int i = 0; i < pushed.size(); ++i) { 21 | st.push(pushed[i]); 22 | while (!st.empty() && st.top() == popped[popIndex]) { 23 | st.pop(); 24 | ++popIndex; 25 | } 26 | } 27 | return st.empty(); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /栈/剑指 Offer 09. 用两个栈实现队列.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 09. 用两个栈实现队列 2 | 3 | class CQueue { 4 | public: 5 | CQueue() { 6 | 7 | } 8 | 9 | void appendTail(int value) { 10 | st1.push(value); 11 | } 12 | 13 | int deleteHead() { 14 | if (st1.empty()) return -1; 15 | int ans; 16 | while (!st1.empty()) { 17 | st2.push(st1.top()); 18 | st1.pop(); 19 | } 20 | ans = st2.top(); 21 | st2.pop(); 22 | while(!st2.empty()) { 23 | st1.push(st2.top()); 24 | st2.pop(); 25 | } 26 | return ans; 27 | } 28 | 29 | stack st1; 30 | stack st2; 31 | }; 32 | -------------------------------------------------------------------------------- /栈/面试题 03.02. 栈的最小值.cpp: -------------------------------------------------------------------------------- 1 | 面试题 03.02. 栈的最小值 2 | 3 | 请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。 4 | 5 | class MinStack { 6 | public: 7 | /** initialize your data structure here. */ 8 | MinStack() { 9 | 10 | } 11 | 12 | void push(int x) { 13 | stk.push(x); 14 | if (!min_stk.empty()) min_stk.push(min(min_stk.top(), x)); 15 | else min_stk.push(x); 16 | } 17 | 18 | void pop() { 19 | int val = stk.top(); stk.pop(); 20 | min_stk.pop(); 21 | } 22 | 23 | int top() { 24 | if (stk.empty()) return -1; 25 | return stk.top(); 26 | } 27 | 28 | int getMin() { 29 | return min_stk.top(); 30 | } 31 | 32 | private: 33 | stack stk; 34 | stack min_stk; 35 | }; 36 | -------------------------------------------------------------------------------- /深度优先搜索/133. 克隆图.cpp: -------------------------------------------------------------------------------- 1 | 133. 克隆图 2 | 3 | 给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。 4 | 5 | 图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。 6 | 7 | class Node { 8 | public int val; 9 | public List neighbors; 10 | } 11 | 12 | class Solution { 13 | public: 14 | unordered_map visited; // old -> new 15 | Node* cloneGraph(Node* node) { 16 | if (node == nullptr) return node; 17 | if (visited.find(node) != visited.end()) return visited[node]; 18 | Node *cloneNode = new Node(node->val); 19 | visited[node] = cloneNode; 20 | for (int i = 0; i < node->neighbors.size(); ++i) { 21 | cloneNode->neighbors.push_back(cloneGraph(node->neighbors[i])); 22 | } 23 | return cloneNode; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /深度优先搜索/257. 二叉树的所有路径.cpp: -------------------------------------------------------------------------------- 1 | 257. 二叉树的所有路径 2 | 3 | 给定一个二叉树,返回所有从根节点到叶子节点的路径。 4 | 5 | 说明: 叶子节点是指没有子节点的节点。 6 | 7 | 示例: 8 | 9 | 输入: 10 | 11 | 1 12 | / \ 13 | 2 3 14 | \ 15 | 5 16 | 17 | 输出: ["1->2->5", "1->3"] 18 | 19 | 解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3 20 | 21 | 注意: 22 | 23 | 1. 要考虑过滤减枝if (root->left) 24 | 25 | class Solution { 26 | public: 27 | vector binaryTreePaths(TreeNode* root) { 28 | vector ans; 29 | string out; 30 | if (root != nullptr) helper(root, "", ans); 31 | return ans; 32 | } 33 | 34 | void helper(TreeNode* root, string out, vector& ans) { 35 | out += to_string(root->val); 36 | if (root->left == nullptr && root->right == nullptr) { 37 | ans.push_back(out); 38 | } 39 | if (root->left) helper(root->left, out + "->", ans); 40 | if (root->right) helper(root->right, out + "->", ans); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /深度优先搜索/386. 字典序排数.cpp: -------------------------------------------------------------------------------- 1 | 386. 字典序排数 2 | 3 | 给定一个整数 n, 返回从 1 到 n 的字典顺序。 4 | 5 | 例如, 6 | 7 | 给定 n =1 3,返回 [1,10,11,12,13,2,3,4,5,6,7,8,9] 。 8 | 9 | 请尽可能的优化算法的时间复杂度和空间复杂度。 输入的数据 n 小于等于 5,000,000。 10 | 11 | class Solution { 12 | vector ans; 13 | public: 14 | vector lexicalOrder(int n) { 15 | for (int i = 1; i < 10; ++i) { 16 | dfs(n, i); 17 | } 18 | return ans; 19 | } 20 | 21 | void dfs(int n, int cur) { 22 | if (cur > n) return; 23 | ans.push_back(cur); 24 | for (int i = 0; i < 10; ++i) { 25 | dfs(n, cur * 10 + i); 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /深度优先搜索/695. 岛屿的最大面积.cpp: -------------------------------------------------------------------------------- 1 | 695. 岛屿的最大面积 2 | 3 | 给定一个包含了一些 0 和 1 的非空二维数组 grid 。 4 | 5 | 一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求 6 | 两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 7 | 0(代表水)包围着。 8 | 9 | 找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。) 10 | 11 | class Solution { 12 | public: 13 | int maxAreaOfIsland(vector>& grid) { 14 | int ans = 0; 15 | for (int i = 0; i < grid.size(); ++i) 16 | for (int j = 0; j < grid[0].size(); ++j) { 17 | if (grid[i][j] == 1) ans = max(ans, maxArea(grid, i, j)); 18 | } 19 | return ans; 20 | } 21 | int maxArea(vector>& grid, int i, int j) { 22 | if (i >= 0 && i < grid.size() && j >= 0 && j < grid[0].size() && grid[i][j] == 1) { 23 | grid[i][j] = 0; 24 | return 1 + maxArea(grid, i + 1, j) + maxArea(grid, i - 1, j) + maxArea(grid, i, j + 1) + maxArea(grid, i, j - 1); 25 | } else return 0; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /滑动窗口/567. 字符串的排列.cpp: -------------------------------------------------------------------------------- 1 | 567. 字符串的排列 2 | 3 | 给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。 4 | 换句话说,第一个字符串的排列之一是第二个字符串的子串。 5 | 6 | 用一个滑动窗口和双指针。用一个hashmap记录s1出现的次数。 7 | 如果m[s2[right]]>0出现就cnt--并自减一。 8 | 遇到cnt==0,收集结果。left不断往右移动,闭关把m[s2[left]]加一。 9 | 如果m[s2[left]]>0那么++cnt,说明s1中有字母被移出了滑动窗口。 10 | 11 | class Solution { 12 | public: 13 | bool checkInclusion(string s1, string s2) { 14 | int left = 0, ans = 0, cnt = s1.size(); 15 | unordered_map m; // char -> freq 16 | for (char c : s1) m[c]++; 17 | for (int right = 0; right < s2.size(); ++right) { 18 | if (m[s2[right]]-- > 0) --cnt; 19 | while (cnt == 0) { 20 | if (right - left + 1 == s1.size()) return true; 21 | if (++m[s2[left]] > 0) { 22 | ++cnt; 23 | } 24 | ++left; 25 | } 26 | } 27 | return false; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /滑动窗口/README.md: -------------------------------------------------------------------------------- 1 | ### More Similar Sliding Window Problems 2 | 3 | ### Here are some similar sliding window problems. 4 | 5 | 1358. Number of Substrings Containing All Three Characters 6 | 1248. Count Number of Nice Subarrays 7 | 1234. Replace the Substring for Balanced String 8 | 1004. Max Consecutive Ones III 9 | 930. Binary Subarrays With Sum 10 | 992. Subarrays with K Different Integers 11 | 904. Fruit Into Baskets 12 | 862. Shortest Subarray with Sum at Least K 13 | 209. Minimum Size Subarray Sum 14 | -------------------------------------------------------------------------------- /滑动窗口/关于滑动窗口相关问题的思考.md: -------------------------------------------------------------------------------- 1 | (原创) 2 | 3 | 待完成 4 | 5 | 关于这个主题在LeetCode里面有不少经典问题,在这里做一个归纳。 6 | 7 | 部分思路参考自: 8 | https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems 9 | 10 | 76. Minimum Window Substring 11 | Longest Substring with At Most Two Distinct Characters 12 | Longest Substring Without Repeating Characters 13 | Longest Substring - at most K distinct characters 14 | 15 | 相似题目: 16 | 串联所有单词的子串 困难 17 | 长度最小的子数组 中等 18 | 滑动窗口最大值 困难 19 | 字符串的排列 中等 20 | 最小窗口子序列 困难 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /滑动窗口/剑指 Offer 48. 最长不含重复字符的子字符串.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 48. 最长不含重复字符的子字符串 2 | 3 | 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。 4 | 5 | 示例 1: 6 | 7 | 输入: "abcabcbb" 8 | 输出: 3 9 | 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 10 | 示例 2: 11 | 12 | 输入: "bbbbb" 13 | 输出: 1 14 | 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 15 | 示例 3: 16 | 17 | 输入: "pwwkew" 18 | 输出: 3 19 | 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 20 |   请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 21 | 22 | class Solution { 23 | public: 24 | 25 | int lengthOfLongestSubstring(string s) { 26 | int res = 0, left = -1, n = s.size(); 27 | unordered_map m; // 字符:下标 28 | for (int i = 0; i < n; ++i) { 29 | if (m.count(s[i]) && m[s[i]] > left) { 30 | left = m[s[i]]; // m[s[i]]表示字符为s[i]的下标 31 | } 32 | m[s[i]] = i; 33 | res = max(res, i - left); 34 | } 35 | return res; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /设计/173. 二叉搜索树迭代器.cpp: -------------------------------------------------------------------------------- 1 | 173. 二叉搜索树迭代器 2 | 3 | https://leetcode-cn.com/problems/binary-search-tree-iterator/ 4 | 5 | 实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。 6 | 7 | 调用 next() 将返回二叉搜索树中的下一个最小的数。 8 | 9 | 示例: 10 | 11 | 7 12 | / \ 13 | 3 15 14 | / \ 15 | 9 20 16 | 17 | BSTIterator iterator = new BSTIterator(root); 18 | iterator.next(); // 返回 3 19 | iterator.next(); // 返回 7 20 | iterator.hasNext(); // 返回 true 21 | iterator.next(); // 返回 9 22 | iterator.hasNext(); // 返回 true 23 | iterator.next(); // 返回 15 24 | iterator.hasNext(); // 返回 true 25 | iterator.next(); // 返回 20 26 | iterator.hasNext(); // 返回 false 27 | 28 | -------------------------------------------------------------------------------- /设计/295. 数据流的中位数.cpp: -------------------------------------------------------------------------------- 1 | 295. 数据流的中位数 2 | 3 | 中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。 4 | 5 | 例如, 6 | 7 | [2,3,4] 的中位数是 3 8 | 9 | [2,3] 的中位数是 (2 + 3) / 2 = 2.5 10 | 11 | 设计一个支持以下两种操作的数据结构: 12 | 13 | void addNum(int num) - 从数据流中添加一个整数到数据结构中。 14 | double findMedian() - 返回目前所有元素的中位数。 15 | 16 | class MedianFinder { 17 | public: 18 | /** initialize your data structure here. */ 19 | MedianFinder() { 20 | 21 | } 22 | 23 | void addNum(int num) { 24 | lo.push(num); 25 | 26 | hi.push(lo.top()); 27 | lo.pop(); 28 | 29 | if (lo.size() < hi.size()) { 30 | lo.push(hi.top()); 31 | hi.pop(); 32 | } 33 | } 34 | 35 | double findMedian() { 36 | if (lo.size() > hi.size()) 37 | return (double) lo.top(); 38 | else 39 | return (double) (lo.top() + hi.top()) * 0.5; 40 | } 41 | 42 | private: 43 | priority_queue, less> lo; // 大顶堆 44 | priority_queue, greater> hi; //小顶堆 45 | }; 46 | -------------------------------------------------------------------------------- /设计/剑指 Offer 41. 数据流中的中位数.cpp: -------------------------------------------------------------------------------- 1 | 剑指 Offer 41. 数据流中的中位数.cpp 2 | 3 | https://github.com/yuan-luo/leetcode/blob/master/%E5%A0%86/%E5%89%91%E6%8C%87%20Offer%2041.%20%E6%95%B0%E6%8D%AE%E6%B5%81%E4%B8%AD%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0.cpp 4 | -------------------------------------------------------------------------------- /贪心算法/045. 跳跃游戏 II.cpp: -------------------------------------------------------------------------------- 1 | 45. 跳跃游戏 II 2 | 3 | 给定一个非负整数数组,你最初位于数组的第一个位置。 4 | 数组中的每个元素代表你在该位置可以跳跃的最大长度。 5 | 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 6 | 7 | reach,当前能达到的最远。 8 | curEnd,上一轮跳达到的最远。 9 | 如果当前位置到达了 curEnd,即上一步能到达的最远位置, 10 | 说明需要再跳一次。 11 | 12 | class Solution { 13 | public: 14 | int jump(vector& nums) { 15 | int jump = 0; 16 | int reach = 0, curEnd = 0; 17 | for (int i = 0; i < nums.size() - 1; ++i) { 18 | reach = max(reach, i + nums[i]); 19 | if (i == curEnd) { 20 | ++jump; 21 | curEnd = reach; 22 | } 23 | } 24 | return jump; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /贪心算法/055. 跳跃游戏.cpp: -------------------------------------------------------------------------------- 1 | 55. 跳跃游戏 2 | 3 | 给定一个非负整数数组,你最初位于数组的第一个位置。 4 | 数组中的每个元素代表你在该位置可以跳跃的最大长度。 5 | 判断你是否能够到达最后一个位置。 6 | 7 | class Solution { 8 | public: 9 | bool canJump(vector& nums) { 10 | int reach = nums[0]; 11 | for (int i = 0; i <= reach; ++i) { 12 | reach = max(reach, i + nums[i]); 13 | if (reach >= nums.size() - 1) return true; 14 | } 15 | return false; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /贪心算法/455. 分发饼干.cpp: -------------------------------------------------------------------------------- 1 | 455. 分发饼干 2 | 3 | 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 4 | 5 | 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。 6 | 如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。 7 | 8 | class Solution { 9 | public: 10 | int findContentChildren(vector& g, vector& s) { 11 | int j = 0; 12 | sort(g.begin(), g.end()); 13 | sort(s.begin(), s.end()); 14 | for (int i = 0; i < s.size() && j < g.size(); ++i) { 15 | if (s[i] >= g[j]) ++j; 16 | } 17 | return j; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /贪心算法/621. 任务调度器.cpp: -------------------------------------------------------------------------------- 1 | 621. 任务调度器 2 | 3 | 给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。 4 | 任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。 5 | 6 | 然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。 7 | 你需要计算完成所有任务所需要的 最短时间 。 8 | 9 | 题解: 10 | 11 | 这道题目要设计一个桶。当每个桶的大小为n+1。这样相同类型的tasks必须放到不同的桶里面以保证n时间间隔。 12 | 这样规划要分两种情况, 13 | 1.当task很多的时候,如果大于桶的规划,这时候取决于task的size。 14 | 2.当task不是很多的时候,桶的个数取决于相同task数量对多的那个task的size减去1,最后要加上一个桶,大小不再是n+1,而是同拥有最大task个数的task的数量。 15 | 16 | class Solution { 17 | public: 18 | int leastInterval(vector& tasks, int n) { 19 | vector counts(26, 0); 20 | for (char c : tasks) { 21 | counts[c - 'A']++; 22 | } 23 | int maxLen = 0; 24 | for (int count : counts) { 25 | maxLen = max(maxLen, count); 26 | } 27 | int maxCount = 0; 28 | for (int count : counts) { 29 | if (count == maxLen) ++maxCount; 30 | } 31 | 32 | return max((n + 1) * (maxLen - 1) + maxCount, (int)tasks.size()); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /贪心算法/738. 单调递增的数字.cpp: -------------------------------------------------------------------------------- 1 | 738. 单调递增的数字 2 | 3 | 给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。 4 | 5 | class Solution { 6 | public: 7 | int monotoneIncreasingDigits(int N) { 8 | string n_str = to_string(N); 9 | int marker= n_str.size(); 10 | for (int i = n_str.size() - 1; i > 0; --i) { 11 | if (n_str[i] < n_str[i - 1]) { 12 | marker = i; 13 | n_str[i - 1] = n_str[i - 1] - 1; 14 | } 15 | } 16 | for (int i = marker; i < n_str.size(); ++i) n_str[i] = '9'; 17 | return stoi(n_str); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /贪心算法/860. 柠檬水找零.cpp: -------------------------------------------------------------------------------- 1 | 860. 柠檬水找零 2 | 3 | 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 4 | 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 5 | 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 6 | 注意,一开始你手头没有任何零钱。 7 | 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 8 | 9 | 示例 1: 10 | 11 | 输入:[5,5,5,10,20] 12 | 输出:true 13 | 14 | 解释: 15 | 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 16 | 第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 17 | 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 18 | 由于所有客户都得到了正确的找零,所以我们输出 true。 19 | 20 | class Solution { 21 | public: 22 | bool lemonadeChange(vector& bills) { 23 | int five = 0, ten = 0; 24 | for (int bill : bills) { 25 | if (bill == 5) ++five; 26 | else if (bill == 10) {--five; ++ten;} 27 | else if (ten > 0) {--ten; --five;} 28 | else five -= 3; 29 | if (five < 0) return false; 30 | } 31 | return true; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /贪心算法/README.md: -------------------------------------------------------------------------------- 1 | 关于贪心算法应不应该学习的讨论: 2 | https://zhuanlan.zhihu.com/p/149824838#:~:text=2%E3%80%81%E9%9D%A2%E8%AF%95%E5%9F%BA%E6%9C%AC%E4%B8%8D%E4%BC%9A%E8%80%83,%E9%A2%98%E6%88%96%E8%80%85%E6%98%AF%E8%83%8C%E8%AF%B5%E9%A2%98%E3%80%82&text=%E4%BB%8E%E5%8F%A6%E5%A4%96%E4%B8%80%E4%B8%AA%E8%A7%92%E5%BA%A6%E6%9D%A5,%E4%BD%9C%E4%B8%BA%E9%9D%A2%E8%AF%95%E7%9A%84%E7%AE%97%E6%B3%95%E9%97%AE%E9%A2%98%E3%80%82 3 | 4 | 必须背熟的贪心算法: 5 | 6 | https://www.jiuzhang.com/qa/2099/?utm_source=sc-zhwenzhang-cyc0621 7 | 8 | http://www.lintcode.com/problem/majority-number/ 9 | http://www.lintcode.com/problem/create-maximum-number/ 10 | http://www.lintcode.com/problem/jump-game-ii/ 11 | http://www.lintcode.com/problem/jump-game/ 12 | http://www.lintcode.com/problem/gas-station/ 13 | http://www.lintcode.com/problem/delete-digits/ 14 | http://www.lintcode.com/problem/task-scheduler/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /链表/*061. 旋转链表.cpp: -------------------------------------------------------------------------------- 1 | 61. 旋转链表 2 | 3 | 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。 4 | 5 | 示例 1: 6 | 7 | 输入: 1->2->3->4->5->NULL, k = 2 8 | 输出: 4->5->1->2->3->NULL 9 | 解释: 10 | 向右旋转 1 步: 5->1->2->3->4->NULL 11 | 向右旋转 2 步: 4->5->1->2->3->NULL 12 | 13 | 题解: 14 | 15 | 把头尾连起来,然后倒走K步。(顺走 n - k % n 步) 16 | 然后断开连接。 17 | 18 | class Solution { 19 | public: 20 | ListNode* rotateRight(ListNode* head, int k) { 21 | if (head == nullptr) return nullptr; 22 | int n = 1; 23 | ListNode *cur = head; 24 | while (cur->next) { 25 | ++n; 26 | cur = cur->next; 27 | } 28 | cur->next = head; 29 | int m = n - k % n; 30 | for (int i = 0; i < m; ++i) { 31 | cur = cur->next; 32 | } 33 | head = cur->next; 34 | cur->next = nullptr; 35 | return head; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /链表/*109. 有序链表转换二叉搜索树.cpp: -------------------------------------------------------------------------------- 1 | 109. 有序链表转换二叉搜索树 2 | 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。 3 | 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 4 | 5 | 示例: 6 | 给定的有序链表: [-10, -3, 0, 5, 9], 7 | 一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树: 8 | 0 9 | / \ 10 | -3 9 11 | / / 12 | -10 5 13 | 14 | class Solution { 15 | public: 16 | TreeNode* sortedListToBST(ListNode* head) { 17 | if (head == nullptr) return nullptr; 18 | if (head->next == nullptr) return new TreeNode(head->val); 19 | ListNode *slow = head, *fast = head, *pre = head; 20 | while (fast->next != nullptr && fast->next->next != nullptr) { 21 | pre = slow; 22 | slow = slow->next; 23 | fast = fast->next->next; 24 | } 25 | fast = slow->next; 26 | pre->next = nullptr; 27 | TreeNode* root = new TreeNode(slow->val); 28 | if (head != slow) root->left = sortedListToBST(head); 29 | root->right = sortedListToBST(fast); 30 | return root; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /链表/002. 两数相加.cpp: -------------------------------------------------------------------------------- 1 | 2. 两数相加 2 | 3 | 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 4 | 5 | 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 6 | 7 | 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 8 | 9 | 示例: 10 | 11 | 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 12 | 输出:7 -> 0 -> 8 13 | 原因:342 + 465 = 807 14 | 15 | class Solution { 16 | public: 17 | ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { 18 | int sum = 0, carry = 0; 19 | ListNode *dummy = new ListNode(-1); 20 | ListNode *cur = dummy; 21 | while (l1 || l2) { 22 | int sum; 23 | int n1 = l1 ? l1->val : 0; 24 | int n2 = l2 ? l2->val : 0; 25 | sum = n1 + n2 + carry; 26 | carry = sum / 10; 27 | cur->next = new ListNode(sum % 10); 28 | cur = cur->next; 29 | if (l1) l1 = l1->next; 30 | if (l2) l2 = l2->next; 31 | } 32 | 33 | if (carry > 0) { 34 | ListNode *node = new ListNode(1); 35 | cur->next = node; 36 | } 37 | return dummy->next; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /链表/019. 删除链表的倒数第N个节点.cpp: -------------------------------------------------------------------------------- 1 | 19. 删除链表的倒数第N个节点 2 | 3 | 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 4 | 5 | 示例: 6 | 7 | 给定一个链表: 1->2->3->4->5, 和 n = 2. 8 | 9 | 当删除了倒数第二个节点后,链表变为 1->2->3->5. 10 | 11 | 题解分析: 12 | 13 | 这里设置两个指针slow和fast。注意slow指针指向dummy,所以slow->next才指向倒数第n个节点。 14 | 15 | D->1->2->3->4->5 16 | 要删除4,slow是3。 17 | 18 | class Solution { 19 | public: 20 | ListNode* removeNthFromEnd(ListNode* head, int n) { 21 | if (head->next == nullptr && n == 1) return nullptr; 22 | ListNode dummy; 23 | dummy.next = head; 24 | ListNode *slow = &dummy, *fast = head; 25 | for (int i = 0; i < n; ++i) fast = fast->next; 26 | while (fast != nullptr) { 27 | fast = fast->next; 28 | slow = slow->next; 29 | } 30 | slow->next = slow->next->next; 31 | 32 | return dummy.next; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /链表/021. 合并两个有序链表.cpp: -------------------------------------------------------------------------------- 1 | 21. 合并两个有序链表 2 | 3 | class Solution { 4 | public: 5 | ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { 6 | if (l1 == nullptr) return l2; 7 | else if (l2 == nullptr) return l1; 8 | else if (l1->val < l2->val) { 9 | l1->next = mergeTwoLists(l1->next, l2); 10 | return l1; 11 | } else { 12 | l2->next = mergeTwoLists(l1, l2->next); 13 | return l2; 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /链表/024. 两两交换链表中的节点.cpp: -------------------------------------------------------------------------------- 1 | 24. 两两交换链表中的节点 2 | 3 | 递归: 4 | class Solution { 5 | public: 6 | ListNode* swapPairs(ListNode* head) { 7 | if (head == nullptr || head->next == nullptr) return head; 8 | ListNode *tmp = head->next; 9 | head->next = swapPairs(tmp->next); 10 | tmp->next = head; 11 | return tmp; 12 | } 13 | }; 14 | 15 | 非递归: 16 | class Solution { 17 | public: 18 | ListNode* swapPairs(ListNode* head) { 19 | ListNode *dummy = new ListNode(-1), *pre = dummy; 20 | dummy->next = head; 21 | while (pre->next && pre->next->next) { 22 | ListNode *t = pre->next->next; 23 | pre->next->next = t->next; 24 | t->next = pre->next; 25 | pre->next = t; 26 | pre = t->next; 27 | } 28 | return dummy->next; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /链表/082. 删除排序链表中的重复元素 II.cpp: -------------------------------------------------------------------------------- 1 | 82. 删除排序链表中的重复元素 II 2 | 3 | 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。 4 | 5 | class Solution { 6 | public: 7 | ListNode* deleteDuplicates(ListNode* head) { 8 | ListNode dummy; 9 | dummy.next = head; 10 | ListNode* pre = &dummy; 11 | while (pre->next) { 12 | ListNode* cur = pre->next; 13 | while (cur->next && cur->val == cur->next->val) cur = cur->next; 14 | if (cur != pre->next) pre->next = cur->next; 15 | else pre = pre->next; 16 | } 17 | return dummy.next; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /链表/086. 分隔链表.cpp: -------------------------------------------------------------------------------- 1 | 86. 分隔链表 2 | 3 | 给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。 4 | 你应当保留两个分区中每个节点的初始相对位置。 5 | 6 | 示例: 7 | 8 | 输入: head = 1->4->3->2->5->2, x = 3 9 | 输出: 1->2->2->4->3->5 10 | 11 | 题解: 12 | 13 | 这道题就是把原链表分割成两个链表,一个小于x,一个大于等于x。然后依次排序插入节点,最后再把它们接起来。 14 | 15 | class Solution { 16 | public: 17 | ListNode* partition(ListNode* head, int x) { 18 | ListNode dummy1(-1); 19 | ListNode dummy2(-1); 20 | ListNode *l1 = &dummy1; 21 | ListNode *l2 = &dummy2; 22 | while (head != nullptr) { 23 | if (head->val < x) { 24 | l1->next = head; 25 | l1 = l1->next; 26 | } else { 27 | l2->next = head; 28 | l2 = l2->next; 29 | } 30 | head = head->next; 31 | } 32 | l1->next = dummy2.next; 33 | l2->next = nullptr; 34 | return dummy1.next; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /链表/092. 反转链表 II.cpp: -------------------------------------------------------------------------------- 1 | 92. 反转链表 II 2 | 3 | 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。 4 | 5 | 说明: 6 | 1 ≤ m ≤ n ≤ 链表长度。 7 | 8 | 示例: 9 | 10 | 输入: 1->2->3->4->5->NULL, m = 2, n = 4 11 | 输出: 1->4->3->2->5->NULL 12 | 13 | ================================ 14 | 15 | class Solution { 16 | public: 17 | 18 | // 1 -> [ 2 -> 3 -> 4 ] -> 5 19 | // 1 -> [ 4 -> 3 -> 2 ] -> 5 20 | // prev 21 | ListNode* reverseBetween(ListNode* head, int m, int n) { 22 | ListNode dummy; 23 | dummy.next = head; 24 | ListNode *prev = &dummy; 25 | for (int i = 1; i < m; ++i) prev = prev->next; 26 | ListNode *curr = prev->next; 27 | for (int i = m; i < n; ++i) { 28 | ListNode *tmp = curr->next; 29 | curr->next = tmp->next; 30 | tmp->next = prev->next; 31 | prev->next = tmp; 32 | } 33 | return dummy.next; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /链表/141. 环形链表.cpp: -------------------------------------------------------------------------------- 1 | 141. 环形链表 2 | 3 | class Solution { 4 | public: 5 | bool hasCycle(ListNode *head) { 6 | ListNode *fast = head, *slow = head; 7 | while (fast != nullptr && fast->next != nullptr) { 8 | fast = fast->next->next; 9 | slow = slow->next; 10 | if (fast == slow) return true; 11 | } 12 | return false; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /链表/142. 环形链表 II.cpp: -------------------------------------------------------------------------------- 1 | 142. 环形链表 II 2 | 3 | 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 4 | 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 5 | 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。 6 | 7 | 说明:不允许修改给定的链表。 8 | 9 | 题解: 10 | f=2s (快指针每次2步,路程刚好2倍) 11 | f = s + nb (相遇时,刚好多走了n圈) 12 | 推出:s = nb 13 | 14 | 从head结点走到入环点需要走 : a + nb, 而slow已经走了nb,那么slow再走a步就是入环点了。 15 | 如何知道slow刚好走了a步? 从head开始,和slow指针一起走,相遇时刚好就是a步 16 | 17 | class Solution { 18 | public: 19 | ListNode *detectCycle(ListNode *head) { 20 | ListNode *fast = head, *slow = head; 21 | while (fast && fast->next) { 22 | fast = fast->next->next; 23 | slow = slow->next; 24 | if (fast == slow) break; 25 | } 26 | if (fast == nullptr || fast->next == nullptr) return nullptr; 27 | fast = head; 28 | while (slow != fast) { 29 | fast = fast->next; 30 | slow = slow->next; 31 | } 32 | return slow; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /链表/147. 对链表进行插入排序.cpp: -------------------------------------------------------------------------------- 1 | 147. 对链表进行插入排序 2 | 3 | 对链表进行插入排序。 4 | 5 | class Solution { 6 | public: 7 | ListNode* insertionSortList(ListNode* head) { 8 | ListNode dummy; 9 | ListNode *cur = nullptr; 10 | while (head) { 11 | cur = &dummy; 12 | while (cur->next && cur->next->val <= head->val) { 13 | cur = cur->next; 14 | } 15 | ListNode *tmp = head->next; 16 | head->next = cur->next; 17 | cur->next = head; 18 | head = tmp; 19 | } 20 | return dummy.next; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /链表/148. 排序链表.cpp: -------------------------------------------------------------------------------- 1 | 148. 排序链表 2 | 3 | 分拆成两个链表分别递归调用sortList先排好序, 4 | 然后调用mergelist归并排序。 5 | 6 | class Solution { 7 | public: 8 | ListNode* sortList(ListNode* head) { 9 | if (head == nullptr || head->next == nullptr) return head; 10 | ListNode* slow = head, *fast = head, *pre = head; 11 | while (fast && fast->next) { 12 | pre = slow; 13 | slow = slow->next; 14 | fast = fast->next->next; 15 | } 16 | pre->next = nullptr; 17 | return merge(sortList(head), sortList(slow)); 18 | } 19 | ListNode* merge(ListNode* l1, ListNode *l2) { 20 | if (l1 == nullptr) return l2; 21 | if (l2 == nullptr) return l1; 22 | if (l1->val < l2->val) { 23 | l1->next = merge(l1->next, l2); 24 | return l1; 25 | } else { 26 | l2->next = merge(l1, l2->next); 27 | return l2; 28 | } 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /链表/160. 相交链表.cpp: -------------------------------------------------------------------------------- 1 | 160. 相交链表 2 | 3 | https://leetcode-cn.com/problems/intersection-of-two-linked-lists/ 4 | 5 | 编写一个程序,找到两个单链表相交的起始节点。 6 | 7 | 注意: 8 | 9 | 如果两个链表没有交点,返回 null. 10 | 在返回结果后,两个链表仍须保持原有的结构。 11 | 可假定整个链表结构中没有循环。 12 | 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。 13 | 14 | 题解: 15 | 16 | 当A走到头时候再从B开始走起, 17 | 当B走到头时候再从A开始走起。 18 | 链表A走了:a + c 19 | 链表B走了:b + c 20 | 这样两者一定会在c的开始处相遇,因为他们都走了a + b + c的距离。 21 | 22 | class Solution { 23 | public: 24 | ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { 25 | ListNode *a = headA, *b = headB; 26 | while (a != b) { 27 | a = a == nullptr ? headB : a->next; 28 | b = b == nullptr ? headA : b->next; 29 | } 30 | return a; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /链表/203. 移除链表元素.cpp: -------------------------------------------------------------------------------- 1 | 203. 移除链表元素 2 | 3 | 删除链表中等于给定值 val 的所有节点。 4 | 5 | 递归: 6 | class Solution { 7 | public: 8 | ListNode* removeElements(ListNode* head, int val) { 9 | if (head == nullptr) return nullptr; 10 | head->next = removeElements(head->next, val); 11 | return head->val == val ? head->next : head; 12 | } 13 | }; 14 | 15 | 非递归: 16 | class Solution { 17 | public: 18 | ListNode* removeElements(ListNode* head, int val) { 19 | if (head == nullptr) return nullptr; 20 | ListNode *cur = head; 21 | while (cur->next) { 22 | if (cur->next->val == val) cur->next = cur->next->next; 23 | else cur = cur->next; 24 | } 25 | return head->val == val ? head->next : head; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /链表/206. 反转链表.cpp: -------------------------------------------------------------------------------- 1 | 206. 反转链表 2 | 3 | 反转一个单链表。 4 | 5 | 示例: 6 | 7 | 输入: 1->2->3->4->5->NULL 8 | 输出: 5->4->3->2->1->NULL 9 | 10 | class Solution { 11 | public: 12 | ListNode* reverseList(ListNode* head) { 13 | ListNode *pre = NULL, *cur = head; 14 | while (cur) { 15 | ListNode *tmp = cur->next; 16 | cur->next = pre; 17 | pre = cur; 18 | cur = tmp; 19 | } 20 | return pre; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /链表/234. 回文链表.cpp: -------------------------------------------------------------------------------- 1 | 234. 回文链表 2 | 3 | 请判断一个链表是否为回文链表。 4 | 5 | class Solution { 6 | public: 7 | bool isPalindrome(ListNode* head) { 8 | if (!head || !head->next) return true; 9 | ListNode *slow = head, *fast = head; 10 | stack s; 11 | s.push(slow->val); 12 | while (fast->next && fast->next->next) { 13 | slow = slow->next; 14 | s.push(slow->val); 15 | fast = fast->next->next; 16 | } 17 | if (!fast->next) s.pop(); 18 | while (slow->next) { 19 | slow = slow->next; 20 | int temp = s.top(); s.pop(); 21 | if (temp != slow->val) return false; 22 | } 23 | return true; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /链表/382. 链表随机节点.cpp: -------------------------------------------------------------------------------- 1 | 382. 链表随机节点 2 | 3 | 给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。 4 | 5 | 进阶: 6 | 如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现? 7 | 8 | class Solution { 9 | public: 10 | /** @param head The linked list's head. 11 | Note that the head is guaranteed to be not null, so it contains at least one node. */ 12 | Solution(ListNode* head) { 13 | len = 0; 14 | this->head = head; 15 | ListNode *cur = head; 16 | while (cur) { 17 | cur = cur->next; 18 | ++len; 19 | } 20 | } 21 | 22 | /** Returns a random node's value. */ 23 | int getRandom() { 24 | int n = random() % len; 25 | ListNode *cur = head; 26 | while (n) { 27 | cur = cur->next; 28 | --n; 29 | } 30 | return cur->val; 31 | } 32 | 33 | private: 34 | int len; 35 | ListNode *head; 36 | }; 37 | -------------------------------------------------------------------------------- /链表/README.md: -------------------------------------------------------------------------------- 1 | 没做出来的题: 2 | 3 | 4 | | 题目 | 提示 | 5 | | ------------------------------------------------------------ | ------------------ | 6 | | [92. 反转链表 II](https://leetcode-cn.com/problems/reverse-linked-list-ii/)(必做) | 凡是改变链表结构的题都要加一个dummy指针 | 7 | | | | 8 | 9 | -------------------------------------------------------------------------------- /链表/面试题 02.02. 返回倒数第 k 个节点.cpp: -------------------------------------------------------------------------------- 1 | 面试题 02.02. 返回倒数第 k 个节点 2 | 3 | 实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。 4 | 5 | /** 6 | * Definition for singly-linked list. 7 | * struct ListNode { 8 | * int val; 9 | * ListNode *next; 10 | * ListNode(int x) : val(x), next(NULL) {} 11 | * }; 12 | */ 13 | class Solution { 14 | public: 15 | int kthToLast(ListNode* head, int k) { 16 | ListNode *fast = head, *slow = head; 17 | for (int i = 0; i < k; ++i) { 18 | if (fast == nullptr) return -1; 19 | fast = fast->next; 20 | } 21 | while (fast != nullptr) { 22 | fast = fast->next; 23 | slow = slow->next; 24 | } 25 | return slow->val; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /链表/面试题 02.03. 删除中间节点.cpp: -------------------------------------------------------------------------------- 1 | 面试题 02.03. 删除中间节点 2 | 3 | 实现一种算法,删除单向链表中间的某个节点(即不是第一个或最后一个节点),假定你只能访问该节点。 4 | 5 | class Solution { 6 | public: 7 | void deleteNode(ListNode* node) { 8 | if (node->next == nullptr) { 9 | node = nullptr; 10 | } else { 11 | node->val = node->next->val; 12 | node->next = node->next->next; 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /队列/347. 前 K 个高频元素.cpp: -------------------------------------------------------------------------------- 1 | 347. 前 K 个高频元素 2 | 3 | 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 4 | 5 | class Solution { 6 | public: 7 | vector topKFrequent(vector& nums, int k) { 8 | unordered_map m; // val:freq; 9 | vector res; 10 | priority_queue> q; 11 | for (auto i : nums) { 12 | m[i]++; 13 | } 14 | for (auto it : m) { 15 | q.push(pair(it.second, it.first)); 16 | } 17 | for (int i = 0; i < k; ++i) { 18 | res.push_back(q.top().second); 19 | q.pop(); 20 | } 21 | return res; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /队列/973. 最接近原点的 K 个点.cpp: -------------------------------------------------------------------------------- 1 | 973. 最接近原点的 K 个点 2 | 3 | 我们有一个由平面上的点组成的列表 points。需要从中找出 K 个距离原点 (0, 0) 最近的点。 4 | (这里,平面上两点之间的距离是欧几里德距离。) 5 | 你可以按任何顺序返回答案。除了点坐标的顺序之外,答案确保是唯一的。 6 | 7 | class Solution { 8 | public: 9 | vector> kClosest(vector>& points, int K) { 10 | priority_queue> q; 11 | for (int i = 0; i < K; ++i) { 12 | q.push({points[i][0] * points[i][0] + points[i][1] * points[i][1], i}); 13 | } 14 | for (int i = K; i < points.size(); ++i) { 15 | q.push({points[i][0] * points[i][0] + points[i][1] * points[i][1], i}); 16 | q.pop(); 17 | } 18 | vector> ans; 19 | for (int i = 0; i < K; ++i) { 20 | ans.push_back({points[q.top().second][0], points[q.top().second][1]}); 21 | q.pop(); 22 | } 23 | return ans; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /队列/剑指 Offer 59 - I. 滑动窗口的最大值.cpp: -------------------------------------------------------------------------------- 1 | https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/ 2 | 3 | 这个题目的解法就是构造一个单调递减双向队列deque来描述sliding window, 4 | 当deque记录的是当前数字的下标。当deque的队尾的数字小于nums[i]时,弹出队尾数字,直到不小于nums[i]再把i进入deque。 5 | 这时候能够保证deque里面一定是最大的k个数值。 6 | 7 | class Solution { 8 | public: 9 | vector maxSlidingWindow(vector& nums, int k) { 10 | vector res; 11 | deque q; 12 | for (int i = 0; i < nums.size(); ++i) { 13 | if (!q.empty() && q.front() == i - k) q.pop_front(); 14 | while (!q.empty() && nums[q.back()] < nums[i]) { 15 | q.pop_back(); 16 | } 17 | q.push_back(i); 18 | if (i >= k - 1) res.push_back(nums[q.front()]); 19 | } 20 | return res; 21 | } 22 | }; 23 | --------------------------------------------------------------------------------