删除有序数组中的重复项(easy)
首先分析题目:
有序的话说明只要相邻的两个做比较即可,不需要把当前数值和后面的全都比较一遍
其实就是改了nums这个vector的前index个值,后面的值并没有改,根据return的修改后的长度将修改后的数组输出
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.empty())
return 0;
int index=0;
for(int i=1;i<nums.size();i++)
{
if(nums[index]!=nums[i])
{
nums[++index]=nums[i];//先index加1,再赋值
}
}
return index+1;
}
};
删除有序数组中的重复项 II(medium)
和上面一题类似,只是允许重复的次数变为2
如果说次数为3,则只需将代码中的2变为3,第一步判断size那里3变为4
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size()<3)
return nums.size();
int index=2;
for(int i=2;i<nums.size();i++)
{
if(nums[i]!=nums[index-2])
{
nums[index]=nums[i];//先赋值,再index加1
index=index+1;
}
}
return index;
}
};
搜索旋转排序数组(medium)
首先分析题目,medium题肯定不能直接全对比一遍,本题主要考二分搜索,这种情况下
时间复杂度:O(log n)
空间复杂度:O(1)
class Solution {
public:
int search(vector<int>& nums, int target) {
int first=0;
int last=nums.size();
while(first!=last)
{
const int mid=first+(last-first)/2;
if(nums[mid]==target)
{
return mid;
}
if(nums[first]<nums[mid])
{
if(nums[first]<=target && target<nums[mid])
{
last=mid;
}
else
first=mid+1;
}
else
{
if(nums[mid]<target && target<=nums[last-1])
{
first=mid+1;
}
else
last=mid;
}
}
return -1;
}
};
搜索旋转排序数组II(medium)
首先分析题目,和上一题乍一看似乎是一样的,想到用二分法来解决,但是由于数组中有重复的数字,就会出现例如 nums=[3,1,2,3,3,3,3],target=2,首次二分时无法判断区间 [0,3] 和区间 [4,6] 哪个是有序的。这种情况就得将左边界加1,右边界减1
class Solution {
public:
bool search(vector<int>& nums, int target) {
int size=nums.size();
if(size==0)
return false;
if(size==1)
{
return nums[0]==target;
}
int first=0;
int last=size-1;
while(first<=last)
{
int mid=(last+first)/2;
if(nums[mid]==target)
return true;
if(nums[first]==nums[mid]&&nums[mid]==nums[last])
{
++first;
--last;
}
else if(nums[first]<=nums[mid])
{
if(nums[first]<=target&&target<nums[mid])
{
last=mid-1;
}
else
first=mid+1;
}
else
{
if(nums[mid]<target&&target<=nums[last])
{
first=mid+1;
}
else
last=mid-1;
}
}
return false;
}
};
寻找两个正序数组的中位数(hard)
题目:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。要求时间复杂度为O(log(m+n))
分析题目:首先最简单的方法是先归并,然后找到中位数,但是这种方式的时间复杂度为O(m+n),看到log就想到二分查找
这个题目可以转化为找第k大的数
class Solution {
public:
int getKthElement(vector<int>& nums1,vector<int>& nums2, int k)
{
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int m=nums1.size();
int n=nums2.size();
int index1=0,index2=0;
while(true)
{
//边界情况
if(index1==m)
{
return nums2[index2+k-1];
}
if(index2==n)
{
return nums1[index1+k-1];
}
if(k==1)
{
return min(nums1[index1],nums2[index2]);
}
//正常情况
int newIndex1=min(index1+k/2-1,m-1);
int newIndex2=min(index2+k/2-1,n-1);
int pivot1=nums1[newIndex1];
int pivot2=nums2[newIndex2];
if(pivot1<=pivot2)
{
k-=newIndex1-index1+1;
index1=newIndex1+1;
}
else
{
k-=newIndex2-index2+1;
index2=newIndex2+1;
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int totalLength=nums1.size()+nums2.size();
if(totalLength%2==1)
{
return getKthElement(nums1,nums2,(totalLength+1)/2);
}
else
{
return (getKthElement(nums1,nums2,totalLength/2)+getKthElement(nums1,nums2,totalLength/2+1))/2.0;
}
}
};
最长连续序列(hard)
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
要求时间复杂度为O(n)
首先分析题目:如果允许O(nlogn)的复杂度,那么可以先排序。由于序列里的元素是无序的,又要求O(n),首先要想到使用哈希表。用哈希表记录每个元素是否使用,对每个元素,以该元素为中心,往左右扩张。对x先往左扩张,如果存在x-1,则跳过当前数,如果不存在x-1,则以这个数为起点,判断x+1,x+2…是否存在,并更新最大长度。
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> num_set;
for(const int& num : nums)
{
num_set.insert(num);
}
int longestStreak=0;
for(const int& num : num_set)
{
if(!num_set.count(num-1))
{
int currentnum=num;
int currentsteak=1;
while(num_set.count(currentnum+1))
{
currentnum += 1;
currentsteak += 1;
}
longestStreak = max(currentsteak,longestStreak);
}
}
return longestStreak;
}
};
两数之和(easy)
首先分析题目,如果暴力解的话,时间复杂度O(n^2),会超时,因此可以用一个哈希表,来记录数值和下标的对应关系,这种情况下时间复杂度为O(n)。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> Map;
for(int i=0;i<nums.size();i++)
{
Map[nums[i]]=i;
}
vector<int> res;
for(int j=0;j<nums.size();j++)
{
int k=target-nums[j];
if( (Map.count(k))&&(Map[k]!=j) )//这个地方要注意先判断它是否存在,并且要不是相同的数,比如2和2
{
res.push_back(j);
res.push_back(Map[k]);
break;
}
}
return res;
}
};
三数之和(medium)
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路:首先将其排个序,利用三指针来做。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++)
{
if(nums[i]>0)
{
return ans;
}
if(i>0 && nums[i]==nums[i-1])//对第一个指针进行判断,看它是否和它前面一个数相等,相等就跳过
{
continue;
}
int left=i+1;
int right= nums.size()-1;
while(right>left)
{
if(nums[i]+nums[left]+nums[right]>0)
{
right--;
}
else if(nums[i]+nums[left]+nums[right]<0)
{
left++;
}
else
{
ans.push_back(vector<int>{nums[i],nums[left],nums[right]});
while(right>left && nums[right]==nums[right-1])
right--;
while(right>left && nums[left]==nums[left+1])
left++;
right--;
left++;
}
}
}
return ans;
}
};
最接近的三数之和(medium)
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
首先分析题目:和上面三数之和这道题做法相似,只是要加个判断差值大小。
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int ans=1e7;
if(nums.size()<4)
return nums[0]+nums[1]+nums[2];
else
{
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++)
{
if(i>0 && nums[i]==nums[i-1])
continue;
int left=i+1;
int right=nums.size()-1;
while(left<right)
{
int sum=nums[left]+nums[right]+nums[i];
if(abs(sum-target)<abs(ans-target))
{
ans=sum;
}
if(sum==target)
{
return sum;
}
if(sum<target)
{
left++;
}
if(sum>target)
right--;
}
}
}
return ans;
}
};
下一个排列
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
分析题目:一种特殊情况就是给的排列是最大排列,此时输出应该是最小的排列,其余情况
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int n=nums.size();
for(int i=n-1;i>0;i--)
{
if(nums[i]<=nums[i-1] && i>1)
continue;
if(nums[i]<=nums[i-1] && i==1)
{
sort(nums.begin(),nums.end());
break;
}
if(nums[i-1]<nums[i])
{
for(int j=n-1;j>=i;j--)
{
if(nums[j]>nums[i-1])
{
int temp=nums[i-1];
nums[i-1]=nums[j];
nums[j]=temp;
sort(nums.begin()+i,nums.end());
break;
}
else
continue;
}
break;
}
}
}
};
有效数独(medium)
请你判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
题目分析:暴力解的话,可以迭代三次,分别判断行、列、块三个部分是否有效,迭代三次耗时久,并且元素被重复访问三次,由此想到减少迭代次数,只通过一次迭代,在迭代的过程中,判断当前数是否已经存在行列块三个数组中,例如数字7,则行列块中第7个元素是否都为0,为0表示还未出现,这种情况可以将其值变为1,为1表示出现了一次,其余情况表示已经出现过,返回false
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
vector<vector<int>> row (9,vector<int>(9,0));
vector<vector<int>> col (9,vector<int>(9,0));
vector<vector<int>> box (9,vector<int>(9,0));
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
if(board[i][j]=='.')
continue;
int val=board[i][j]-'1';
int box_index=(i/3)*3+j/3;
if(row[i][val]==0 && col[j][val]==0 && box[box_index][val]==0)
{
row[i][val]=1;
col[j][val]=1;
box[box_index][val]=1;
}
else
return false;
}
}
return true;
}
};
接雨水(hard)
首先分析题目:暴力的解法就是,对于每一个柱子,都判断一下它左边和右边柱子的高度,选出其中较小的高柱子,再减去自身的高度就是当前位置能装的雨水,只是这种方式时间复杂度为O(n^2)。
时间复杂度为O(n),空间复杂度为O(1)的解法为双指针。通过一次遍历即可。
想法是:接水的高度其实是由两边高柱子中较矮的那个柱子决定的,因此在从左往右遍历的过程中,接的水是由左边柱子决定的,在从右往左遍历的过程中,接的水是由右边柱子决定的,
class Solution {
public:
int trap(vector<int>& height) {
int left=0,right=height.size()-1;
int ans=0;
int left_max=0, right_max=0;
while(left<right)
{
if(height[left]<height[right])
{
if(height[left]>=left_max)
{
left_max=height[left];
}
else
{
ans+=left_max-height[left];
}
left++;
}
else
{
if(height[right]>=right_max)
{
right_max=height[right];
}
else
{
ans+=right_max-height[right];
}
right--;
}
}
return ans;
}
};
旋转图像(medium)
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
首先分析题目:
这种方法时间复杂度为O(n^2),空间复杂度为O(1)
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n=matrix.size();
//水平翻转
for(int i=0;i<n/2;i++)
{
for(int j=0;j<n;j++)
{
swap(matrix[i][j],matrix[n-i-1][j]);
}
}
//主对角线翻转
for(int i=0;i<n;i++)
{
for(int j=0;j<i;j++)
{
swap(matrix[i][j],matrix[j][i]);
}
}
}
};
加一(easy)
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
分析题目:
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
int n=digits.size();
for(int i=n-1;i>=0;i--)
{
digits[i]++;
digits[i]=digits[i]%10;
if(digits[i]!=0)//说明无需进位
return digits;
}
vector<int> res(n+1,0);//上面的情况还没有return说明是999->1000这种情况
res[0]=1;
return res;
}
};
爬楼梯(easy)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
首先分析题目:
设f(n)是爬n阶楼梯的不同方法数,为了爬到第n阶楼梯有两种选择:
从n-1阶爬一阶到n阶;
从n-2阶爬二阶到n阶;
即:f(n)=f(n-1)+f(n-2);这是一个斐波那契数列
时间复杂度O(n),空间复杂度O(1)
class Solution {
public:
int climbStairs(int n) {
int prev=0;
int cur=1;
for(int i=0;i<n;i++)
{
int tmp=cur;
cur=tmp+prev;
prev=tmp;
}
return cur;
}
};
格雷编码
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。即使有多个不同答案,你也只需要返回其中一种。
格雷编码序列必须以 0 开头。
首先分析题目:
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> ans;
int size=pow(2,n);
for(int i=0;i<size;i++)
{
ans.push_back(i^i>>1);
}
return ans;
}
};
矩阵置零(medium)
方法一:
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
vector<int> row(m), col(n);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (!matrix[i][j]) {
row[i] = col[j] = true;
}
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (row[i] || col[j]) {
matrix[i][j] = 0;
}
}
}
}
};
方法二
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m=matrix.size();
int n=matrix[0].size();
bool flag_col0=false, flag_row0=false;
for(int i=0;i<m;i++)
{
if(matrix[i][0]==0)
flag_row0=true;
}
for(int j=0;j<n;j++)
{
if(matrix[0][j]==0)
flag_col0=true;
}
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)
{
if(matrix[i][j]==0)
{
matrix[i][0]=0;
matrix[0][j]=0;
}
}
}
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)
{
if(matrix[i][0]==0 || matrix[0][j]==0)
{
matrix[i][j]=0;
}
}
}
if(flag_row0)
{
for(int i=0;i<m;i++)
matrix[i][0]=0;
}
if(flag_col0)
{
for(int j=0;j<n;j++)
matrix[0][j]=0;
}
}
};
加油站
分析题目:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int total=0;
int j=-1;
for(int i=0,sum=0;i<gas.size();i++)
{
sum+=gas[i]-cost[i];
total+=gas[i]-cost[i];
if(sum<0)
{
j=i;
sum=0;
}
}
return total>=0? j+1:-1;
}
};
分发糖果(hard)
分析题目:
class Solution {
public:
int candy(vector<int>& ratings) {
int n=ratings.size();
vector<int> left(n);
for(int i=0;i<n;i++)
{
if(i>0 && ratings[i]>ratings[i-1])
{
left[i]=left[i-1]+1;
}
else
left[i]=1;
}
int right=0,ret=0;
for(int i=n-1;i>=0;i--)
{
if(i<n-1 && ratings[i]>ratings[i+1])
{
right++;
}
else
right=1;
ret+=max(left[i],right);
}
return ret;
}
};
只出现一次的数字(easy)
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
分析题目:
class Solution {
public:
int singleNumber(vector<int>& nums) {
int x=0;
int n= nums.size();
for(int i =0;i<n;i++)
{
x^=nums[i];
}
return x;
}
};
只出现一次的数字II(medium)
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
题目分析:通过位运算的解法没有看懂,所以还是直接用两个都是O(n)的哈希表解法。键是数组中的元素值,值是出现的次数,找到出现次数为1的数,直接break。
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int,int> freq;
for(int i=0;i<nums.size();i++)
{
freq[nums[i]]+=1;
}
int ans=0;
for(int j=0;j<nums.size();j++)
{
if(freq[nums[j]]==1)
{
ans=nums[j];
break;
}
}
return ans;
}
};