题目背景描述

新年第一天,N 个人排队坐过山车。每个人穿有带编号的衣服 \([1, 2, 3, ...]\)。

因为排队时间太久,有人发现给前面相邻的人喂一颗糖,就可以和他交换位置,而每人手里只有两颗糖。所以这些人就通过“喂糖”的方式,让队伍变得乱七八糟。

作为游乐场管理员,为了纠正这种不正之风,你要使队伍恢复原状。首先,你的任务是:要确定需要最少交换多少次才能让队伍恢复原状。

要求

编写函数,计算最少交换次数;若所给数列不是由上述规则打乱的,则显示"Too chaos!"

输入:整形数组 q[n] -> 乱序队列

输出 :

  • 打印整数 candies -> 交换所需的糖果数量
  • "Too chaos!" -> 提示输入无效

样例

输入数列1:[3, 2, 5, 6, 7, 4, 1]

恢复过程如下:

  1. 7 后退两格:[3, 2, 5, 6, 4, 1, 7]
  2. 6 后退两格:[3, 2, 5, 4, 1, 6, 7]
  3. 5 后退两格:[3, 2, 4, 1, 5, 6, 7]
  4. 4 后退一格:[3, 2, 1, 4, 5, 6, 7]
  5. 3 后退两格:[2, 1, 3, 4, 5, 6, 7]
  6. 2 后退一格:[1, 2, 3, 4, 5, 6, 7]

输入样例2:[3, 2, 5, 7, 6, 4, 1]

所给数列不是由上述规则所打乱的,因为一个人只有两颗糖,7 不可能给 3 个人“喂糖”。

思路

直观思路

类似冒泡排序,找到最大的数字并最多与后面的数字交换两次。如果达不到原来的位置则判定输入无效;否则固定排好的数字顺序,继续下一次“冒泡”。

时间复杂度分析:每次循环都要遍历数列找到最大数字并交换到末尾,一共循环 n 次:\(O(n^2)\)

当数据量达到 \(n^5\) 量级时,计算量达到 \(n^{10}\),测试提示超时。

优化思路

无效输入的判断

以之前样例中的输入为例:

[3, 2, 5, 6, 7, 4, 1]

目标结果,亦即原来的队形为:

[1, 2, 3, 4, 5, 6, 7]

将两数列相减,得到每个人从初始位置来到当前位置所需要移动的次数,亦即需要消耗的糖果个数(负数实际上是表示吃到糖果的个数):

[2, 0, 2, 2, 2, -2, -6]

只要两个数列相减得到的差超过每个人手里糖的个数,则该数列无效

最少交换次数的计算

刚才提到,两数列之差里面的每个正数,是所对应的人从初始位置来到当前位置所需要消耗的糖果个数,但是否意味着我把得到的差的数列里面的正数加起来就是所需要的交换次数呢?

答案是否定的。

为什么呢?

因为两数列之差,实质上仅仅是考虑了每个人从初始位置来到当前位置所需要单独移动的步数,而并没有考虑和别人交换给别人带来的影响。因此每进行一次交换,这个差值都可能有所变化。

所以差值数列对于我们的计算已经没有帮助了,我们需要找到其它信息帮助计算最少交换次数。

再审一遍题目,我们会发现,唯一的限制条件就是:在原队形中,某个人最多只能和他前面的两个人交换位置,因为每个人只有两块糖。

回到之前的例子,仔细观察第一个样例:

输入数列1:[3, 2, 5, 6, 7, 4, 1]

  • 7 后退两格:[3, 2, 5, 6, 4, 1, 7]
  • 6 后退两格:[3, 2, 5, 4, 1, 6, 7]
  • 5 后退两格:[3, 2, 4, 1, 5, 6, 7]
  • 4 后退一格:[3, 2, 1, 4, 5, 6, 7]
  • 3 后退两格:[2, 1, 3, 4, 5, 6, 7]
  • 2 后退一格:[1, 2, 3, 4, 5, 6, 7]

7 之所以能够后退两格,是因为 7 的后面有两个数字比 7 小;

6 之所以能够后退两格,是因为 6 的后面有两个数字比 6 小;

5 之所以能够后退两格,是因为 6 的后面有两个数字比 5 小;

4 之所以能够后退一格,是因为 4 的后面有一个数字比 4 小;

3 之所以能够后退两格,是因为 3 的后面有两个数字比 3 小;

2 之所以能够后退一格,是因为 2 的后面有两个数字比 2 小;

而且,交换数字使序列回归部分有序状态,并不影响乱序部分的队形。也就是说,可以统计每个数字后面比该数字小的数字个数,然后对每个数字的统计结果求和

但是,上述算法需要对每个数字遍历进行统计,依然是 \(O(n^2)\) 的时间复杂度。

换个方向,我们也可以这样做:

输入数列1:[3, 2, 5, 6, 7, 4, 1]

  • 1 前进六格:[1, 3, 2, 5, 6, 7, 4]
  • 2 前进一格:[1, 2, 3, 5, 6, 7, 4]
  • 4 前进三格:[1, 2, 3, 4, 5, 6, 7]

1 之所以能够前进六格,是因为 1 的前面有六个数字比 1 大;

2 之所以能够前进一格,是因为 2 的前面有一个数字比 2 大;

4 之所以能够前进三格,是因为 4 的前面有三个数字比 4 大;

注意:“被喂糖”的次数是没有硬性规定的

从右往左搜索比当前数字大的数,搜索范围就被“喂糖次数限制”限定在了一定范围内。

