1.Leetcode283.移动零
2.Leetcode1089. 复写零
3.Leetcode202. 快乐数
4.Leetcode11. 盛最多水的容器
5.Leetcode611. 有效三角形的个数
6.Leetcode LCR 179. 查找总价格为目标值的两个商品 (原:剑指 offer:和为 s 的两个数)
7.Leetcode15. 三数之和
8.Leetcode18. 四数之和
1.Leetcode283.移动零

cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//利用cur遍历数组,同时利用dest用于将非0元素传到前面
for (int cur = 0, dest = -1; cur < nums.size(); cur++) {
//当nums[cur]不等于0时开始交换
if (nums[cur]) {
swap(nums[++dest], nums[cur]);
}
}
}
};
解析
利用cur遍历数组,同时将数组划分为3段,一段是0到dest,一段是dest+1到cur-1,一段是cur到n-1。最终第一段为非0元素,最后一段为0元素
cur作用 ∶从左往右扫描数组,遍历数组
dest作用 ∶已处理的区间内,非零元素的最后一个位置
cur遇见零元素 就++,遇见非零元素就和dest+1交换,dest++ cur++
2.Leetcode1089. 复写零

cpp
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int n = arr.size();
int cur = 0, dest = -1;
//这一步是为了找到复写0之后的最后一个值。cur一次走一步,dest一次走两步
while (cur < n)
{
if (arr[cur])dest++;
else dest += 2;
if (dest >= n - 1)break;
cur++;
}
//处理边界情况(dest移动两次出数组)
if (dest == n)
{
arr[n - 1] = 0;
cur--; dest -= 2;
}
//开始从后往前复写
while (cur >= 0)
{
if (arr[cur])//遇到非0,转换dest--
{
arr[dest--] = arr[cur--];
}
else// 遇到0,转换两次dest--
{
arr[dest--] = 0;
arr[dest--] = 0;
cur--;
}
}
}
解析
复写零有3步
第一步,找到最后一个复写的数(复写完数组刚好满)
利用cur=0,dest=-1。当cur的值不等于0时,dest++。等于0时,dest+=2。直到dest到达数组最后一个值。这时cur的位置就是最后一个复写的数。
**!!!**dest每移动一步或两步,cur++
第二步,判断特殊情况(dest越界)
这时我们知道dest越界只有移动两步才可能发生越界,也就是说cur位置的值一定为0,这时我们只需将cur和dest从后往前移动到上一次的位置,也就是cur--,dest-=2,这时候cur对应的值就是最后一个复写的值
第三步,从后往前复写数据
当cur位置的值不等于0时,把cur的值放进dest里,在--dest。当cur位置的值为0时,dest和dest-1的位置都变成0。
**!!!**dest位置的值发生变化时记得把cur--
3.Leetcode202. 快乐数

cpp
class Solution {
public:
//取平方和
int mysub(int x)
{
int num = 0;
while (x)
{
int n = x % 10;
num += n * n;
x /= 10;
}
return num;
}
bool isHappy(int n) {
int slow = n, fast = mysub(n);
while (slow != fast)//slow走一步,fast走两步,一直到其平方和为1(类似于单链表判断有环,这里是判断结果为1)
{
slow = mysub(slow);
fast = mysub(mysub(fast));
}
return slow == 1;
}
};
解析
首先我们先要写一个函数用于计算正整数每一位的平方和。
其次我们要懂得一个思路 ,无论最终平方和是否为1,我们最终平方和中都会有循环,类似单链表判断有环。只不过一种环全是1 ,一种也是几个数值都循环 。
有了这个思路我们就可以利用单链表判断有环的思路,使slow指针一次走一步,fast一次走两步,只要fast==slow就跳出循环,然后判断这时候的值是否为1
4.Leetcode11. 盛最多水的容器

cpp
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0, 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;
}
};
解析
在完成这道题之前我们要知道盛最多的水的两个柱子取决于最短的那个柱子和两个柱子之间的距离(类似长方形的长和宽)。
有了这个思路我们就可以开始做题了,我们利用两个指针代表两个柱子(数组最小和最大),这样我们就可以计算初始的盛水量,然后让柱子最短的那个向另一个柱子移动,再次计算盛水量。
**!!!**这里我们是利用一个变量作为初始盛水量,每计算一个盛水量都和它比较,比它小就放弃,比它大就交换
5.Leetcode611. 有效三角形的个数

