LintCode 题解丨谷歌高频题:两个排序数组的中位数排颜色 II

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: LintCode 题解丨谷歌高频题:两个排序数组的中位数排颜色 II

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

不能使用代码库中的排序函数来解决这个问题
​k​ <= ​n​
在线评测地址:LintCode 领扣

样例1
输入:
[3,2,2,1,4]
4
输出:
[1,2,2,3,4]
样例2
输入:
[2,1,1,2,2]
2
输出:
[1,1,2,2,2]
算法一:分治法

运使用rainbowSort,或者说是改动过的quickSort,运用的是分治的思想,不断将当前需要处理的序列分成两个更小的序列处理。

算法思路

思路与quickSort大致相同,每次选定一个中间的颜色,这个中间的颜色用给出的​k​来决定,将小于等于中间的颜色的就放到左边,大于中间颜色的就放到右边,然后分别再递归左右两半。
代码思路

递归函数设置四个参数,序列需要处理区间的左右端点和处理的颜色区间
根据给定的颜色区间决定中间的颜色
将小于等于中间的颜色的就放到左边,大于中间颜色的就放到右边
递归左右两半,直到颜色区间长度等于1
复杂度分析

N为序列长度,K为颜色数

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

  • 每次是对K分成左右进行递归,因此有logK层,每层递归遍历到整个序列,长度为N

public class Solution {

/*
 * @param colors: A list of integer
 * @param k: An integer
 * @return: nothing
 */

public void sortColors2(int[] colors, int k) {
    if (colors == null || colors.length < 2) {
        return;
    }
    sort(colors, 0, colors.length - 1, 1, k);
}

private void sort(int[] colors, int start, int end, int colorFrom, int colorTo) {
    //若处理区间长度为小于等于1或颜色区间长度为1,则不需要再进行处理
    if (start >= end || colorFrom == colorTo) {
        return;
    }
    //设置左右指针以及中间的颜色
    int left = start;
    int right = end;
    int colorMid = colorFrom + (colorTo - colorFrom) / 2;
    
    while (left <= right) {
        //找到左侧大于中间颜色的位置
        while (left <= right && colors[left] <= colorMid) {
            left++;
        }
        //找到右侧小于等于中间颜色的位置
        while (left <= right && colors[right] > colorMid) {
            right--;
        }
        //交换左右指针指向的颜色
        if (left <= right) {
            int temp = colors[left];
            colors[left] = colors[right];
            colors[right] = temp;
        }
    }
    //继续递归处理左右两半序列
    sort(colors, start, right, colorFrom, colorMid);
    sort(colors, left, end, colorMid + 1, colorTo);
}

}
算法二:计数排序(counting sort)

  • 题目要求不使用额外的数组,一种方法是使用彩虹排序(rainbow sort),但是这样虽然做到了没有使用额外的空间,但是代价是时间复杂度变成了O(N logK),那么是否有方法做到时间和空间的双赢呢?
  • 我们重新考虑计数排序(counting sort),这里我们需要注意到颜色肯定是1-k,那么​k​一定小于​n​,我们是否可以用​colors​自己本身这个数组作为​count​数组呢?
  • 下面我们介绍一种不占用大量额外空间的计数排序的写法。

算法思路

我们用负数代表数字出现的次数,例如​colors[i]=-cnt​表示数字​i​出现了​cnt​次
代码思路

我们从左往右遍历colors数组

  • 若​colors[i] > 0​且​colors[colors[i]] < 0​,那么​colors[colors[i]] -= 1​
  • 若​colors[i] > 0​且​colors[colors[i]] > 0​,那么先用临时变量​temp​存下​colors[i]​,将​colors[colors[i]]​赋值给​colors[i]​,再将​colors[temp] = -1​

注意此时​i​指针并不需要指向下一个位置,因为交换过来的值还未进行计数

  • 若​colors[i] < 0​,跳过

倒着输出每种颜色
另外注意数组下标是从0开始,为了避免​n==k​导致数组越界的情况,本题中​colors[i]​对应的计数位为​colors[colors[i] - 1]​
复杂度分析

NN表示​colors​数组长度

空间复杂度:O(1)
时间复杂度:O(N)
public class Solution {

/**
 * @param colors: A list of integer
 * @param k: An integer
 * @return: nothing
 */

public void sortColors2(int[] colors, int k) {
    int len = colors.length;
    if(len <= 0) {
        return;
    }

    int index = 0;
    while(index < len) {
        int temp = colors[index] - 1;
        //遇到计数位,跳过
        if(colors[index] <= 0){  
            index++;  
        }
        else {
            //已经作为计数位
            if(colors[temp] <= 0) {
                colors[temp]--;
                colors[index] = 0;
                index++;
            }

            //还未被作为计数位使用
            else {
                swap(colors[index], colors[temp]);
                colors[temp] = -1;
            }
        }
    }

    //倒着输出
    int i = len - 1;  
    while(k > 0) {
        for(int j = 0; j>colors[k-1]; j--) {
            colors[i--] = k;
        }
        k--;
    }
}

public void swap(int[] colors , int a , int b){
    int temp = colors[a];
    colors[a] = colors[b];
    colors[b] = temp;
    return ;
}

}
更多题解参考:

