82 颜色分类II-彩虹排序(Sort Colors II)

1 题目

题目:颜色分类II(Sort Colors II)
描述:给定一个有n个对象(包括k种不同的颜色,并按照1到k进行编号)的数组,将对象进行分类使相同颜色的对象相邻,并按照1,2,…k的顺序进行排序。

  1. 不能使用代码库中的排序函数来解决这个问题
  2. k <= n

lintcode题号——143,难度——medium

样例1:

输入: 
[3,2,2,1,4] 
4
输出: [1,2,2,3,4]

样例2:

输入: 
[2,1,1,2,2] 
2
输出: [1,1,2,2,2]

2 解决方案

2.1 思路

  将两组元素进行排序需要用双指针,将三组元素进行排序需要用三指针,该题需要排序的颜色种类不确定,无法直接套用相应的指针算法,一种笨办法是先排序出[颜色1,(颜色2,颜色3,...)],再排序出[颜色1,颜色2,(颜色3,...)],这样可以把该题当成每次都对两组元素进行排序的情况来做,时间复杂度O(n^k)。
  考虑进行优化,该题可以用一种更优的算法解决,参考快速排序的思想,将颜色进行分治,而不是一个个颜色去排,将区间按照颜色向下细分,每次用O(n)的耗时将规模为T(k)的问题变为2*T(k/2)的规模,将时间复杂度降为O(n * log k)。

其实该题可以直接通过快速排序解决,不过时间复杂度为O(n * log n)。彩虹排序可以看作对快速排序在特定情况下的优化,将时间复杂度降为O(n * log k),其中n为数组元素个数,k为颜色种类数量。

2.2 图解

假设颜色顺序为(赤,橙,黄,绿,青,蓝,紫):

小于等于绿色的
大于绿色的
小于等于橙色的
大于橙色的
小于等于蓝色的
大于蓝色的
小于等于赤色的
大于赤色的
小于等于黄色的
大于黄色的
小于等于青色的
大于青色的
赤,黄,青,蓝,绿,蓝,橙,黄,黄,紫,赤,紫
赤,黄,橙,黄,黄,绿,赤
青,蓝,紫,蓝,紫
赤,橙,赤
黄,黄,黄,绿
青,蓝,蓝
紫,紫
赤,赤
黄,黄,黄
绿
蓝,蓝
紫,紫
赤,赤,橙,黄,黄,黄,绿,青,蓝,蓝,紫,紫

2.3 时间复杂度

  每次使用O(n)的耗时将规模为T(k)的问题变为2*T(k/2)的规模,时间复杂度的计算如下:

T(k) = 2 * T(k/2) + O(n)............................(n=k)
     = 2 * (2*T(k/4) + O(k/2)) + O(n).............(n=k/2)
     = 4 * T(k/4) + 2*O(k/2) + O(n)
     = 4 * (2*T(k/8) + O(k/4)) + 2*O(k/2) + O(n)..(n=k/4)
     = 8 * T(k/8) + 4*O(k/4) + 2*O(k/2) + O(n)
     = k * T(k/k) + O(k) + O(k) + …… + O(n)
     = k*T(1) + (logk - 1)*O(k) + O(n)
	   = O(k) + (logk - 1)*O(k) + O(n)
     = O(nlogk) + O(n)
	   = O(nlogk)

  所以总的时间复杂度为O(n * log k)。

2.4 空间复杂度

  空间复杂度为O(1)。

3 源码

细节:

  1. 彩虹排序算法,递归的定义需要包含索引的起止位置(start,end)以及颜色的起止位置(colorStart,colorEnd)。
  2. 将区间按照颜色向下细分,每次用O(n)的耗时将规模为T(k)的问题变为2*T(k/2)的规模,时间复杂度O(n * log k)。(n为数组长度,k为颜色种类)
  3. 索引范围的操作类似快速排序的left和right。
  4. 排序范围的操作类似合并排序的区间(left,mid)和(mid+1,right)。

C++版本:

/**
* @param colors: A list of integer
* @param k: An integer
* @return: nothing
*/
void sortColors2(vector<int> &colors, int k) {
    // write your code here
    if (colors.empty())
    {
        return;
    }

    rainbowSort(colors, 0, colors.size() - 1, 1, k);
}

// 对数组中从start到end的位置进行排序,排序区间内的颜色分布从colorStart到colorEnd
void rainbowSort(vector<int> & colors, int start, int end, int colorStart, int colorEnd)
{
    if (colorStart == colorEnd) // 只有一种颜色,不需要继续排序
    {
        return;
    }

    if (start == end) // 只有一个元素,不需要继续排序
    {
        return;
    }

    int left = start;
    int right = end;
    int colorMid = colorStart + (colorEnd - colorStart) / 2;
    while (left <= right)
    {
        if (colors.at(left) <= colorMid) // 按照颜色划分左右区间
        {
            left++;
            continue;
        }
        if (colors.at(right) > colorMid) // 按照颜色划分左右区间
        {
            right--;
            continue;
        }

        swap(colors.at(left++), colors.at(right--));
    }

    rainbowSort(colors, start, right, colorStart, colorMid);
    rainbowSort(colors, left, end, colorMid + 1, colorEnd);
}

猜你喜欢

转载自blog.csdn.net/SeeDoubleU/article/details/124642346
今日推荐