[算法系列之十一]荷兰国旗问题

【问题】

现有红白蓝三个不同颜色的小球,乱序排列在一起,请重新排列这些小球,使得红白蓝三色的同颜色的球在一起。这个问题之所以叫荷兰国旗问题,是因为我们可以将红白蓝三色小球想象成条状物,有序排列后正好组成荷兰国旗。

【分析】

这个问题我们可以将这个问题视为一个数组排序问题。红白蓝分别对应数字0、1、2。红、白、蓝三色小球数量并不一定相同。

【思路一】

First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's.

(1)遍历数组,统计红白蓝三色球(0,1,2)的个数

(2)根据红白蓝三色球(0,1,2)的个数重排数组

时间复杂度:O(n)

【代码一】

    /**------------------------------------
    *   日期:2015-02-02
    *   作者:SJF0115
    *   题目: 75.Sort Colors
    *   网址:https://oj.leetcode.com/problems/sort-colors/
    *   结果:AC
    *   来源:LeetCode
    *   博客:
    ---------------------------------------**/
   class Solution {
    public:
        void sortColors(int A[], int n) {
            if(n <= 1){
                return;
            }//if
            // 统计个数
            int red = 0,white = 0,blue = 0;
            for(int i = 0;i < n;++i){
                if(A[i] == 0){
                    ++red;
                }//if
                else if(A[i] == 1){
                    ++white;
                }//else
                else{
                    ++blue;
                }//else
            }//for
            // 重新布局
            for(int i = 0;i < n;++i){
                if(red > 0){
                    A[i] = 0;
                    --red;
                }//if
                else if(white > 0){
                    A[i] = 1;
                    --white;
                }//else
                else{
                    A[i] = 2;
                }
            }//for
        }
    };

【思路二】

我们可以把数组分成三部分,前部(全部是0),中部(全部是1)和后部(全部是2)三个部分,每一个元素(红白蓝分别对应0、1、2)必属于其中之一。

将前部和后部各排在数组的前边和后边,中部自然就排好了。

设置两个指针begin指向前部的末尾的下一个元素(刚开始默认前部无0,所以指向第一个位置),end指向后部开头的前一个位置(刚开始默认后部无2,所以指向最后一个位置),然后设置一个遍历指针current,从头开始进行遍历。

(1)若遍历到的位置为1,则说明它一定属于中部,根据总思路,中部的我们都不动,然后current向前移动一个位置。

(2)若遍历到的位置为0,则说明它一定属于前部,于是就和begin位置进行交换,然后current向前移动一个位置,begin也向前移动一个位置(表示前边的已经都排好了)。

(3)若遍历到的位置为2,则说明它一定属于后部,于是就和end位置进行交换,由于交换完毕后current指向的可能是属于前部的,若此时current前进则会导致该位置不能被交换到前部,所以此时current不前进。而同1),end向前移动一个位置。

【代码二】

    /**------------------------------------
    *   日期:2015-02-04
    *   作者:SJF0115
    *   题目: Sort Colors
    *   网址:https://oj.leetcode.com/problems/sort-colors/
    *   博客:
    ---------------------------------------**/
   class Solution {
    public:
        void sortColors(int A[], int n) {
            int begin = 0,end = n-1,cur = 0;
            while(cur <= end){
                if(A[cur] == 0){
                    swap(A[begin],A[cur]);
                    // 指向排序0末尾的下一个位置
                    ++begin;
                    // 向前遍历
                    ++cur;
                }//if
                else if(A[cur] == 1){
                    ++cur;
                }//else
                else{
                    swap(A[end],A[cur]);
                    // 指向排序2开头的前一个位置
                    --end;
                }//else
            }//for
        }
    };

【思路三】

用三个变量记录red,white,blue的下标位置。起始下标都为-1

如果A[i] == 0 ,插入red对white blue有影响,blue先整体向后移动一位,white再整体向后移动一位,如果不移动,前面插入的数据就会覆盖已有的。

如果A[i] == 1,插入white对blue有影响,blue整体向后移动一位。

A[i] == 2,直接插入blue

【代码三】

    /**------------------------------------
    *   日期:2015-02-03
    *   作者:SJF0115
    *   题目: 75.Sort Colors
    *   网址:https://oj.leetcode.com/problems/sort-colors/
    *   结果:AC
    *   来源:LeetCode
    *   博客:
    ---------------------------------------**/
    class Solution {
    public:
        void sortColors(int A[], int n) {
            if(n <= 1){
                return;
            }//if
            int red = -1,white = -1,blue = -1;
            for(int i = 0;i < n;++i){
                // 插入red对white blue有影响
                if(A[i] == 0){
                    // blue整体向后移动一位
                    A[++blue] = 2;
                    // white整体向后移动一位
                    A[++white] = 1;
                    // 插入red
                    A[++red] = 0;
                }//if
                // 插入white blue受到影响
                else if(A[i] == 1){
                    // blue整体向后移动一位
                    A[++blue] = 2;
                    // 插入white
                    A[++white] = 1;
                }//else
                // 插入blue对其他没有影响
                else{
                    // 插入blue
                    A[++blue] = 2;
                }//else
            }//for
        }
    };

引用:http://www.cnblogs.com/gnuhpc/archive/2012/12/21/2828166.html

时间: 2024-10-25 06:22:00

[算法系列之十一]荷兰国旗问题的相关文章

[算法系列之三十一]最近公共祖先(LCA)

