【颜色分类--荷兰国旗问题】

news2025/1/10 16:18:01

问题

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,
原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

思路

荷兰国旗问题

荷兰国旗问题的解决方案,这个问题是由高德纳提出的,因此也被称为“高德纳排序”或“三向切分快速排序”。

这个问题的关键在于,我们有三个颜色的元素,而不是两个,
这就需要我们在遍历数组时,维护三个指针:
一个指向红色元素应该放置的位置(red),
一个指向当前遍历的位置(current),
以及一个指向蓝色元素应该放置的位置(blue)。

以下是解决这个问题的步骤:
初始化三个指针:
red 指向数组的开始,current 也指向数组的开始,blue 指向数组的末尾。

遍历数组,从 current 开始,直到 current 等于 blue。

如果 nums[current] 为 0(红色),则交换 nums[red] 和 nums[current],然后 red 和 current 都向前移动一位。

如果 nums[current] 为 1(白色),则 current 向前移动一位。

如果 nums[current] 为 2(蓝色),则交换 nums[blue] 和 nums[current],然后 blue 向后移动一位。
重复步骤 2,直到 current 等于 blue。

这种方法不需要额外的存储空间,因为我们在原地对数组进行排序,且时间复杂度为 O(n)。

算法

class Solution:
    def sortColors(self, nums: list[int]) -> None:
        red, current, blue = 0, 0, len(nums) - 1
        
        while current <= blue:
            if nums[current] == 0:
                nums[red], nums[current] = nums[current], nums[red]
                red += 1
                current += 1
            elif nums[current] == 1:
                current += 1
            else:  # 也就是nums[current] == 2
                nums[blue], nums[current] = nums[current], nums[blue]
                blue -= 1

# 示例
if __name__ == '__main__':
    nums = [2, 0, 2, 1, 1, 0]
    Solution().sortColors(nums)
    print(nums)  # 输出: [0, 0, 1, 1, 2, 2]

时间和空间复杂度

因为它只需要遍历数组一次。空间复杂度是 O(1),因为只使用了常量额外空间。
举例:

通过一个具体的例子 `[1, 2, 0]` 来解释荷兰国旗排序算法的应用过程,并通过图形来展示每一步的变化。

### 初始状态
数组为 `[1, 2, 0]`,我们有三个指针:`red` 指向 0,`current` 指向 0,`blue` 指向 2。


### 第一步
- `current` 指向 1(白色),不满足任何条件,`current` 移动到下一个位置。
- 状态变为 `[1, 2, 0]`,`current` 指向 2。


### 第二步
- `current` 指向 2(蓝色),触发 `else` 分支,交换 `nums[blue]` 和 `nums[current]`。
- 交换后,数组变为 `[1, 0, 2]`,`blue` 向左移动,`current` 不动。


### 第三步
- `current` 仍然指向 0(红色),触发 `if` 分支,交换 `nums[red]` 和 `nums[current]`。
- 交换后,数组变为 `[0, 1, 2]`,`red` 和 `current` 都向右移动。


### 结束条件
- `current` 已经越过 `blue`,循环结束。

最终,数组被排序为 `[0, 1, 2]`,红色、白色、蓝色元素按顺序排列。


通过这种方式,我们可以看到数组是如何通过维护三个指针来实现原地排序的。这种方法的时间复杂度是 O(n),因为它只需要遍历数组一次。空间复杂度是 O(1),因为只使用了常量额外空间。

 

疑惑

1、为什么是  while current <= blue:,而不是 while red <= blue?


回答:

在荷兰国旗排序问题中,使用 `while current <= blue:` 而不是 `while red <= blue:` 的原因是我们需要确保在遍历数组时,`current` 指针能够正确地处理所有三种颜色的元素(红色、白色和蓝色),并且能够正确地与 `red` 和 `blue` 指针交互。

这里是为什么使用 `while current <= blue:` 的原因:

1. **`current` 指针的作用**:`current` 指针用于遍历数组中的每个元素。它从数组的开始位置(索引 0)开始,一直移动到数组的末尾(索引 `len(nums) - 1`)。

2. **处理所有颜色**:`current` 指针需要检查数组中的每个元素,并根据元素的颜色(0、1 或 2)进行相应的操作。如果元素是红色(0),则与 `red` 指针指向的元素交换;如果元素是蓝色(2),则与 `blue` 指针指向的元素交换;如果元素是白色(1),则不需要交换,`current` 指针直接向前移动。

