返回

LeetCode 颜色分类(Top 100):让你的算法技能亮起来

见解分享

算法揭秘:用 Swift 征服 LeetCode 中的经典颜色分类问题

简介

算法是编程世界中不可或缺的基础,而 LeetCode 是算法学习的圣殿,汇聚了海量经典算法题。其中,颜色分类算法(Top 100)更是重中之重。今天,让我们跟随算法大咖顾毅的步伐,使用 Swift 语言,一步步破解这道难题,点亮你的算法技能!

问题背景

假设我们有一个包含红色、白色和蓝色元素的数组,我们的任务是将这些元素分类,使相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

算法详解

1. 单指针法

单指针法是一种简单高效的解决方案。我们使用一个指针 i 遍历数组,同时维护两个边界指针 redblue,分别指向数组中第一个红色元素和最后一个蓝色元素。

i 指向红色元素时,我们将该元素与 red 指向的元素交换,并向前移动 red 指向;当 i 指向蓝色元素时,我们将该元素与 blue 指向的元素交换,并向后移动 blue 指向;当 i 指向白色元素时,我们直接向前移动 i 指向。

代码示例:

func sortColors(_ nums: inout [Int]) {
    var i = 0, red = 0, blue = nums.count - 1
    while i <= blue {
        switch nums[i] {
        case 0:
            nums.swapAt(i, red)
            red += 1
            i += 1
        case 1:
            i += 1
        case 2:
            nums.swapAt(i, blue)
            blue -= 1
        default:
            break
        }
    }
}

2. 双指针法

双指针法是一种更优雅的解决方案。我们使用两个指针 leftright,分别指向数组中第一个蓝色元素和最后一个红色元素。

left 指向白色元素时,我们将该元素与 right 指向的元素交换,并向后移动 right 指向;当 right 指向红色元素时,我们将该元素与 left 指向的元素交换,并向前移动 left 指向;当 left 指向蓝色元素,right 指向红色元素时,我们直接向前移动 left 指向,并向后移动 right 指向。

代码示例:

func sortColors(_ nums: inout [Int]) {
    var left = 0, right = nums.count - 1
    for i in 0...right {
        switch nums[i] {
        case 0:
            nums.swapAt(i, left)
            left += 1
        case 2:
            nums.swapAt(i, right)
            right -= 1
            i -= 1 // 交换后 i 需回退一步,避免重复比较
        default:
            break
        }
    }
}

3. 三路快排法

三路快排法是一种更高级的解决方案。它将数组划分为三个区域:红色区域、白色区域和蓝色区域。我们使用两个指针 lowhigh,分别指向红色区域的右边界和蓝色区域的左边界。

我们从头遍历数组,当遇到红色元素时,将其与 low 指向的元素交换,并将 low 指向的元素向前移动;当遇到蓝色元素时,将其与 high 指向的元素交换,并将 high 指向的元素向后移动;当遇到白色元素时,我们直接向前移动指针。

代码示例:

func sortColors(_ nums: inout [Int]) {
    var low = 0, mid = 0, high = nums.count - 1
    while mid <= high {
        switch nums[mid] {
        case 0:
            nums.swapAt(low, mid)
            low += 1
            mid += 1
        case 1:
            mid += 1
        case 2:
            nums.swapAt(mid, high)
            high -= 1
        default:
            break
        }
    }
}

实战演练

我们用一个例子来演示颜色分类算法的实际应用:

var nums = [2, 0, 2, 1, 1, 0]
sortColors(&nums)
print(nums) // 输出:[0, 0, 1, 1, 2, 2]

总结

颜色分类算法是算法学习中不可或缺的一部分,掌握这一算法有助于你提升算法技能。我们介绍了三种不同的解决方案,每一种都有其优缺点,你可以根据实际情况选择最适合你的方法。

算法之路永无止境,愿你不断探索,不断提升,成为一名算法大咖!

常见问题解答

1. 为什么单指针法中,遇到白色元素时不交换?

因为白色元素已经处于正确的位置,不需要交换。

2. 双指针法中,i -= 1 的作用是什么?

因为在交换红色元素和白色元素后,i 指向了红色元素,需要回退一步才能继续比较。

3. 三路快排法中,为什么白色元素不需要交换?

因为白色区域位于红色区域和蓝色区域之间,遇到白色元素时,指针 lowhigh 已经正确地将红色元素和蓝色元素交换到了它们应该在的位置。

4. 哪种算法更适合大规模数据?

三路快排法在时间复杂度上优于单指针法和双指针法,因此更适合处理大规模数据。

5. 除了颜色分类,还有什么其他问题可以用这些算法解决?

这些算法还可以用于解决荷兰国旗问题(将元素分类为红色、白色和蓝色)、三向归并排序(将数组中的元素分为三个部分:小于某个值、等于某个值和大于某个值)等问题。