比如说,我要找比 6 大的数,比 6 大的数只可能出现在原队形中 6 的位置左边两格以右的区域:

[3, 2, 5, 6, 7, 4, 1]

[1, 2, 3, 4, 5, 6, 7]

在这个例子中,比 6 大的数只可能出现在第 5 个数字及以后,而当前的乱序队形中 6 是第 4 个数字,也就是说在 6 的左边且比 6 大的数字不存在,也就不用找了。

更严谨的语言表达就是:

设当前乱序队形为 \(Q\),\(Q_i\) 表示队列中的第 \(i\) 个数字,\(1\le i \le n\) 也可以代表原队形;

如果要找在 \(Q_i\) 左边且比 \(Q_i\) 大的数字,只需要在 \(j = max(Q_i-1, 0), ..., i-1\) 的取值范围内遍历 \(Q_j\) 就能找到。

实现

def minimumBribes(q):
candies = 0 for i in range(len(q)):
if q[i] - (i+1) > 2:
print('Too chaotic')
return
for j in range(max(0, q[i]-1-1), i):
if q[j] > q[i]:
candies += 1 print(candies)

Written with StackEdit.

Hackerank-Array-NewYearChaos的更多相关文章

  1. javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈

    Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...

  2. ES5对Array增强的9个API

    为了更方便的对Array进行操作,ES5规范在Array的原型上新增了9个方法,分别是forEach.filter.map.reduce.reduceRight.some.every.indexOf ...

  3. JavaScript Array对象

    介绍Js的Array 数组对象. 目录 1. 介绍:介绍 Array 数组对象的说明.定义方式以及属性. 2. 实例方法:介绍 Array 对象的实例方法:concat.every.filter.fo ...

  4. 了解PHP中的Array数组和foreach

    1. 了解数组 PHP 中的数组实际上是一个有序映射.映射是一种把 values 关联到 keys 的类型.详细的解释可参见:PHP.net中的Array数组    . 2.例子:一般的数组 这里,我 ...

  5. 关于面试题 Array.indexof() 方法的实现及思考

    这是我在面试大公司时碰到的一个笔试题,当时自己云里雾里的胡写了一番,回头也曾思考过,最终没实现也就不了了之了. 昨天看到有网友说面试中也碰到过这个问题,我就重新思考了这个问题的实现方法. 对于想进大公 ...

  6. javascript之活灵活现的Array

    前言 就如同标题一样,这篇文章将会灵活的运行Array对象的一些方法来实现看上去较复杂的应用. 大家都知道Array实例有这四个方法:push.pop.shift.unshift.大家也都知道 pus ...

  7. 5.2 Array类型的方法汇总

    所有对象都具有toString(),toLocaleString(),valueOf()方法. 1.数组转化为字符串 toString(),toLocaleString() ,数组调用这些方法,则返回 ...

  8. OpenGL ES: Array Texture初体验

    [TOC] Array Texture这个东西的意思是,一个纹理对象,可以存储不止一张图片信息,就是说是是一个数组,每个元素都是一张图片.这样免了频繁地去切换当前需要bind的纹理,而且可以节省系统资 ...

  9. Merge Sorted Array

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note:Yo ...

  10. C++ std::array

    std::array template < class T, size_t N > class array; Code Example #include <iostream> ...

随机推荐

  1. Web—09-正则表达式

    正则表达式 1.什么是正则表达式: 能让计算机读懂的字符串匹配规则. 2.正则表达式的写法: var re=new RegExp('规则', '可选参数'); var re=/规则/参数; 3.规则中 ...

  2. js随笔--关于数组

    1.split()将一个字符串分割成字符串数组 stringObject.split(separator,howmany) separator:必需,字符串或正则表达式,从该参数指定的地方分割stri ...

  3. Javascript--将十进制数字转换成罗马数字显示

    下午在FCC(FreeCodeCamp)中文网上做到一道练习题:将给定的数字转换成罗马数字.折磨了一个多小时,终于能把基本功能给实现了.过程如下: 关于罗马数字 罗马数字的详细介绍可见百度,或者罗马数 ...

  4. Immutable.js 以及在 react+redux 项目中的实践

    来自一位美团大牛的分享,相信可以帮助到你. 原文链接:https://juejin.im/post/5948985ea0bb9f006bed7472?utm_source=tuicool&ut ...

  5. 基于Vue实现可以拖拽的树形表格(原创)

    因业务需求,需要一个树形表格,并且支持拖拽排序,任意未知插入,github搜了下,真不到合适的,大部分树形表格都没有拖拽功能,所以决定自己实现一个.这里分享一下实现过程,项目源代码请看github,插 ...

  6. 常用PHP方法

    个人常用的一些方法记录/** * 返回错误 * * @param int $err_no * @param string $err_msg * @param array $data * @return ...

  7. C语言/C++对编程学习的重要性!

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  8. node.js(二)

    今天我们学习如何运行起来一个项目,我还不会新建项目,所以我们打开一个小伙伴创建的项目,我用的开发工具是vscode 选择项目所在文件夹就好了. 打开后是这样子的 我们还要安装一下npm, 在这里安装, ...

  9. js判断两个日期是否相等的方法

    今天优化代码的时候,发现一个问题,js比较日期是否相等时,我用==去比较,发现两个时间不相等但是运行结果却是true,然后去百度了下发现oldStartTime, startTime都是对象,类型为引 ...

  10. Verilog的一些系统任务(二)

    $monitor 任务$monitor提供了监控和输出参数列表中的表达式或变量值的功能. 格式: $monitor(p1,p2,...,pn);   $monitor;        $monitor ...