题目描述
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
题解
先二分法找行,再二分法找列。
最后提交的版本:
1 | bool searchMatrix(vector<vector<int>>& matrix, int target) { |
下面是修改之前找行数的while循环。修改之后的明显时间上快了许多。
1 | while (up <= down) |
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
先二分法找行,再二分法找列。
最后提交的版本:
1 | bool searchMatrix(vector<vector<int>>& matrix, int target) { |
下面是修改之前找行数的while循环。修改之后的明显时间上快了许多。
1 | while (up <= down) |
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。你可以对一个单词进行如下三种操作:
果然最近都是考动态规划!编辑距离我应该是会的,明天试试~
1 | int minDistance(string word1, string word2) { |
一遍过,正常发挥。
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
进阶:
一个直接的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个常数空间的解决方案吗?
O(m + n) 的额外空间也就是两个数组,记录哪一行全1,哪一列全1吧,常数空间的话,看了下官方题解,是在原有的矩阵上做标记,m[i][j]=0,就把m[0][j]和m[i][0]都置为零。
emm……官方题解有问题,用特殊数字做标记行不通。就写 O(m + n) 的吧。
1 | void setZeroes(vector<vector<int>>& matrix) { |
执行用时 :12 ms, 在所有 C++ 提交中击败了96.46%的用户
内存消耗 :11.4 MB, 在所有 C++ 提交中击败了68.26%的用户
我觉得可以接受。
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
或许需要用一个栈,遇到文件名压入,遇到“/../”弹出,遇到”/./“忽略,连续的斜杠只算一个,最后再把栈里剩下的连起来。
1 | string simplifyPath(string path) { |
其实应该是可以在原来的字符串上直接修改的。
while语句总是忘了写递增条件,这是什么坏毛病。
…居然是文件名??太过分了,测试如下:
1 | [root@iZm5efgntvp9yn8px32ud1Z ~]# cd ... |
实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
二分法
1 | int mySqrt(int x) { |
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?注意:给定 n 是一个正整数。
动态规划
1 | int climbStairs(int n) { |
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
不适合转成整数加1后再存进去,如果数组太长的话,整型存不下。
1 | vector<int> plusOne(vector<int>& digits) { |
给定两个二进制字符串,返回他们的和(用二进制表示)。
输入为非空字符串且只包含数字 1
和 0
。
逐位判断
1 | string addBinary(string a, string b) { |
有点慢……改为位操作:首先计算两个数字的无进位相加结果和进位,然后计算无进位相加结果与进位之和。同理求和问题又可以转换成上一步,直到进位为 0 结束。
1 | class Solution: |
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
感觉,和62, 63题的思路没有区别。可不可以把二维数组缩减为一位数组呢?没想出来……下面这样其实也还不错。
1 | int minPathSum(vector<vector<int>>& grid) { |
验证给定的字符串是否可以解释为十进制数字。
说明: 我们有意将问题陈述地比较模糊。在实现代码之前,你应当事先思考所有可能的情况。这里给出一份可能存在于有效十进制数字中的字符列表:
数字 0-9
指数 - “e”
当然,在输入中,这些字符的上下文也很重要
有种自动机的味道。
剩下的就是各种面向特殊样例编程的过程了。
1 | int start = 0; |
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
倒不是很难,但是怎么让效率高一些?最初的想法是新创建一个链表,利用原链表的值,找对应关系,完成新链表;后来觉得也可以直接改变next指针,毕竟两个子链表内部的顺序是不变的。
1 | ListNode* rotateRight(ListNode* head, int k) { |
执行用时 :8 ms, 在所有 C++ 提交中击败了86.96%的用户
内存消耗 :8.9 MB, 在所有 C++ 提交中击败了58.02%的用户
果然不错。
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
说明:m 和 n的值均不超过 100。
动态规划,用一个二维数组记录路径数。
1 | int uniquePaths(int m, int n) { |
时间我很满意。
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1
和 0
来表示。
说明:m 和 n 的值均不超过 100。
思路与上一题相同,细节上做了一些修改。
1 | int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) { |
给定一个正整数 n,生成一个包含 1 到 $n^2$ 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
与上次螺旋矩阵思路类似,还是按照顺时针的顺序,先把空填了,再按顺序读出来。看起来效率还不错。
1 | vector<vector<int>> generateMatrix(int n) { |
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
“123”
“132”
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
有一说一,我觉得我的思路不错。不用挨着找出每个排列,而是计算k与(n-1)! 的关系
1 | int fac(int n) |
给出一个无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
1 | vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) { |
怎么觉得我的代码像一件缝缝补补的烂衣裳……
不过时间效率还是不错的。
给定一个仅包含大小写字母和空格 ‘ ‘ 的字符串 s,返回其最后一个单词的长度。
如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。
如果不存在最后一个单词,请返回 0 。
说明:一个单词是指仅由字母组成、不包含任何空格的 最大子字符串。
用split()函数应该挺快的,不过C++没有。
1 | int lengthOfLastWord(string s) { |
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
最先浮现在脑中的是一个图的结构。
其实是看起点最远能到达的点吧?动态规划:$dp[i]=max( \sum_{k=1}^{nums[i]}dp[i+k] ,i)$
更进一步,或许连dp数组都不需要呢?还没想出来,先要着吧。
在细节上做了一点修改。
1 | bool canJump(vector<int>& nums) { |
倒是没出什么大问题,只不过时空效率低得没脸看……
给出一个区间的集合,请合并所有重叠的区间。
这个时候排序就很重要了。如果是一个二维数组,也可以是用sort,我们可以选择根据某一列来进行排序,如果我们不重写cmp函数,那么默认的是根据第一列来排序。
1 | vector<vector<int>> merge(vector<vector<int>>& intervals) { |