LeetCode刷题记录----75.颜色分类

2025/9/30

题目(Medium):

75.颜色分类


我的思路:

很显然,这里可以看成是要这个数组进行升序排序。所以我们可以用任意的排序算法来解决。这里我选择用快速排序:

public class Solution {
    public void SortColors(int[] nums) {
        Qsort(nums, 0, nums.Length-1);
    }

    private void Qsort(int[] nums, int left, int right){
        if(left >= right)
            return;

        int privotIndex = new Random().Next(left, right);
        int privot = nums[privotIndex];

        Swap(nums, privotIndex, right);
        int index = left;
        for(int i = left; i < right; i++){
            if(privot > nums[i]){
                Swap(nums, index, i);
                index++;
            }
        }
        Swap(nums, index, right);

        Qsort(nums, left, index-1);
        Qsort(nums, index+1, right);
    }

    private void Swap(int[] nums, int i, int j){
        if(i == j)  return;

        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

时间复杂度:O(nlogn)

空间复杂度:O(n)

不过很显然,这题只有0,1,2这三个元素要排序,直接这样上排序算法有点大炮打蚊子了。应该是有更巧妙的解法的


优化思路:

1.单指针两次扫描

很显然在结果中我们可以看出,0总是排在前面,1总是排在中间,2总是排在后面。我们可以先找到数组中所有的0,让他们排在前面;之后再找到数组中所有的1,让它们排在中间。至于2,当0,1排序完了之后,就应该已经不用排了。

因此我们通过一个指针标记当前要放置0或者1的位置,来通过两次扫描(第一次扫描把0放在指针位置,指针前进;第二次扫描把1放在指针位置,指针前间)。期间每次扫描以指针的位置起始,初始的时候指针位置为0

具体代码如下:

public class Solution {
    public void SortColors(int[] nums) {
        int n = nums.Length;
        if(n == 1)  return;

        int index = 0;
        
        //第一次遍历把所有0放置在数组的头部
        for(int i = 0; i < n; i++){
            if(nums[i] == 0){
                Swap(nums, i, index);
                index++;
            }
        }

        //第二次遍历把所有的1放置在所有放置好0的尾部
        for(int i = index; i < n; i++){
            if(nums[i] == 1){
                Swap(nums, i, index);
                index++;
            }
        }
    }

    private void Swap(int[] nums, int left, int right)
    {
        if(left == right) return;

        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

时间复杂度:O(N)
空间复杂度:O(1)

2.双指针首部开始

还可以把扫描次数优化到一次。此时需要用双指针,p0指向0该放置的位置,p1指向1该放置的位置。

①当扫描到0的时候,放置到p0的位置,(如果p0 == p1,p0和p1都++(因为放了0就不可能放1了))

  • 不过这里注意:如果p0 < p1,那把0交换到p0位置的时候,肯定是把原本在p0位置的1交换了出去,所以说此时要再把这个1交换到p1的位置(此时p0++,p1也++)

②当扫描到1的时候,放置到p1的位置,p1++

具体代码如下:
 

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int n = nums.size();
        int p0 = 0, p1 = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 1) {
                swap(nums[i], nums[p1]);
                ++p1;
            } else if (nums[i] == 0) {
                swap(nums[i], nums[p0]);
                if (p0 < p1) {
                    swap(nums[i], nums[p1]);
                }
                ++p0;
                ++p1;
            }
        }
    }
};

时间复杂度:O(N)

空间复杂度:O(1)

【这里用的是LeetCode官方的题解,我写的好像没保存到,我服了。不过思路差不多的】

3.双指针首尾收缩

还可以通过在两段的指针,考虑把0放置在左边,2放置在右边位置来达到最终的排序结果。

也就是:设置首尾指针left = 0 , rigth = n-1

①如果遇到0了,交换到left的位置,left++

②如果遇到2了,交换到right的位置,right--

  • 不过注意,此时交换了之后,当前位置依然有可能是2,此时应该循环执行该交换,直到right索引到达当前位置了为止。
  • 如果交换了之后,当前位置是0,那么就按①处理
  • 如果交换了之后,当前位置是1,不用处理

具体代码如下:(这个版本的代码的思考逻辑比较线性)

public class Solution {
    public void SortColors(int[] nums) {
        int n = nums.Length;
        if(n == 1)  return;

        int left = 0, right = n-1;

        for(int i = 0; i <= right && left < right; i++){
            if(nums[i] == 0){
                //交换到头位置
                Swap(nums, i, left);
                left++;
            }
            else if(nums[i] == 2){
                //交换到尾部位置
                Swap(nums, i, right);
                right--;

                //如果此时位置还是2的话,那就继续交换
                while(nums[i] == 2 && i < right){
                    Swap(nums, i, right);
                    right--;
                }

                //如果此时位置是0了,那就按是0时候的交换规则
                if(nums[i] == 0){
                    Swap(nums, i, left);
                    left++;
                }
            }
        }

        
    }

    private void Swap(int[] nums, int left, int right)
    {
        if(left == right) return;

        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

时间复杂度:O(N)

空间复杂度:O(1)

下面还有一个合并了部分重复逻辑的代码0(思路一样的,只是写法表现上更精简一点了):

public class Solution {
    public void SortColors(int[] nums) {
        int n = nums.Length;
        if(n == 1)  return;

        int left = 0, right = n-1;

        for(int i = 0; i <= right && left < right; i++){
            while(nums[i] == 2 && i < right){
                Swap(nums, i, right);
                    right--;
            }

            if(nums[i] == 0){
                Swap(nums, i, left);
                left++;
            }
        }

        
    }

    private void Swap(int[] nums, int left, int right)
    {
        if(left == right) return;

        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

总结:

①对于这种固定只有几个数的排序(3个以内),我们完全可以因为提前直到它们的位置而对单个数字所在位置进行单独的排列。因为元素少,所以在对一两个元素排序完后,剩余的元素也自动排序好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萘柰奈

谢谢老板喵

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值