3. **维护 `red` 和 `blue` 指针**:`red` 指针用于跟踪下一个红色元素应该放置的位置,而 `blue` 指针用于跟踪下一个蓝色元素应该放置的位置。这两个指针分别从数组的两端向中间移动,确保红色和蓝色元素被正确地放置在数组的两端。

4. **避免重复检查**:如果使用 `while red <= blue:`,那么在某些情况下,`current` 指针可能会跳过一些元素,导致算法无法正确地处理所有元素。例如,如果 `current` 指针在 `red` 和 `blue` 之间,那么这些元素将不会被检查和排序。

5. **确保稳定性**:使用 `while current <= blue:` 可以确保算法在一次遍历中完成排序,这是原地排序算法的一个重要特性。它保证了算法的时间复杂度为 O(n),并且不需要额外的存储空间。

综上所述,使用 `while current <= blue:` 可以确保算法正确、高效地对数组进行排序,同时满足原地排序的要求。
2、为什么不对白色元素维护指针呢

回答:

在荷兰国旗问题中,确实不需要为白色元素维护一个专门的指针,原因如下:

1. **白色元素的自然排序**:在这个问题中,白色元素(用数字1表示)是介于红色元素(用数字0表示)和蓝色元素(用数字2表示)之间的。通过维护红色和蓝色元素的指针,我们可以确保所有红色元素都在白色元素的前面,所有蓝色元素都在白色元素的后面。这样,白色元素自然会被“挤压”到中间的位置。

2. **简化问题**:通过只关注红色和蓝色元素,我们可以将问题简化为两个部分:将红色元素移动到数组的前面,将蓝色元素移动到数组的后面。白色元素不需要额外的操作,它们会自动填充剩余的空间。

3. **减少复杂性**:如果为每种颜色都维护一个指针,算法的复杂性会增加,因为需要处理更多的指针移动和元素交换。在荷兰国旗问题中,通过只使用两个指针,我们可以保持算法的简洁性和高效性。

4. **算法效率**:在荷兰国旗问题中,我们的目标是尽量减少元素交换的次数,以提高算法的效率。由于白色元素不需要特定的排序操作,它们可以自然地填充在红色和蓝色元素之间,这有助于减少不必要的交换。

5. **空间复杂度**:题目要求使用常量额外空间,这意味着我们不能使用额外的数组或数据结构来存储元素。通过只使用两个指针,我们满足了这一要求。

综上所述,不对白色元素维护指针是因为在荷兰国旗问题中,白色元素可以自然地排序在红色和蓝色元素之间,这样做可以简化算法,提高效率,并且满足空间复杂度的要求。
3、 荷兰国旗问题中,如何确定红色和蓝色元素的边界?

回答:

在荷兰国旗问题中,确定红色和蓝色元素的边界是通过使用两个指针来实现的,这两个指针分别从数组的两端向中间移动。这里是一个详细的步骤说明:

1. **初始化指针**:
   - `red` 指针初始化在数组的起始位置,即 `red = 0`。
   - `blue` 指针初始化在数组的末尾位置,即 `blue = len(nums) - 1`。
   - `current` 指针用于遍历数组,初始化在数组的起始位置,即 `current = 0`。

2. **遍历数组**:
   - 从 `current` 指针开始,遍历数组直到 `current` 达到 `blue` 指针的位置。
   - 在遍历过程中,根据 `nums[current]` 的值来决定如何移动指针。

3. **处理红色元素**:
   - 如果 `nums[current]` 的值为 0(代表红色),则需要将 `current` 位置的元素与 `red` 位置的元素交换。
   - 交换后,`red` 指针向右移动一位(`red++`),`current` 指针也向右移动一位(`current++`)。

4. **处理白色元素**:
   - 如果 `nums[current]` 的值为 1(代表白色),不需要进行交换,只需将 `current` 指针向右移动一位(`current++`)。

5. **处理蓝色元素**:
   - 如果 `nums[current]` 的值为 2(代表蓝色),则需要将 `current` 位置的元素与 `blue` 位置的元素交换。
   - 交换后,`blue` 指针向左移动一位(`blue--`),但 `current` 指针不移动,因为我们需要重新检查交换后的元素。

6. **循环条件**:
   - 循环继续,直到 `current` 指针超过 `blue` 指针。这是因为在最坏的情况下,所有元素都可能是同一种颜色,导致 `current` 需要遍历整个数组。