九章算法 - 帮助更多中国人找到好工作,硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧

相关文章
|
2月前
|
人工智能 运维 前端开发
生产环境 Agent 总"抽风"?这套"人工 SOP + 大模型"混合架构让我省了 80% 的调试时间
本文剖析Agent生产落地痛点,提出“三明治”架构:感知层用Qwen3.6识别意图,决策层以规则引擎(人工SOP)为主、LLM动态兜底,执行层通过Function Calling调用本地服务。强调可控性优于自主性,兼顾可解释、可回滚与审计合规。(239字)
|
7月前
|
数据采集 运维 监控
阿里云可观测 2025 年 11 月产品动态
阿里云可观测 2025 年 11 月产品动态。
235 57
|
SQL 分布式计算 DataWorks
破界·融合·进化:解码DataWorks与Hologres的湖仓一体实践
基于阿里云DataWorks与实时数仓Hologres,提供统一的大数据开发治理平台与全链路实时分析能力。DataWorks支持多行业数据集成与管理,Hologres实现海量数据的实时写入与高性能查询分析,二者深度融合,助力企业构建高效、实时的数据驱动决策体系,加速数字化升级。
|
7月前
|
人工智能
当“好故事”必须变成“好画面”,集之互动用AI创意视频服务让品牌创意更容易被看见
集之互动推出AI创意视频服务,以大模型赋能创意生成,结合专业导演与后期团队,打通从灵感到成片的全链路。助力品牌突破预算、周期与制作瓶颈,将碎片化创意转化为可复制、可延展的视听内容生态,让好故事真正“看得见、走得远、活得久”。
221 0
|
8月前
|
数据采集 存储 NoSQL
用Redis实现爬虫URL去重与队列管理:从原理到实战的极简指南
本文详解Redis在爬虫中的核心应用:利用SET与BloomFilter实现高效URL去重,结合LIST、BRPOP与ZSET构建高性能任务队列,并支持分布式协作。通过代码示例与实战优化技巧,助你打造亿级规模、高并发的智能爬虫系统,显著提升抓取效率与稳定性。
465 0
|
机器学习/深度学习 人工智能 自然语言处理
《AI自动化测试:网络开发的效率革命》
在网络开发中,AI驱动的自动化测试正成为提升效率和质量的关键。通过自动生成测试脚本、自适应执行、基于图像的视觉测试、缺陷预测与风险建模、智能故障排除等方法,AI可优化测试流程。例如,邮储银行的金牛座系统利用大模型生成单接口脚本;Applitools实现像素级视觉测试;机器学习算法预测缺陷区域,提前防范风险。这些技术显著提高了测试的全面性和准确性,加速问题发现与解决,提升了软件质量。
655 15
|
SQL 关系型数据库 MySQL
Mysql-常用函数及其用法总结
以上列举了MySQL中一些常用的函数及其用法。这些函数在日常的数据库操作中非常实用,能够简化数据查询和处理过程,提高开发效率。掌握这些函数的使用方法,可以更高效地处理和分析数据。
467 19
|
Java
用java实现Client和Server之间的互相通信
本文介绍了如何使用Java实现客户端和服务器之间的通信,包括服务器端创建ServerSocket、接受客户端连接、读取和发送消息,以及客户端创建Socket连接、发送和接收消息的完整过程。
658 1
用java实现Client和Server之间的互相通信
|
存储 监控 自动驾驶
云端问道15期方案教学-基于数据生命周期管理的存储成本优化
基于数据生命周期管理的存储成本优化,由王太平分享。内容涵盖对象存储的产品技术背景、介绍、核心功能及商业应用情况。重点讲解了如何通过生命周期管理,根据数据热度和访问时间,将数据智能迁移至不同存储层级(标准、低频、归档、冷归档、深度冷归档),从而实现成本最优。同时,针对不同类型的数据访问需求,提供了具体的策略建议,帮助企业在不影响业务的前提下大幅降低存储成本。
520 0
|
数据采集 自然语言处理 搜索推荐
python 【包含数据预处理】基于词频生成词云图
这段文本是关于如何使用Python基于词频生成词云图的教程。内容包括:1) 中文分词的必要性,因中文无明显单词边界及语言单位特性;2) 文本预处理步骤,如移除特殊符号、网址、日期等;3) 使用`data_process`函数清除无用字符;4) `getText`函数读取并处理文本为句子数组;5) 使用jieba分词库进行分词和词频统计;6) 示例代码展示了从分词到生成词云的完整流程,最后展示生成的词云图。整个过程旨在从中文文本中提取关键词并可视化。
1190 5
python 【包含数据预处理】基于词频生成词云图