Leetcode解题思路总结(Medium)

本文总结了多个LeetCode算法题目的解题思路,包括Counting Bits、Single Number、Top K Frequent Elements、Product of Array Except Self等。通过位操作、动态规划、优先队列等方法,提供高效解决方案,如使用异或操作快速找到数组中只出现一次的数字,以及利用二叉树特性进行先序遍历等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

338. Counting Bits

思路:计算一个10进制数的2进制表示中含有多少个1,有2种方法,一种是bit manipulation,另一种是简单的dynamic programming。代码见:http://www.cnblogs.com/coderworld/p/leetcode-countingbits.html

136. Single Number

思路:一个数组中,每个数字都出现了2次,只有一个数字出现了1次,要求找到那个数。我一开始想到的方法是用扫描数组添加到set中,如果出现了2次就从set中erase掉,扫面完后set中唯一一个元素就是我们需要的返回值,写完代码AC结果发现要84ms,超级慢。然后我在搜了搜,发现有种20ms就AC的方法,那就是利用异或操作^,a^a=0, a^0=a,根据交换律a^b=b^a,比如3^4^3=4,根据异或操作的这种性质,我们可以将所有元素依次做异或操作,相同元素异或为0,最终剩下的元素就为Single Number。时间复杂度O(n),空间复杂度O(1)。代码见我

347. Top K Frequent Elements

思路:找出一个数组中出现次数最多的K个数。可以考虑用priority_queue,假如元素是<int, int>这种pair形式,那么优先队列就是按着key的降序进行的,所以队首一定是key最大的元素。这样从优先队列中pop出前K个元素,就可以得到结果了。不过问题是怎样把数组中的元素加到优先队列中。所以我们需要对数组做一次预处理,即统计每个数字出现的次数。可以用map,但是我选择用unordered_map,因为unordered_map是无序的,map是有序的,所以插入元素时unordered_map会快很多。最后代码运行36ms 如果用map的话,那就是60ms了。

260. Single Number III

思路:前面做了只有一个出现一次的数,其他都是出现两次,思路和那题一样。只是这个先将所有数一起异或,然后得到的结果将是两个只出现一次数的异或。也就是说最后得到的数字是a ^ b,而我们需要的是a和b而不是a和b的异或。那如何将a和b从(a^b)中分离呢?比如a是5(101),b是3(011),那么(a^b)就是6(110),观察一下6,6的第二位为1说明a和b在第二位肯定不同,然后就可以根据与这个位的数是否为0将数组分为两半,并且a和b肯定会被分别分开到这两个数组中。这样问题就回到了上题的思路,只要让子数组内元素全部异或一下,得到的两个数就是最终要的只出现一次的数。这段话可能有点绕,需要好好琢磨琢磨一下。判断1在哪一位就用0x01循环的去做&运算就好。代码只需要16ms

238. Product of Array Except Self

思路:解法比较巧妙。由于output[i] = (x0 * x1 * ... * xi-1) * (xi+1 * .... * xn-1)。因此执行两趟循环:
第一趟正向遍历数组,计算x0 ~ xi-1的乘积
第二趟反向遍历数组,计算xi+1 ~ xn-1的乘积

运行时间64ms

122. Best Time to Buy and Sell Stock II

思路:解法比较巧妙。题目说明可以多次买卖,但是同一时间只能有一股在手里。 这样就可以在每次上升子序列之前买入,在上升子序列结束的时候卖出。相当于能够获得所有的上升子序列的收益。 而且,对于一个上升子序列,比如:5,1,2,3,4,0 中的1,2,3,4序列来说,

对于两种操作方案: 

一,在1买入,4卖出; 

二,在1买入,2卖出同时买入,3卖出同时买入,4卖出; 

这两种操作下,收益是一样的。 所以算法就是:从头到尾扫描prices,如果i比i-1大,那么price[i] – price[i-1]就可以计入最后的收益中。这样扫描一次O(n)就可以获得最大收益了。

运行时间为8ms

319. Bulb Switcher

思路:这是一道数学题。大概解释一下,当一个灯泡被执行偶数次switch操作时它是关着的,当被执行奇数次switch操作时它是开着的,那么这题就是要找出哪些编号的灯泡会被执行奇数次操作。将灯泡编号为1到n,第i个灯泡会在第d轮被switch,当且仅当d能整除i(i % d == 0)。所以第i个灯泡只有当switch了奇数次后,它才会亮着。