7. **结束条件**:
   - 当 `current` 指针超过 `blue` 指针时,循环结束。此时,所有红色元素都在 `red` 指针的左边,所有蓝色元素都在 `blue` 指针的右边,中间的元素都是白色的。

通过这种方法,我们可以确保红色和蓝色元素的边界被正确地确定,而不需要额外的空间。这种算法的时间复杂度是 O(n),因为它只需要遍历数组一次。

学习

1、初始时,red 指向数组的开头,white 也指向数组的开头,blue 指向数组的结尾。
   应该写为:red, current, blue = 0, 0, len(nums) - 1 

 #这个意思是 nums[0] = nums[red]、nums[white], 指针指向的是索引,所以指针就是角标

2、结尾这个:
     else :
                nums[current], nums[blue] = nums[blue], nums[current]
                blue -= 1
                current += 1


不能再 current += 1

这样会换多了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2274429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习张量的秩、轴和形状

深度学习张量的秩、轴和形状 秩、轴和形状是在深度学习中我们最关心的张量属性。 秩轴形状 秩、轴和形状是在深度学习中开始使用张量时我们最关心的三个属性。这些概念相互建立&#xff0c;从秩开始&#xff0c;然后是轴&#xff0c;最后构建到形状&#xff0c;所以请注意这…

积分与签到设计

积分 在交互系统中&#xff0c;可以通过看视频、发评论、点赞、签到等操作获取积分&#xff0c;获取的积分又可以参与排行榜、兑换优惠券等&#xff0c;提高用户使用系统的积极性&#xff0c;实现引流。这些功能在很多项目中都很常见&#xff0c;关于功能的实现我的思路如下。 …

vue实现虚拟列表滚动

<template> <div class"cont"> //box 视图区域Y轴滚动 滚动的是box盒子 滚动条显示的也是因为box<div class"box">//itemBox。 一个空白的盒子 计算高度为所有数据的高度 固定每一条数据高度为50px<div class"itemBox" :st…

IEC61850遥控-增强安全选控是什么?

摘要&#xff1a;遥控服务是IEC61850协议中非常重要的一项服务&#xff0c;其通常会被应用在电源开关、指示灯、档位调节等器件的操作。 遥控是一类比较特殊的操作&#xff0c;其通过远程方式操作指定的设备器件&#xff0c;在一些重要的场景中需要有严谨的机制来进行约束&…

【Uniapp-Vue3】创建自定义页面模板

大多数情况下我们都使用的是默认模板&#xff0c;但是默认模板是Vue2格式的&#xff0c;如果我们想要定义一个Vue3模板的页面就需要自定义。 一、我们先复制下面的模板代码&#xff08;可根据自身需要进行修改&#xff09;&#xff1a; <template><view class"…

如何操作github,gitee,gitcode三个git平台建立镜像仓库机制,这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈

如何操作github&#xff0c;gitee&#xff0c;gitcode三个git平台建立镜像仓库机制&#xff0c;这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈 问题背景 由于我司最早期19年使用的是gitee&#xff0c;因此大部分仓库都在gitee有几百个库的代码&#xff0c;…

QThread多线程详解

本文结构如下 文章目录 本文结构如下 1.概述2.开始多线程之旅2.1应该把耗时代码放在哪里&#xff1f;2.2再谈moveToThread() 3.启动线程前的准备工作3.1开多少个线程比较合适&#xff1f;3.2设置栈大小 4.启动线程/退出线程4.1启动线程4.2优雅的退出线程 5.操作运行中的线程5.1…

深度学习数据集有没有规范或指导意见,数据集的建立都需要做哪些研究工作?

一、数据集的核心原则是什么&#xff1f; 数据集的目标&#xff1a;它需要回答“你要解决什么问题&#xff1f;” 在构建数据集之前&#xff0c;最重要的不是去采集数据&#xff0c;而是明确目标&#xff1a; 你的模型是要做图像分类&#xff0c;还是目标检测&#xff1f;是要…

前端for循环遍历——foreach、map使用

title: 前端不同类型的for循环遍历——foreach、map date: 2025-01-04 11:02:17 tags: javascript 前端不同类型的for循环遍历 场景&#xff1a;很多时候后端发来的数据是不能够完全契合前端的需求的&#xff0c;比如你要一个数据对象中的值&#xff0c;但是这个值却作为了ke…

MR30分布式 IO 在物流分拣线的卓越应用

