题目来源:LeetCode
数据结构和算法新手,目前在按教材练习,每日代码题,日常打卡,会记录一些自己做题的思路,以及心得。
才疏学浅,如有问题感谢指正。
语言:C语言
日期排序
1、数组类算法
1.1【283】移动零
题目:
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
提示:
- 1 <= nums.length <= 104
- -231 <= nums[i] <= 231 - 1
1.1.1 我的思路
- 通过遍历查找第一个值为 0 的元素,之后两两交换,这样可以在保证顺序的前提下将 0 移动到数组末尾;
- 如果 0 的数量过多,就需要多次判断 0,但是每次循环的条件不是规律变化,所以再在外层套一层 while 循环;
- 同样,0 的数量一多,还需要防止下次循环的起始位置不断被后移,从而丢失当前趟的第二个 0 的位置。
注意点:
要注意数组防止越界!
#define END -1
void moveZeroes(int* nums, int numsSize) {
int start_pos = 0, swap_flag = 0;
while(swap_flag != END) {
for(int i = start_pos; i < numsSize - 1; i++) {
if(nums[i] == 0 && swap_flag == 0) {
swap_flag = 1;
}
if(swap_flag) {
if(nums[i + 1] == 0 && swap_flag < 2) {
swap_flag++;
start_pos = i;
} else {
int temp = nums[i + 1];
nums[i + 1] = nums[i];
nums[i] = temp;
}
}
}
numsSize--;
if(start_pos == numsSize)
break;
else
swap_flag = 0;
}
}
- 结果:
1.1.2 leetcode解题思路 —— 双指针
思路:(左右指针)
- 左指针 —— 指向非零元素
右指针 —— 指向数组,遍历整个数组 - 两个指针均从头开始交换两个元素,遇到 0 不交换。
- 左指针(移动后)确定 0 的位置,右指针找到非零元素后可以把非 0 元素交换出来,这样交换可以不断地把 0 聚集起来。
void swap(int *a, int *b) {
int t = *a;
*a = *b, *b = t;
}
void moveZeroes(int *nums, int numsSize) {
int left = 0, right = 0;
while (right < numsSize) {
if (nums[right]) {
swap(nums + left, nums + right);
left++;
}
right++;
}
}
- 结果:
1.2【27】移除元素
题目:
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
1.2.1 我的思路
- 本题目和上一题的情况一样,只不过是把 0 换成了指定的值。
- 至于移除元素后的数组长度,每趟处理完后,左指针始终指向之前数组中符合条件的子数组末尾。
int removeElement(int* nums, int numsSize, int val) {
int left = 0, right = 0;
while(right < numsSize) {
if(nums[right] != val) {
int temp = nums[right];
nums[right] = nums[left];
nums[left] = temp;
left++;
}
right++;
}
return left;
}
- 结果:
1.3【26】删除排序数组中的重复项
题目:
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
- 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
- 返回 k 。
1.3.1 个人思路
- 该题目和前面两道题比较像,考虑采用双指针。
- 由于数组有序,所以相同的元素会挨在一起排列。但是也导致循环的判断还是得有修改(起始条件 或 终止条件)。
- 因为只考虑前面的数据,所以可以直接赋值。
注意:慢指针 从 0 开始,又是先变化再赋值,所以返回的时候需要 +1。
int removeDuplicates(int* nums, int numsSize) {
int slow = 0, fast = 1;
for(fast = 1; fast < numsSize; fast++) {
if(nums[fast - 1] != nums[fast]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}
- 结果:
1.3.2 扩展思路
- 结合前面的双指针,考虑可以采用 基址 + 偏移 的方式访问之前的值。
int removeDuplicates(int* nums, int numsSize) {
int i = 0, j = 0;
for(i = 1; i < numsSize; i++) {
if(nums[i - 1] != nums[i]) {
nums[i - j] = nums[i];
} else {
j++;
}
}
return i - j;
}
- 结果
1.4 【80】删除有序数组中的重复项 II
题目:
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
1.4.1 个人思路
- 题目大体上和 26 题很像,继续考虑快慢指针。
- 由于题目要求每个值最多保留两个,所以需要对 慢指针 做计数。
- 慢指针 的移动如果和之前一样跟着 快指针 一起移动对于当前题目,操作比较麻烦,所以考虑将相同值的元素作为一个 “区域”,等到到第二个 “区域” 时再移动 慢指针。
int removeDuplicates(int* nums, int numsSize) {
int fast = 0, slow = 0;
int count = 0;
for(fast = 1; fast < numsSize; fast++) {
if(nums[slow] != nums[fast]) {
if(count < 2) {
slow += count + 1;
} else {
slow += 2;
}
nums[slow] = nums[fast];
count = 0;
} else {
if(count < 2) {
nums[slow + count + 1] = nums[fast];
}
count++;
}
}
if(count >= 2)
count = 1;
return slow + count + 1;
}
- 结果:
1.4.2 leetcode解题思路
- 还是采用双指针,但是双指针要从 2 开始,比较从 0 开始,拷贝时从 2 开始。
- 然后参考之前的代码,水平移动即可……
int removeDuplicates(int* nums, int numsSize) {
if (numsSize <= 2) {
return numsSize;
}
int slow = 2, fast = 2;
while (fast < numsSize) {
if (nums[slow - 2] != nums[fast]) {
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
}
- 结果