而一个数的质因数是成对存在的。比如12,它能被1、12、2、6、3、4整除,并且12被switch了偶数次,所以最后还是灭着的。所以问题就转变成了求一个数的质因子数有多少个。再来看下36,36有质因数1、36、2、18、3、12、4、9.还有2个6.所以它的distinct的质因数是奇数个,它最后会亮着。

可以发现规律,只有当i它是一个平方数的时候,第i个灯泡最后会亮。所以问题就变成了求1到n范围内的平方数有多少个。

所以直接用sqrt(n),就可以求出1到n范围内的平方数的数目了。这道题真是考数学和智商啊23333

343. Integer Break

思路:一个数,把它分为至少2个数的和的形式,然后求分解的各个数的乘积的最大值。比如9=3+3+3,9=4+5.但是3*3*3=27是大于4+5的,所以最后返回27.这道题也是一道数学找规律题。可以尝试着手写2到12之间的数字。然后发现的规律就是把每个数分解为3+3+···+2+···+2的形式是最优的。并且3的数目要尽量的多,只有当不能取够3的时候,再去取2.代码可见:0ms

268. Missing Number

思路:求一个数组中少了哪个数,比如[0,1,3]里面少了2,数组里的数都是distinct的,数组的长度如果是3,那么就从0到3里挑3个数存入数组,然后让你找少了哪个数。有2种方法可以AC。

第一种是数学方法:可以先求0到n的和,然后求数组的sum,然后把他们一相减,得到的数字就是答案。运行时间32ms

第二种是bit位操作:利用异或的性质。把数组里的每个数和下标以及数组长度连环做异或。那么得到的就是答案。运行时间36ms

比如0^1^3 ^0^1^2^3 = 2,2就是缺少的那个数,也就是答案。

144. Binary Tree Preorder Traversal

思路:二叉树先根遍历。有递归和非递归2种方式,递归很简单:0ms

非递归就是利用一个stack,不过记得每次是先把右节点push进来,然后再push左节点。时间也是0ms

318. Maximum Product of Word Lengths

思路:在一个字符串组成的数组words中,找出max{Length(words[i]) * Length(words[j]) },其中words[i]和words[j]中没有相同的字母。

方法就是先把每一个单词编码为26位的二进制数,比如abcd就是00000000000000000000001111,abce就是00000000000000000000010111。然后,两两之间互相比较,如果位与运算为0,则说明两者没有共同字母,则可以进行乘法。然后在这两层循环的过程中不断更新最大值,循环结束后就得到返回值了。时间复杂度为O(n*n)。运行时间为128ms

94. Binary Tree Inorder Traversal

思路:二叉树中序遍历。有递归和非递归2种方式,递归很简单:3ms

非递归就是利用一个stack,对于任一结点P,

1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理; 

2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子; 

3)直到P为NULL并且栈为空则遍历结束

时间0ms

可参考:http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html

12. Integer to Roman

思路:整数转换为罗马数字。分析罗马数字的规律。就把罗马数字和对应的整数存入数组,然后逐个循环遍历。

我们现在拿到一个数N 我们就去表里面找不超过它的最大的数x, 然后把它入我们的输出字符串中,然后将数N-=x, 继续执行这个操作,直到N=0。运行时间32ms

230. Kth Smallest Element in a BST

思路:求二叉查找树的第k个最小元素。注意到二叉查找树的中序遍历结果是有序的,可以利用这个性质来输出结果。用递归的方法做24ms

137. Single Number II

思路:一个数组中,除了一个数之外,其他全部元素都出现了3次,找出那个数。我用的很简单暴力的方法,就是先直接全部存入unordered_map中(因为不用排序,比map要快),key是每个元素的值,value是每个元素出现的次数。然后再遍历整个map,找出value不为3的元素,返回就是答案了,运行28ms  还有一种是位运算,但是我没搞懂,希望有大神能给我讲解一下

96. Unique Binary Search Trees

思路:一个典型的动态规划题,后面的计算依赖于前面产生的结果。假如当前根节点是A,则这棵树的二叉树的数目为#(左子树)×#(右子树),而根据二叉查找树的规律,左子树跟右子树是互相没有交集的,并且左子树的任意元素的值都要小于右子树的任意元素的值。以一个count数组来表示共有i个节点时,能产生的BST树的个数。

