目录
1.移动0
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int cur = 0;
int des = 0;
int size = nums.size();
while(cur < size)
{
if(nums[cur] == 0)cur++;
else
{
swap(nums[cur],nums[des]);
cur++;
des++;
}
}
}
};
cur 用于遍历数组
des 中储存的下标指向 已经遍历过区域中 从左到右第一个0.
当nums[cur]为0时,cur++,des不变。
不为0时,先交换des和cur所指向的数组中的值,然后再让cur++,des++。
使得des储存的下标仍然指向 已经遍历过区域中 从左到右第一个0.
2.复写0
class Solution {
public:
void duplicateZeros(vector<int>& nums) {
int end = nums.size()-1;
int startpos;
int endpos;
for(endpos = -1,startpos = 0;endpos < end;)
{
if(nums[startpos] == 0)
{
startpos++;
endpos = endpos + 2;
}
else
{
startpos++;
endpos++;
}
}
startpos--;
if(endpos != end)
{
startpos--;
nums[--endpos] = 0;
--endpos;
}
{
while(startpos >=0)
{
if(nums[startpos] == 0)
{
nums[endpos] = nums[endpos-1] = nums[startpos];
endpos = endpos - 2;
startpos--;
}
else
{
nums[endpos] = nums[startpos];
endpos--;
startpos--;
}
}
}
}
};
因为从左往右赋值会出现覆盖,因此我们选择从右向左赋值。
先进行一次遍历得到从右往左开始遍历位置的下标startpos。
变量endpos与数组最后一位的下标end比较,用于判断是否填满了数组。
因为开始的时候startpos为0,endpos为-1,因此遍历结束时,startpos指向的是真正开始遍历的位置的下标+1.因此startpos--;
因为有可能出现最后一位的值为0,但是所剩余的空格只有1,endpos+2后会出现越界,因此我们要进行判断,如果越界那么我们先进行处理。
最后从右往左非0赋值一位,0赋值两位即可
3.快乐数
class Solution {
public:
int happy(int n)
{
int ret = 0;
while(n)
{
ret += (n%10) *(n%10);
n /= 10;
}
return ret;
}
bool isHappy(int n) {
int slow = happy(n);
int fast = happy(happy(n));
while(slow != fast)
{
slow = happy(slow);
fast = happy(happy(fast));
}
if (slow == 1)return true;
else return false;
}
};
原理类似判断链表是否有环,
但是这个是一定有环,第一种情况是交点处不为1,一种是交点处为1,
利用快慢指针寻找到交点处的值即可
4.盛水最多的容器
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0;
int right = height.size()-1;
int ret = 0;
while(left < right)
{
int v = min(height[left],height[right])*(right-left);
ret = max(ret, v);
if(height[left] < height[right])left++;
else right--;
}
return ret;
}
};
3 2 9 5 7
即选取两个位置,因为木桶效应,以较小的那个值为高,两者间的距离为宽。
情况有 3 7,2 7,9 7 , 5 7
3 5 ,2 5 ,9 5
3 9, 2 9
3 2
我们一开始选取最左边和最右边的板子,下标记为left和right
一开始是一个宽 w为5的木桶。高h为3.
我们现在要将木桶的宽缩小,如果我们去掉更高的那个板子7。
以3为一边的木桶,剩余的情况为 3 2 , 3 9 , 3 5
当另一边的板高大于等于3 时,h不变,w减小,总体体积减小
当另一边的板高小于3时,h减小,w减小,总体体积减小。
因此以3为一边板的情况下,3 7为最大情况
因此我们只有把短板3去掉,才有可能出现一个比3大的板子,使h增大,才可能在w减小的情况下使得总体积增大
因此我们每次都将两板中较短的板去掉,才有可能得到一个更大的体积。
将每次取得的体积进行比较,取出最大值即可
我们可以在图中看到,这个过程就是以一个值为起点,向后或者向下进行比较的结果,(最大的组合加粗)但是它只能保证这次比较的结果中它是最大,所以还需要和它之前的小组中最大值值进行比较
5.有效三角形的个数
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int size = nums.size();
if(size ==1 || size == 2)return 0;
int ret = 0;
for(int i = size - 1 ; i >=2; i--)
{
int left = 0;
int right = i - 1;
while(left < right)
{
if(nums[left]+nums[right] > nums[i])
{
ret += right - left;
right--;
}
else
{
left++;
}
}
}
return ret;
}
};
如下一个用例
4 6 5 4 3 2
如果我们使用暴力解法
for(int i = 0; i < size; i++)
for(int j = i + 1; j < size; j++)
for(int k = j+1; k < size ;k++)
判断函数(i, j,k)
判断函数: i + j > k i + k > j , j + k > i 判断三次
所以总的时间复杂度为3N^3
所以我们对它进行优化。
首先我们先进行排序,使得我们只需要比较较小的两个数相加是否大于最大数,只需要判断一次
同时我们再对算法进行优化
2 3 4 4 5 6
我们先取6为最大数,然后取left ->,right-> 5 为两个较小数
当left ->2 + right -> 5大于最大数 6时
因为我们是按照大小排序的left为左端最小值,因此left右的任意一个数 + right ->都大于最大数
2 + 5 > 6
3 + 5 > 6
4 + 5 > 6
4 + 5 > 6
因此我们增加的情况即 right - left
此时以5为一个较小数的情况被遍历,right左移
此时
left->2 right->4 ,等于最大数6
此时left 与 right 左端的任意一个值相加都小于等于 最大数
2+4 <= 6
2+4 <= 6
2+3 <= 6
此时以2 为一个较小数的情况被遍历,left右移
增加的情况为0.
当left == right 时结束循环。
以5为最大数开始遍历
此方法的时间复杂度仅为N^2
6.三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> ret;
int size = nums.size();
for(int i = 0; i < size - 2; )
{
int aim = -nums[i];
if(aim < 0)break;//一个小优化,当nums[i]>0,就找不到两数之和等于它的相反数了
int left = i + 1;
int right = size - 1;
while(left < right)
{
if(nums[left] + nums[right] > aim)right--;
else if (nums[left] + nums[right] < aim) left++;
else
{
ret.push_back({nums[i],nums[left],nums[right]});
left++;
right--;
while(left < right&& nums[left] == nums[left-1])
{
left++;
}
while(right> left && nums[right] == nums[right+1])
{
right--;
}
}
}
i++;
while(i < size - 2 && nums[i] == nums[i-1])i++;
}
return ret;
}
};
我们仍然是先进行排序
用例[-1,0,1,2,-1,-4]
-4 -1 -1 0 1 2
我们先固定-4,从左到右遍历到0处即可。
再定义left和right变量,来指向右侧数组最左侧和最右侧的数。
当nums[left] + nums[right] > aim 时,说明太大,right--
nums[left] + nums[right] < aim 时,说明太小,left--
相等时,将三个数存入二维数组即可。
同时我们要去重,如果left指向的数与上一个位置相同,那么left继续加
right同理,一直减
并且要注意范围,left < right ,不能越界
处理过一个i之后,i向右移动,也要去重,原理同上。
7.四数之和
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
int n=nums.size();
sort(nums.begin(),nums.end());
for(int first=0;first<n-3;first++){
if(first>0&&nums[first]==nums[first-1])continue;
if(first>=n-3)break;
for(int second=first+1;second<n-2;second++){
if(second>=n-2)break;
int fourth = n -1;
for(int third=second+1;third<n-1;third++){
if(third>=fourth)break;
double all=0.0+nums[first]+nums[second]+nums[third]+nums[fourth];
if(all==target){
bool test=true;
for(int i=0;i<ans.size();i++){
vector<int> test1={nums[first],nums[second],nums[third],nums[fourth]};
if(ans[i]==test1)test=false;
}
if(test)ans.push_back({nums[first],nums[second],nums[third],nums[fourth]});
}else if(all>target){
fourth--;
third--;
}else if(all<target){
continue;
}
}
}
}
return ans;
}
};
原理同上,只需要在三数之和的基础上,再加一层循环。
三数之和等于 target- 第一个固定的数
因此两数之和等于target- 第一个固定的数-第二个固定的数