cpp
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
int ret = 0, n = nums.size();
for (int i = n - 1; i >= 2; i--)
{
int left = 0, right = i - 1;
while (left < right)
{
if (nums[left] + nums[right] > nums[i])//是三角形right--继续判断
{
ret += right - left;
right--;
}
else//不是三角形left++继续判断
{
left++;
}
}
}
return ret;
}
};
解析
首先我们要先把数组进行排序 ,然后在固定最大的数 (最右边的数)和一个变量用于计算有效三角形的个数,然后利用双指针(一个是最大数左区间的最大值,一个是最小值),计算指针之和,当指针之和小于=最大的数 ,最小的指针就++ 。当指针之和大于最大的数 ,最大的指针就-- ,然后有效三角形个数++ 。
然后最大的数--,两个指针位置也重置,直至最大的数遍历到倒数第三个数就结束,返回有效三角形个数
6.Leetcode LCR 179. 查找总价格为目标值的两个商品 (原:剑指 offer:和为 s 的两个数)

cpp
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target) {
int left = 0, right = price.size() - 1;
while (left < right)
{
int num = price[left] + price[right];
if (num > target)
right--;
else if (num < target)
left++;
else return{ price[left],price[right] };
}
//照顾编译器(都有返回值)
return { -1,-1 };
}
};
解析
这里有个关键的条件就是有序且为升序 ,这样我们就可以利用双指针 对其值进行相加判断,当相加的值<固定的值 ,小的指针就++ ,相加的值>固定的值 ,大的指针就--。直至两个值相同,这样我们就返回这个值对应的两个指针的值。如果返回后小的指针任然<大的指针,就继续判断直至所有情况都找完
7.Leetcode15. 三数之和

cpp
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
sort(nums.begin(), nums.end());//排序
for (int i = 0; i < nums.size() - 1;)//放在去重i进行递增
{
if (nums[i] > 0) break;
int left = i + 1, right = nums.size() - 1;
while (left < right)
{
int sum = nums[left] + nums[right];
if (sum == -nums[i])
{
ret.push_back({ nums[i],nums[left],nums[right] });
left++;
right--;
//去重left right
while (left < right && nums[left] == nums[left - 1])
left++;
while (left < right && nums[right] == nums[right + 1])
right--;
}
else if (sum > -nums[i])
right--;
else
left++;
}
//去重i
i++;
while (i < nums.size() && nums[i] == nums[i - 1]) i++;
}
return ret;
}
};
解析
对于三数之和的思路跟上一个两数之和很像,不过这里我们需要自己进行固定值,其次这里我们依旧也要先排序再进行运算。
!!!只有当固定值<=0时,才能利用双指针
我们先固定数组第一个值 ,然后在利用双指针作为固定值右边区间的最大值和最小值,当两指针数之和=-固定值 ,就返回每个值 ,两指针数之和>-固定值 ,**大指针--,两指针数之和<-固定值,小指针++。**同时固定的值向后移动遍历,直至找到最后一个=-固定的值的值,然后返回所有情况。
不过这里麻烦的一点是我们的结果不能重复,如果两个指针后面有重复的值就要++,并要小指针一直<大指针(left<right)。对于我们固定的值我们也要去重,当它和后面的值相同时就++,并要求其不能越界访问数组(<数组数量)
!!!这里我们要利用定义一个vector来接收这些值,这样返回时只有返回一个vector就行了
8.Leetcode18. 四数之和

cpp
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ret;
sort(nums.begin(), nums.end());
int n = nums.size();
for (int i = 0; i < n;)
{
for (int j = i + 1; j < n;)
{
int left = j + 1, right = n - 1;
long long aim = (long long)target - nums[i] - nums[j];
while (left < right)
{
int sum = nums[left] + nums[right];
if (sum > aim)right--;
else if (sum < aim)left++;
else
{
ret.push_back({ nums[i],nums[j],nums[left],nums[right] });
left++;
right--;
//去重一
while (left < right && nums[left] == nums[left - 1])left++;
while (left < right && nums[right] == nums[right + 1])right--;
}
}
//去重二
j++;
while (j < nums.size() && nums[j] == nums[j - 1]) j++;
}
//去重三
i++;
while (i < nums.size() && nums[i] == nums[i - 1]) i++;
}
return ret;
}
};
解析
四数之和的原理和三数之和差不多,只不过这里需要两层for循环 ,同时也要排序 。也要利用一个vector收集所有情况
两层循环固定两个相邻的值,同时两层for循环都要遍历数组的值。再利用双指针计算两数之和==目标值-这两个相邻的值 。当两数之和<目标值-这两个相邻的值 ,小指针++ 。两数之和>目标值-这两个相邻的值,大指针--。
这里也要去重,因为我们固定的值有两个,去重也就有两个。同时两个指针的值也要去重。