n == 0 时,count(0) = 1,

n == 1 时,count(1) = 1,因为只有1这个根节点,数量也为1。

n == 2 时,分2种情况考虑,一种是根为1,另一种是根为2.

n = 2; 1__ __2  

\ /  

count[1] count[1]  

count(2) = count(0) × count(1) + count(1) × count(0) = 2

n == 3 时,分3种情况考虑,根分别为1、2、3,

count(3) = count(0) × count(2) + count(1) × count(1) + count(2) × count(0) = 5

同时,当根节点元素为 1, 2, 3, 4, 5, ..., i, ..., n时,基于以下原则的BST树具有唯一性: 以i为根节点时,其左子树构成为[0,...,i-1],其右子树构成为[i+1,...,n]构成 

因此,count[i] = sigma(count[0...k] × count[k+1...i]) 0 <= k < i - 1

所以只要找到规律,就能0ms通过

35. Search Insert Position

思路:二分查找,但是需要变下型,因为其实这个问题是等价于找lower bound,所以每次就把left+1,9ms

108. Convert Sorted Array to Binary Search Tree

思路:把一个有序数组转换为平衡二叉搜索树。还是用二分查找递归构造,每次选择中点作为root,然后左子树就递归的用左半边数组,右子树就递归的用右半边数组。运行时间20ms

337. House Robber III

思路:https://leetcode.com/discuss/91899/step-by-step-tackling-of-the-problem

22. Generate Parentheses

思路:见CC150的9.6

78. Subsets

思路:详见:http://www.cnblogs.com/felixfang/p/3775712.html  递归的话可以参考九章算法的模板

90. Subsets II

思路:同上

46. Permutations

思路:最简单的是用C++的next_permutation函数去实现,记得先排序一下。12ms过。此外,自己实现的话,有递归和迭代两种方式,细细观察,可以从头开始找规律。

47. Permutations II

思路:受到这个解法的启发,以及这个博客的4种生成全排列的算法。这道题也可以用C++的next_permutation函数去AC过。我还是选择了用全排列树去AC,不过注意的是,如果2个需要交换的数相等的话或者要交换的数已经在set中,就不用交换了。40ms过的

74. Search a 2D Matrix

思路:二维数组里进行二分查找,记得用下标把二维转换为一维,此外注意以下几点,否则会造成死循环:记得在外层循环的判断条件里写left <= right,如果写的是left < right的话,就不会到达最右边的值。然后记得每次二分后left = mid +1 和right = mid - 1。1ms




