【数据结构与算法】每日练习

题目来源: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;
    }
}
  • 结果:
    283个人思路结果

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++;
    }
}
  • 结果:
    283官方思路结果

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;
}
  • 结果:
    27移除元素

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;
}
  • 结果:
    26个人思路

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;
}
  • 结果
    26扩展思路

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;
}
  • 结果:
    80个人思路

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;
}
  • 结果
    80leetcode解题思路

1.5 【75】颜色分类

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值