原题地址

0601.png

这题是典型的荷兰国旗问题,最初由 Edsger W. Dijkstra提出。

三路快排 :与双路快排的不同是,它每次挑选出枢纽元素后,不是将数组分为小于等于枢纽元素,大于枢纽元素两部分,而是分成小于枢纽元素,等于枢纽元素,大于枢纽元素三部分,然后再对大于枢纽元素和小于枢纽元素这两个子序列进行上述操作。
三路快排代码

这里借助三路快排的思路,一次遍历就可以将荷兰国旗问题解决。

思路

我们定义三个区间[0, p0), [p0, p2], (p2, n-1],表示排序后0,1,2的三个区间。

所以p0表示0的右边界,p2表示2的左边界,我们再定义cur表示当前元素

遍历步骤如下:

  1. nums[cur]==0,则swap(nums[cur],nums[p0]),同时cur++;p0++
  2. nums[cur]==1,则直接cur++
  3. nums[cur]==2,则swap(nums[cur],nums[p2]),同时只有p2--

很好理解,就是当前元素是0,就把它放到左边0的区间中,同时0的区间右端点更新;当前元素是1,直接省略掉;当前元素时2,就把它放到右边的2的区间中,同时区间左端点更新。

可是,为什么当前元素是0,cur和p0都要更新;但当前元素是2,则只有p2更新呢?

这个是由于当前元素是0时,不管是nums[cur], 还是nums[p0],都检查过了;但是当前元素是2时,nums[p2]是还没有进行检查的元素,如果交换后cur++,就把该元素忽略掉了。

不理解的话自己可以手推一个例子看一下

Python代码

  1. def sortColors(self, nums: List[int]) -> None:
  2. """
  3. Do not return anything, modify nums in-place instead.
  4. """
  5. p0, p2, cur = 0, len(nums)-1, 0
  6. while cur <= p2:
  7. if nums[cur] == 0:
  8. nums[cur], nums[p0] = nums[p0], nums[cur]
  9. p0 += 1
  10. cur += 1
  11. elif nums[cur] == 2:
  12. nums[cur], nums[p2] = nums[p2], nums[cur]
  13. p2 -= 1
  14. else:
  15. cur += 1

这个题用常规快排也可以解决,只不过需要遍历两次,不过时间复杂度也是O(n)

  1. def sortColors(self, nums: List[int]) -> None:
  2. """
  3. Do not return anything, modify nums in-place instead.
  4. """
  5. def Partition(low, high, pivlot):
  6. temp = nums[low]
  7. while low < high:
  8. while low < high and nums[high] > pivlot:
  9. high -= 1
  10. nums[low] = nums[high]
  11. while low < high and nums[low] <= pivlot:
  12. low += 1
  13. nums[high] = nums[low]
  14. nums[low] = temp
  15. # 使用快排的思路,两遍即可
  16. # 第一遍,排好0,第二遍,排好1,时间复杂度为O(2*n)=O(n)
  17. # 第一遍,排好0
  18. l, h = 0, len(nums)-1
  19. while l <= h and nums[l] == 0:
  20. l += 1
  21. if l < h:
  22. Partition(l, h, 0)
  23. # 第二遍,排好1
  24. l = 0
  25. while l <= h and nums[l] <= 1:
  26. l += 1
  27. if l < h:
  28. Partition(l, h, 1)

时间复杂度O(n)

空间复杂度O(1)