[简介] 对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. 另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点. 例如,对于下面的树,结点4和结点6的最近公共祖先LCA(T,4,6)为结点2. 求树中两个结点的最低公共祖先是面试中经常出现的一个问题.一般的做法,可能是针对是否为二叉查找树分情况讨论. LCA问题的扩展主要在于结点是否只包含父结点指针,对于同一棵树是否进行多次LCA查询

算法系列(十一) 圆生成算法

在平面解析几何中,圆的方程可以描述为(x – x0)2 + (y – y0)2 = R2,其中(x0, y0)是圆心坐 标,R是圆的半径,特别的,当(x0, y0)就是坐标中心点时,圆方程可以简化为x2 + y2 = R2.在计算 机图形学中,圆和直线一样,也存在在点阵输出设备上显示或输出的问题,因此也需要一套光栅扫描 转换算法.为了简化,我们先考虑圆心在原点的圆的生成,对于中心不是原点的圆,可以通过坐标的 平移变换获得相应位置的圆. 在进行扫描转换之前,需要了解一个圆的特性,就是圆的八分对 成

缓存淘汰算法系列之3——FIFO类

缓存淘汰算法系列之3--FIFO类 1 FIFO 1.1. 原理 按照"先进先出(First In,First Out)"的原理淘汰数据. 1.2. 实现 FIFO队列,具体实现如下: 1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动: 2. 淘汰FIFO队列头部的数据: 1.3. 分析 l 命中率 命中率很低,因为命中率太低,实际应用中基本上不会采用. l 复杂度 简单 l 代价 实现代价很小. 2. Second Chance 2.1. 原理 FIFO算法的改进

算法系列(二十) 计算中国农历(二)

所谓的"天文算法",就是利用经典力学定律推导行星运转轨道,对任意时刻的行星位置进行精确计 算,从而获得某种天文现象发生时的时间,比如日月合朔这一天文现象就是太阳和月亮的地心黄经(视黄 经)差为0的那一瞬间.能够计算任意时刻行星位置的一套理论就被称为星历表,比较著名的星历表有美 国国家航空航天局下属的喷气推进实验室发布的DE系列星历表,还有瑞士天文台在DE406基础上拓展的瑞 士星历表等等.根据行星运行轨道直接计算行星位置通常不是很方便,更何况大多数民用天文计算用不上 那么多精确的轨道参

算法系列(十九) 用天文方法计算日月合朔(新月)

中国农历的朔望月是农历历法的基础,而朔望月又是严格以日月合朔发生的那一天作为月首,因此日 月合朔时间的计算是制定农历历法的关键.本文将介绍ELP-2000/82月球运行理论,以及如何用ELP- 2000/82月球运行理论计算日月合朔时间. 要计算日月合朔时间, 首先要对日月合朔这一天文现象进行数学定义.朔望月是在地球上观察到的月相周期,平均长度约等于 29.53059日,而恒星月(天文月)是月亮绕地球公转一周的时间,长度约27.32166日.月相周期长度比恒 星月长大约两天,这是因为在月球绕地球

算法系列(十四) 狼、羊、菜和农夫过河问题

题目描述:农夫需要把狼.羊.菜和自己运到河对岸去,只有农夫能够划船,而且船比较小,除农 夫之外每次只能运一种东西,还有一个棘手问题,就是如果没有农夫看着,羊会偷吃菜,狼会吃羊. 请考虑一种方法,让农夫能够安全地安排这些东西和他自己过河. 这个题目考察人的快速逻辑运算和短期记忆力.分析一下,在狼->羊->菜这个食物链条中 ,"羊"处在关键位置,解决问题的指导思想就是将"羊"与"狼"和"菜"始终处于隔离状态,也 就是说

数据结构与算法系列(1)时间测试

前言 好久都把数据结构和算法的东西忘完了,最近想重温下这些知识.因此就写了<<数据结构与 算法系列>的文章,希望能和大家共同学习,共同探讨这方面的知识.希望大家多多指异. 1.时间测试 由于本部分内容采用了一种实用的方法来分析数据结构与算法检测,所以在这里避开了使用大O分 析法,而采用运行简单基准测试的方法来代替.这种测试将会说明运行一段代码需要多少秒数(或者 无论什么时间单位). 基准法测试是使用时间测试的方法来衡量运行完整算法所花费的时间长度.如同科学一样,基准测 试也像是一门艺术,

[算法系列之二十四]后缀树(Suffix Tree)

之前有篇文章([算法系列之二十]字典树(Trie))我们详细的介绍了字典树.有了这些基础我们就能更好的理解后缀树了. 一 引言 模式匹配问题 给定一个文本text[0-n-1], 和一个模式串 pattern[0-m-1],写一个函数 search(char pattern[], char text[]), 打印出pattern在text中出现的所有位置(n > m). 这个问题已经有两个经典的算法:KMP算法 ,有限自动机,前者是对模式串pattern做预处理,后者是对待查证文本text做预处

[算法系列之十九]最长公共子序列

题目 最长公共子序列 分析 有两个字符串S1和S2,求一个最长公共子串,即求字符串S3,它们同时是S1和S2的子串,且要求它们的长度最长,并确定这个长度.这个问题我们称之为最长公共子序列问题. 与求最长递增子序列一样,我们首先将原问题分割成一些子问题,我们用dp[i][j]表示S1中前i个字符和S2中前j个字符分别组成的两个前缀字符串的最长公共子串长度.显然的,当i,j较小时我们可以直接给出答案,如dp[0][j] 必等于0.那么,假设我们已经求的dp[i][j](0 <= i < x,0 &