LeetCode解题总结 1. 数组 1.1 从有序数组中删除重复元素 1.2 在排序数组被旋转后进行查找 1.3 寻找两个排序数组的中位数 1.4 最长连续序列 1.5 累加和 1.6 移除数组中指定值 1.7 下一个排列 1.8 第n个全排列 1.9 验证数独的正确性 1.10 容纳雨水的量 1.11 旋转图像 1.12 数字加1 1.13 爬楼梯 1.14 格雷码 1.15 设置矩阵的行列为0 1.16 加油站问题 1.17 分糖果 1.18 只出现一次的数 2. 单链表 2.1 单链表相加 2.2 指定位置反转单链表 2.3 依据给定值将链表重新排序 2.4 删除链表中重复元素 2.5 指定位置旋转链表 2.6 删除倒数第N个节点 2.7 成对交换链表元素 2.8 复制复杂链表 2.9 链表环相关问题 2.9.1 链表是否有环 2.9.2 链表环的入口 2.10 改变链表中的元素位置2.11 LRU Cache(设计题) 3. 字符串 3.1 判断字符串是否为回文 3.2 实现strStr() 3.3 字符串转为int(atoi) 3.4 二进制树相加 3.5 最长回文字符串 3.6 正则表达式匹配[hard] 3.7 正则匹配 3.8 最长公共前缀 3.9 验证字符串是否为数字 3.10 数字转为罗马数字 3.11 罗马数字到数字 3.12 Count and Say 3.13 变位词 3.14 简化系统路径 3.15 最后一个单词的长度 3.16 反转字符串中的单词 3.16.1 字符串前后和中间可能存在多个空格 3.16.2 不存在前后和中间的多余空格 3.17 一个编辑距离 4. 栈 4.1 验证括号的正确性 4.2 最长的正确括号表达式 4.3 柱状图中的最大矩形面积 4.4 计算逆波兰式的值 5. 树 5.1 二叉树的遍历 5.1.1 二叉树的前、中、后序遍历 5.1.2 二叉树的层序遍历 5.1.3 恢复二叉树[hard] 5.1.4 判断两棵树是否相等 5.1.5 判断二叉树是否为AVL树 5.1.6 将二叉树转为链表 5.1.7 二叉树添加指向右边节点的指针 5.1.8 树中节点的最小公共祖先 5.2 二叉树的构建5.3 二叉查找树 5.3.1 生成不重复的二叉查找树数目 5.3.2 验证是否为二叉查找树 5.3.3 将有序数组转为二叉树 5.3.4 将有序链表转为二叉树 5.4 二叉树的递归 5.4.1 二叉树的最大深度 5.4.2 二叉树的最小深度 5.4.3 路径和 5.4.4 满二叉树添加指向右边节点的指针 5.4.5 根节点到叶结点的所有路径代表的数字之和 6. 排序 6.1 合并两个有序数组到其中一个数组 6.2 合并两个有序链表 6.3 合并K个有序链表 6.4 使用插入排序来排序链表 6.5 归并排序排序链表 6.6 第一个缺少的正数 6.7 排序颜色 7. 查找 7.1 在排序数组中查找数出现的范围 7.2 在排序数组中查找给定值的插入位置 7.3 在二维排序数组中查找给定值 7.4 在旋转有序数组中查找最小值 7.4.1 数组无重复 7.4.2 数组有重复 7.5 在旋转排序数组中查找指定数字 8. 暴力枚举法 8.1 求集合的子集 8.2 集合的全排列 8.3 在指定树中选择进行全排列 8.4 电话上对应数字的字母组成的所有单词 9. 广度优先搜索 9.1 单词变换路径(Word Ladder) 9.1.1 是否存在变换路径 9.1.2 所有最短变换路径9.2 包围区域 10. 深度优先搜索 10.1 N皇后问题 10.2 恢复IP地址 10.3 集合元素之和 10.3.1 元素可以重复 10.3.2 元素不可重复 10.3.3 给定元素数目和元素范围 10.4 正确的括号对 10.5 解数独 10.6 单词搜索 10.7 小结 10.7.1 适用场景 10.7.2 思考步骤 10.7.3 代码模板 10.7.4 深搜与回溯、递归的区别 11. 分治法 11.1 实现pow(x, n) 11.2 Sqrt(x) 12. 贪心算法 12.1 跳台阶游戏 12.2 买卖股票的最佳时机 12.2.1 最多允许交易一次 12.2.2 可以交易任意多次 12.2.3 最多可以交易两次 12.2.4 可以交易任意多次 12.2.5 交易后需要停止一段时间 12.3 最长不含重复元素的子串 12.4 存放的最大水量 13. 动态规划 13.1 三角形从顶到底的最小路径和 13.2 最大连续子数组 13.3 字符串的所有子回文字符串 13.4 最长公共子序列问题 13.5 字符串的编辑距离 13.6 不同路径之和 13.6.1 无障碍13.6.2 有障碍 13.7 最大矩形面积 13.8 字符串交叉组合 13.9 旋转字符串 13.10 最小路径和 13.11 所有的编码方式 13.12 独一无二的子序列数 13.13 拆分单词 13.13.1 单词是否由词典中的单词组成 13.13.2 返回所有可以切分的解 14. 图 14.1 图的克隆 15. 细节实现题 15.1 反转整数 15.2 对称数判断 15.3 区间的相关操作 15.3.1 在区间中插入新的区间 15.3.2 合并区间 15.4 包含子串元素的最小窗口 15.5 大数乘法 15.6 给定串中是否存在包含所有单词的子串 15.7 Pascal 三角形 15.7.1 生成Pascal三角形 15.7.2 Pascal三角形的第N行 15.8 螺旋形矩阵 15.8.1 螺旋打印矩阵 15.8.2 生成螺旋矩阵 15.9 Z字形输出字符串 15.10 不使用乘、除、取模实现两个整数相除 15.11 文本对齐 15.12 共线的最大点数 16 其他问题 16.1 随机数生成器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值