leetcode刷题笔记
2019.01.11
905.Sort Array By Parity
描述:给一组数组,按先偶数后奇数的方式返回该数组。
Input: [3,1,2,4]
Output: [2,4,3,1]
高票代码:
vector<int> sortArrayByParity(vector<int> &A) {
for (int i = 0, j = 0; j < A.size(); j++)
if (A[j] % 2 == 0) swap(A[i++], A[j]);
//i代表数组中第一个奇数的位置,j代表第一个偶数的位置,全面互换之后即得目标数组
return A;
}
2019.01.13
150.Evaluate Reverse Polish Notation
描述:计算逆波兰表达式
Input: [“10”, “6”, “9”, “3”, “+”, “-11”, “", “/”, "”, “17”, “+”, “5”, “+”]
Output: 22
Explanation:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
思路:由于每一个运算符号之前必定有两个数,并且先出现的符号先算。按这个特性,使用栈可以完美解决。遍历数组的同时,将每一个数都压入栈中。当出现运算符号时,弹出栈顶的两个数字进行运算,并将运算结果压入栈中。直到遍历结束,栈顶的数字就是答案。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> ans;
int a,b;
for(int i=0;i<tokens.size();i++)
{
if(tokens[i]!="+" && tokens[i]!="-" && tokens[i]!="*" && tokens[i]!="/")
{
ans.push(stoi(tokens[i]));
}
else
{
b=ans.top();
ans.pop();
a=ans.top();
ans.pop();
if(tokens[i]=="+") ans.push(a+b);
else if(tokens[i]=="-") ans.push(a-b);
else if(tokens[i]=="*") ans.push(a*b);
else if(tokens[i]=="/") ans.push(a/b);
}
}
return ans.top();
}
};
2019.01.14
287.Find the Duplicate Number
描述:给定一个包含n + 1个整数的数组,其中每一个整数均介于[1, n]之间,证明其中至少有一个重复元素存在。假设只有一个数字出现重复,找出这个重复的数字。
Input: [1,3,4,2,2]
Output: 2
思路:将题目中数组与下标抽象为链表,即
input[0]=1;
input[1]=3;
input[3]=2;
…
根据题意可知,该链表必定存在一个环,而只有重复数才会映射到同一个位置。因此环的起点的值就是重复数,这就变成了Floyd算法中寻找环的起点的问题。
设定快慢指针,先找到相遇的位置之后,再找环的起点:
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int slow=0,fast=0;
do
{
slow=nums[slow];
fast=nums[nums[fast]];
}while(slow!=fast);
fast=0;
while(slow!=fast)
{
slow=nums[slow];
fast=nums[fast];
}
return slow;
}
};
2019.01.15
11.Container With Most Water
描述:给定n个非负整数a1,a2,…,an,其中每个代表一个点坐标(i,ai)。n个垂直线段例如线段的两个端点在(i,ai)和(i,0)。找到两个线段,与x轴形成一个容器,使其包含最多的水。
思路:除了暴力枚举没想出好的思路,百度了一下。使用双指针分别从左端和右端开始,每次移动高度较低的指针。
因为一开始的低是最长的,而水的多少由短边确定,因此如果向内移动长边没有意义,只会让水更少。因此每次都移动短边的指针,记下水的容量。直到两指针相遇,最多的水的容量即为返回值。
class Solution {
public:
int maxArea(vector<int>& height) {
int left=0,right=height.size()-1;
int max=0,area=0;
while(left<right)
{
if(height[left]<height[right])
{
area=(right-left) * height[left];
left++;
}
else
{
area=(right-left) * height[right];
right--;
}
if(area>max)
max=area;
}
return max;
}
};
15.3Sum
描述:给一组整数,寻找和为0的三个数,求出符合条件的所有解。
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:类似于寻找两数之和为0的思路,首先将数组排序。接着第一层循环确定第一个数,即将第一个数确定为target,之后使用双指针分别从target的右边和数组的末尾向中间移动。
如果两指针之和大于-target,右指针向左,反之左指针向右。如果相等则记录这三个数。
需要注意的是,移动指针时需判断移动后指针指向的数与现在是否相同,如果相同则跳过,否则结果中会出现重复解。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
std::sort(nums.begin(),nums.end());
vector<vector<int> > ans;
vector<int> tmp;
if(nums.size()<3)
return ans;
for(int i=0;i<nums.size()-2;)
{
int target=0-nums[i];
int l=i+1,r=nums.size()-1;
while(l<r)
{
if(nums[l]+nums[r]>target)
r--;
else if(nums[l]+nums[r]<target)
l++;
else
{
tmp.clear();
tmp.push_back(-target);
tmp.push_back(nums[l]);
tmp.push_back(nums[r]);
ans.push_back(tmp);
while(nums[l]==nums[++l] && l<r);//避免出现重复解
}
}
while(-target==nums[++i] && i<nums.size()-2);//避免出现重复解
}
return ans;
}
};
16.3Sum Closest
描述:求出距离目标数最近的3个数的和。
Given array nums = [-1, 2, 1, -4], and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
思路:采取与上一题类似的思路,只需在对撞指针的循环内改判断条件即可,以最小的差值作为判断条件。
一开始写出来时,运算时间较长,只超过60%。后来发现只要判断差值为0时直接返回,就能避免后面没有必要的寻找。
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
std::sort(nums.begin(),nums.end());
int mintmp=99999,tmp,ans;
if(nums.size()<3)
return ans;
for(int i=0;i<nums.size()-2;i++)
{
int target1=target-nums[i];
int l=i+1,r=nums.size()-1;
while(l<r)
{
tmp=nums[l]+nums[r]-target1;
if(tmp>0)
r--;
else
l++;
if(tmp==0)//如果为0,则不必继续寻找
return tmp+target;
if(abs(tmp)<mintmp)
{
mintmp=abs(tmp);
ans=tmp+target;
}
}
}
return ans;
}
};
2019.01.17
18.4Sum
描述:求出数组内4个数之和为target的全部组合。
Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
思路:与之前的3sum类似,把3sum的代码再加一层遍历,先求出两个数的和。然后使用对撞指针寻找剩下的两个数,同样的是需要进行重复判断,避免出现重复解。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
std::sort(nums.begin(),nums.end());
vector<vector<int> > ans;
vector<int> tmp;
int tmptarget;
if(nums.size()<4)
return ans;
for(int i=0;i<nums.size()-3;)
{
for(int j=i+1;j<nums.size()-2;)
{
tmptarget=target-nums[i]-nums[j];
int l=j+1,r=nums.size()-1;
while(l<r)
{
if(nums[l]+nums[r]>tmptarget)
r--;
else if(nums[l]+nums[r]<tmptarget)
l++;
else
{
tmp.clear();
tmp.push_back(nums[i]);
tmp.push_back(nums[j]);
tmp.push_back(nums[l]);
tmp.push_back(nums[r]);
ans.push_back(tmp);
while(nums[l]==nums[++l] && l<r);//避免出现重复解
}
}
while(nums[j]==nums[++j] && i<nums.size()-2);//避免出现重复解
}
while(nums[i]==nums[++i] && i<nums.size()-3);//避免出现重复解
}
return ans;
}
};
31.Next Permutation
描述:产生下一个序列,对给定序列进行重排,生成一个字母顺序比它更大的下一个序列。如果给定的序列已经是按字母顺序排列中最大的一个了,则进行逆序排列。算法在数组内进行,不要使用额外空间。
就是说在这几个数字构成的数进行排序,返回现有数的下一个数。如果已经是最大的,则返回最小的。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
思路:从后向前遍历寻找第一个降序数字,找到之后将该数字之后的数组排序。在排序的部分中选一个比降序数字大的最小数,将将两者互换即为结果。
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int r=nums.size()-1,min=r;
while(r>=0)
{
if(r==0)//从后向前完全升序数列
{
r--;
break;
}
if(nums[r]>nums[--r] )//寻找第一个降序数字
break;
}
if(r==-1)
reverse(nums.begin(),nums.end());
else
{
sort(nums.begin()+r+1,nums.end());
int j=r+1;
for(;nums[j]<=nums[r] && j<nums.size();j++);//寻找比降序数字大的最小数
swap(nums[r],nums[j]);
}
}
};
2019.01.22
33.Search in Rotated Sorted Array
描述:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
思路:由于该数组没有重复数字。且只在一个随机位置进行旋转。故可以使用二分法,先找到一组个有序的上升序列。二分之后,至少有一组是上升序列。判断目标是否在该组上升序列中,如果是则在该组中继续二分搜索。如果不在该上升序列中,则在另一组中继续二分,并搜索上升序列。直到找到目标,或者左右指针重合。
class Solution {
public:
int search(vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
int tmp=nums.size()/2;
if(nums.empty())
return -1;
if(nums[0]==target)
return 0;
if(nums[nums.size()-1]==target)
return nums.size()-1;
while(tmp!=l && tmp!=r)
{
if(nums[tmp]==target)
return tmp;
int tmplr=tmp;
if(nums[l]<nums[tmp])
{
if(nums[l]<target && target<nums[tmp])
{
tmp=(tmp+l)/2;
r=tmplr;
}
else
{
tmp=(tmp+r)/2;
l=tmplr;
}
}
else
{
if(nums[tmp]<target && target<nums[r])
{
tmp=(tmp+r)/2;
l=tmplr;
}
else
{
tmp=(tmp+l)/2;
r=tmplr;
}
}
}
return -1;
}
};
34.Find First and Last Position of Element in Sorted Array
描述:求 target 在有序数组 nums 中出现的最小下标和最大下标组成的数组。否则返回 {-1, -1}。时间复杂度为 O(log n)。
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
思路:二分法搜索,此题关键在于可能存在找不到解的情况。此时二分法可能会陷入死循环,因此将循环跳出条件设为,tmp是否变化。如果tmp不变说明没有结果并进入死循环,跳出。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
int tmp=r,tmptmp=-2;
vector<int> ans;
if(nums.empty())
{
ans.push_back(-1);
ans.push_back(-1);
return ans;
}
while(tmptmp!=tmp)
{
tmptmp=tmp;
if(nums[tmp]==target)
{
int ansl=tmp,ansr=tmp;
while(nums[--ansl]==target && ansl>=0);
while(nums[++ansr]==target && ansr<=nums.size()-1);
ans.push_back(ansl+1);
ans.push_back(ansr-1);
return ans;
}
if(nums[tmp]<target)
l=tmp;
else
r=tmp;
tmp=(l+r)/2;
}
ans.push_back(-1);
ans.push_back(-1);
return ans;
}
};
2019.01.23
39.Combination Sum
描述:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
Input: candidates = [2,3,5], target = 8,
A solution set is:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
思路:回溯算法,先将元素不断的放入tmp中,直到tmp中的和大于target。由于已经将数组排序了,因此当tmp中的和已经过大时,那么上一轮遍历中之后的数也不需要再放入tmp了,这样可以很大程度提高效率。
class Solution {
public:
vector<vector<int>> ans;
vector<int> tmp;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
dfs(candidates,target,tmp,0);
return ans;
}
bool dfs(vector<int> candidates, int target,vector<int>& tmp, int index)
{
if(target == 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
{
ans.push_back(tmp);
return 1;
}
if(target < 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
return 1;
for(int i=index;i<candidates.size();i++)
{
tmp.push_back(candidates[i]);
if(dfs(candidates,target-candidates[i],tmp,i))
{
tmp.pop_back();
return 0;//结束本轮迭代
}
tmp.pop_back();
}
return 0;
}
};
2019.01.24
40.Combination Sum II
描述:给定一个有重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字只能被选一次,且不能有重复解。
Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
思路:与上一题非常类似,区别在于每个数只能被拿一次,且数组有重复数。因此只需在循环中增加约束条件,使放入tmp的数不得与之前重复即可。
class Solution {
public:
vector<vector<int>> ans;
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<int> tmp;
sort(candidates.begin(),candidates.end());
dfs(candidates,target,tmp,-1);
return ans;
}
bool dfs(vector<int> candidates, int target,vector<int>& tmp, int index)
{
if(target == 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
{
ans.push_back(tmp);
return 1;
}
if(target < 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
return 1;
int i=index+1;
while(i<candidates.size())
{
tmp.push_back(candidates[i]);
if(dfs(candidates,target-candidates[i],tmp,i))
{
tmp.pop_back();
return 0;//结束本轮迭代
}
tmp.pop_back();
while(candidates[i]==candidates[++i]);//由于不允许出现重复解,因此如果有相同的数字跳过
}
return 0;
}
};
48.Rotate Image
描述:将给定的二维数组顺时针旋转90度,并且只能在数组内操作,不得另外开辟数组。
Given input matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
rotate the input matrix in-place such that it becomes:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
思路:找规律,将每一层的正方形边上的数挨个互换。一层结束之后,就向内走一层,注意内层的起点和终点受约束。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n=matrix.size()/2;
int m=matrix.size()-1;
for(int i=0;i<n;i++)//按行遍历正方形的边,只遍历一半的行数即可
for(int j=i;j<m-i;j++)//按列遍历正方形的边,起点和终点受行数的约束
{
swap(matrix[i][j],matrix[m-j][i]);
swap(matrix[m-j][i],matrix[m-i][m-j]);
swap(matrix[m-i][m-j],matrix[j][m-i]);
}
}
};
2019.01.25
55.Jump Game
描述:给定一个数组,每个数字代表当前可向后走的最大步数。从第一个数字开始,求是否能走到最后一个数字位置,若能,返回true,否则返回false。
Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
思路:刚看到这一题,觉的回溯法肯定可以解。于是写了一个,大概思路就是尝试每个位置所有种可能,如果抵达终点就返回正确,否则继续递归。这种方法对于正常数组可能会比较快的判断,但是对于某些极端倒序的数组效率极低。最终结果只超过1.5%。代码如下:
class Solution {
public:
bool canJump(vector<int>& nums) {
bool flag[nums.size()];
for(int i=0;i<nums.size();i++)
flag[i]=0;
return dfs(nums,0,flag);
}
bool dfs(vector<int>& nums,int index,bool *flag)
{
int n=nums[index];
if(index>=nums.size()-1)
return 1;
if(n==0 || flag[index])
return 0;
for(int i=n;i>0;i--)
{
if(dfs(nums,index+i,flag))
return 1;
}
flag[index]=1;
return 0;
}
};
之后看了高票答案,对于所有情况时间复杂度都是O(n),并且空间复杂度常数级。思路就是从前至后遍历整个数组,同时记下当前所知的能到达的最远位置reach。同时把这个reach作为约束条件。如果在遍历到结尾之前,退出循环,说明reach未到终点。反之则说明可以抵达终点,代码如下:
class Solution {
public:
bool canJump(vector<int>& nums) {
int n=nums.size();
int i=0;
for(int reach=0;i<n && i<=reach;i++)
reach=max(i+nums[i],reach);
return i==n;
}
};
2019.02.14
56.Merge Intervals
描述:给定一个数组,将数组内重叠的区间提取出来。
Input: [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
思路:观察给定数组可知,需要先将数组排序。接着维护一个临时重叠区间,通过对数组内接下来的区间进行遍历。由于数组已经排序,故后续区间的左端点一定比临时重叠区间大。如果该区间的左端点比临时重叠区间的右端点小,那么可以将临时区间的右端点取:当前区间右端点 和 临时重叠区间右端点 的大值。
而如果后续区间的左端点已经比临时重叠区间右端点大了,那么将该临时重叠区间保存,并重新开辟新的临时重叠区间,继续遍历。
由于数组是有序的,所以ans数组内的最后一个区间就是临时区间,可以节省空间。
/**
* Definition for an interval.
* struct Interval {
* int start;
* int end;
* Interval() : start(0), end(0) {}
* Interval(int s, int e) : start(s), end(e) {}
* };
*/
class Solution {
public:
static bool comp(const Interval &a,const Interval &b)
{
return a.start<b.start;
}
vector<Interval> merge(vector<Interval>& intervals) {
sort(intervals.begin(), intervals.end(), comp);
if(intervals.empty())
return intervals;
vector<Interval> ans;
ans.push_back(intervals[0]);
for(int i=1;i<intervals.size();i++)
{
if(intervals[i].start<=ans.back().end)
ans.back().end=max(ans.back().end,intervals[i].end);
else
ans.push_back(intervals[i]);
}
return ans;
}
};
62.Unique Paths
描述:输入一个m×n的栅格,只能往下和右走,求出从[0,0]到[m-1,n-1]有多少种走法。
Input: m = 3, n = 2
Output: 3
思路:采用动态规划思想,首先我们可以看出栅格地图的第一行和第一列只有一种走法。然后由于每个格子只能从它的左边或上边的格子过来,故可得出:
path[ i ] [ j ] =path[ i-1 ] [ j ] + path[ i -1] [ j ]
因此通过初始条件,遍历计算栅格地图每个格子即可求出走到右下角的走法总数。
class Solution {
public:
int uniquePaths(int m, int n) {
int map[m][n];
for(int i=0;i<n;)//第一行和第一列可知只有一种走法
map[0][i++]=1;
for(int i=0;i<m;)
map[i++][0]=1;
for(int i=1;i<m;i++)
for(int j=1;j<n;j++)
map[i][j]=map[i-1][j]+map[i][j-1];//动态规划
return map[m-1][n-1];
}
};
2019.02.15
63.Unique Paths II
描述:输入一个m×n的栅格,只能往下和右走,中间有障碍物,求出从[0,0]到[m-1,n-1]有多少种走法。
Input:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
Output: 2
思路:与上一题类似,只是中间增加了障碍物。可以采取类似的思路,第一行和第一列在障碍物之前只有一种走法,在障碍物之后则无法到达,即为0种走法。之后遍历剩下的栅格,没有障碍物的栅格计算方法相同,有障碍物的栅格走法为0 。
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m=obstacleGrid.size();
int n=obstacleGrid[0].size();
unsigned int map[m][n];
int i=0;
for(;i<n;i++)
if(obstacleGrid[0][i]==0)
map[0][i]=1;
else
break;
if(i<n)
for(;i<n;i++)
map[0][i]=0;
i=0;
for(;i<m;i++)
if(obstacleGrid[i][0]==0)
map[i][0]=1;
else
break;
if(i<m)
for(;i<m;i++)
map[i][0]=0;
for(int i=1;i<m;i++)
for(int j=1;j<n;j++)
if(obstacleGrid[i][j]==0)
map[i][j]=map[i-1][j]+map[i][j-1];//动态规划
else
map[i][j]=0;
return map[m-1][n-1];
}
};
64.Minimum Path Sum
描述:求在一个全为正整数的 m X n 的矩阵中, 取一条从左上为起点, 走到右下为重点的路径, (前进方向只能向左或者向右),求一条所经过元素和最小的一条路径。
Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.
思路:仍旧是动态规划的思路,求出递推表达式。由于只能往下或往右走,所以每个栅格仍然只能从上或者左过来,那么该栅格的最小值只能从这个两个值中取小值。因此仍旧是遍历剩下的栅格,计算最小值。
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
for(int i=1;i<grid.size();i++)
grid[i][0]+=grid[i-1][0];
for(int i=1;i<grid[0].size();i++)
grid[0][i]+=grid[0][i-1];
for(int i=1;i<grid.size();i++)
for(int j=1;j<grid[0].size();j++)
grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);
return grid[grid.size()-1][grid[0].size()-1];
}
};
73.Set Matrix Zeroes
描述:在一个全为正整数的 m X n 的矩阵中,将每个为0元素所在的行和列全部置为0。要求在原始矩阵内操作,空间复杂度为o(1)。
Input:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
Output:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
思路:一开始想建立一个数组记录所有0元素的行和列,但是题目主要考察空间复杂度的极小。看了高票答案,思路是只用一个变量记录第一列是否存在0。接着遍历剩下的元素,并用矩阵的第一行和第一列来记录该行/列是否需要置0。不同的是,接下来的循环对剩余矩阵的元素挨个进行置0操作,而不是整列操作。这样能节省一个空间,从而不需要在设置一个标志位来记录第一行是否存在0元素。
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int col0 = 1, rows = matrix.size(), cols = matrix[0].size();
for (int i = 0; i < rows; i++) {
if (matrix[i][0] == 0) col0 = 0;
for (int j = 1; j < cols; j++)
if (matrix[i][j] == 0)
matrix[i][0] = matrix[0][j] = 0;
}
for (int i = rows - 1; i >= 0; i--) {
for (int j = cols - 1; j >= 1; j--)
if (matrix[i][0] == 0 || matrix[0][j] == 0)
matrix[i][j] = 0;
if (col0 == 0) matrix[i][0] = 0;
}
}
};
74.Search a 2D Matrix
描述:在一个排好序的二维数组内查找目标值,并返回是否找到。
Input:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
Output: true
思路:肯定是使用二分搜索,不过该题关键在于将二维数组看成一维已排序数组处理,会大大降低时间复杂度。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.size()==0 || matrix[0].size()==0)
return false;
int m=matrix[0].size();
int n=matrix.size();
int l=0,r=m*n-1;
while(l!=r)
{
int mid=(l+r)/2;
if(matrix[mid/m][mid%m]<target)
l=mid+1;
else
r=mid;
}
return matrix[r/m][r%m]==target;
}
};
2019.02.19
75.Sort Colors
描述:将输入的数组按0,1,2的顺序分类放在一起。
Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
思路:自己想的思路,面对各种特殊情况有bug。看了高票代码,思路大概是遍历数组,碰到2时,往数组右端换;碰到1时,往数组左端换。这里的关键在于边界条件的设定,l代表第一个非0数,r代表倒数第一个非2数。发生交换之前需要判断i是否在l和r之间,因为之外的数是不需要发生交换的。而循环的终止条件为i大于r,代表所有的数都已经处理完毕,1就会留在中间。
class Solution {
public:
void sortColors(vector<int>& nums) {
int l=0,r=nums.size()-1,i=0;
for(;i<=r;i++)
{
while(nums[i]==2 && i<r)
swap(nums[i],nums[r--]);
while(nums[i]==0 && i>l)
swap(nums[i],nums[l++]);
}
}
};
2019.02.20
78.Subsets
描述:输入一个数组,返回该数组内数组的所有种可能的组合。
Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
思路:递归,没什么可说的。
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<int> tmp;
vector<vector<int>> ans;
ans.push_back(tmp);
if(nums.empty())
return ans;
rec(ans,nums,tmp,0);
return ans;
}
void rec(vector<vector<int>> &ans,vector<int>& nums,vector<int>& tmp,int index)
{
for(int i=index;i<nums.size();i++)
{
tmp.push_back(nums[i]);
ans.push_back(tmp);
rec(ans,nums,tmp,i+1);
tmp.pop_back();
}
}
};
2019.02.28
2.Add Two Numbers
描述:输入两个以链表形式表达的反数,返回两者相加之和。
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
思路:这题难度不大,关键是特殊情况的处理。主要是当两个输入的长度不一样时,设置val值为0.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* ans=new ListNode(0),*ans1=ans,*tmp;
int val1,val2;
while(l1 || l2)
{
val1=l1?l1->val:0;
val2=l2?l2->val:0;
ans->val+=val1+val2;
ans->next = new ListNode(ans->val / 10);
ans->val %= 10;
l1=l1?l1->next:nullptr;
l2=l2?l2->next:nullptr;
tmp=ans;
ans=ans->next;
}
if(ans->val ==0)
tmp->next=nullptr;
return ans1;
}
};
2019.03.06
3.Longest Substring Without Repeating Characters
描述:给一个字符串,返回不包含重复字母的最长子串长度
Input: “abcabcbb”
Output: 3
Explanation: The answer is “abc”, with the length of 3.
思路:建立一个哈希表,映射每一个出现的字母最后出现的位置。在遍历字符串的时候,首先通过查询哈希表得到该字母出现的最后位置。如果出现的最后位置大于start起始位置,则说明该字母已经出现过,则把该字母位置赋给start。接着把该位置更新为新出现的位置,并更新最大距离。
本质上是维护一个动态的窗口,由于右边界一直在向右探索,关键在于左边界的维护。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> hash(256,-1);
int maxlen=0,start=-1;
for(int i=0;i<s.length();i++)
{
if(hash[s[i]]>start)
start=hash[s[i]];
hash[s[i]]=i;
maxlen=max(maxlen,i-start);
}
return maxlen;
}
};