在当今物流行业高速发展的时代&#xff0c;物流分拣线的高效与精准运作至关重要&#xff0c;而其中对于货物点数较多情况下的有效控制更是一大关键环节。明达技术MR30分布式 IO 系统凭借其独特的优势&#xff0c;在物流分拣线中大放异彩&#xff0c;为实现精准的点数控制提供了…

5 分布式ID

这里讲一个比较常用的分布式防重复的ID生成策略&#xff0c;雪花算法 一个用户体量比较大的分布式系统必然伴随着分表分库&#xff0c;分机房部署&#xff0c;单体的部署方式肯定是承载不了这么大的体量。 雪花算法的结构说明 如下图所示: 雪花算法组成 从上图我们可以看…

Android wifi常见问题及分析

参考 Android Network/WiFi 那些事儿 前言 本文将讨论几个有意思的网络问题&#xff0c;同时介绍 Android 上常见WiFi 问题的分析思路。 网络基础Q & A 一. 网络分层缘由 分层想必大家很熟悉&#xff0c;是否想过为何需要这样分层&#xff1f; 网上大多都是介绍每一层…

音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现

音视频入门基础&#xff1a;MPEG2-PS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-PS专题&#xff08;1&#xff09;——MPEG2-PS官方文档下载 音视频入门基础&#xff1a;MPEG2-PS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ps文件 音视频入门基础…

读书笔记:分布式系统原理介绍

写在前面 已经大概三个月左右没有更新博客了&#xff0c;哈哈哈哈&#xff1b; 此博客是笔者在对《分布式系统原理介绍》进行概述&#xff0c;对于整个分布式系统协议的理解基于一些量化的指标考虑了数据的分布副本协议&#xff08;中心化/去中心化&#xff09;进行了总结&…

Dexcap复现代码数据预处理全流程(四)——demo_clipping_3d.py

此脚本的主要功能是可视化点云数据文件&#xff08;.pcd 文件&#xff09;&#xff0c;并通过键盘交互选择演示数据的起始帧和结束帧&#xff0c;生成片段标记文件 (clip_marks.json) 主要流程包括&#xff1a; 用户指定数据目录&#xff1a;检查目录是否存在并处理标记文件 -…

MBM指尖六维力触觉传感器:高灵敏度、低漂移,精准掌控力学世界

MBM指尖六维力触觉传感器是一种专为机器人设计的高性能传感器。它通过集成三轴力和三轴力矩的感知能力&#xff0c;能够精准捕捉复杂的力学信息。传感器采用MEMS与应变体复合测量技术&#xff0c;具备数字输出功能&#xff0c;显著降低漂移并减少安装偏移的影响。其紧凑轻便的设…

C#,图论与图算法,任意一对节点之间最短距离的弗洛伊德·沃肖尔(Floyd Warshall)算法与源程序

一、弗洛伊德沃肖尔算法 Floyd-Warshall算法是图的最短路径算法。与Bellman-Ford算法或Dijkstra算法一样&#xff0c;它计算图中的最短路径。然而&#xff0c;Bellman Ford和Dijkstra都是单源最短路径算法。这意味着他们只计算来自单个源的最短路径。另一方面&#xff0c;Floy…

为答疑机器人扩展问题分类与路由功能

1.意图识别 2. 构建路由模块 简单的意图识别 from chatbot import llmfrom config.load_key import load_key load_key()prompt 【角色背景】 你是一个问题分类路由器&#xff0c;需要识别问题的类型。 --- 【任务要求】 问题的类型目前有&#xff1a;公司内部文档查询、内…

spring boot启动源码分析(三)之Environment准备

上一篇《spring-boot启动源码分析&#xff08;二&#xff09;之SpringApplicationRunListener》 环境介绍&#xff1a; spring boot版本&#xff1a;2.7.18 主要starter:spring-boot-starter-web 本篇开始讲启动过程中Environment环境准备&#xff0c;Environment是管理所有…

Pandas-RFM会员价值度模型

文章目录 一. 会员价值度模型介绍二. RFM计算与显示1. 背景2. 技术点3. 数据4. 代码① 导入模块② 读取数据③ 数据预处理Ⅰ. 数据清洗, 即: 删除缺失值, 去掉异常值.Ⅱ. 查看清洗后的数据Ⅲ. 把前四年的数据, 拼接到一起 ④ 计算RFM的原始值⑤ 确定RFM划分区间⑥ RFM计算过程⑦…