本文 https://github.com/youngyangyang04/leetcode-master 已经收录,里面还有leetcode刷题攻略、各个类型经典题目刷题顺序、思维导图,可以fork到自己仓库,有空看一看一定会有所收获,如果对你有帮助也给一个star支持一下吧!

在讲解贪心算法:根据身高重建队列中,我们提到了使用vector(C++中的动态数组)来进行insert操作是费时的。

但是在解释的过程中有不恰当的地方,所以来专门写一篇文章来详细说一说这个问题。

使用vector的代码如下:

// 版本一,使用vector(动态数组)
class Solution {
public:static bool cmp(const vector<int> a, const vector<int> b) {if (a[0] == b[0]) return a[1] < b[1];return a[0] > b[0];}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort (people.begin(), people.end(), cmp);vector<vector<int>> que;for (int i = 0; i < people.size(); i++) {int position = people[i][1];que.insert(que.begin() + position, people[i]);}return que;}
};

耗时如下:

其直观上来看数组的insert操作是O(n)的,整体代码的时间复杂度是O(n^2)。

这么一分析好像和版本二链表实现的时间复杂度是一样的啊,为什么提交之后效率会差距这么大呢?

// 版本二,使用list(链表)
class Solution {
public:// 身高从大到小排(身高相同k小的站前面)static bool cmp(const vector<int> a, const vector<int> b) {if (a[0] == b[0]) return a[1] < b[1];return a[0] > b[0];}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort (people.begin(), people.end(), cmp);list<vector<int>> que; // list底层是链表实现,插入效率比vector高的多for (int i = 0; i < people.size(); i++) {int position = people[i][1]; // 插入到下标为position的位置std::list<vector<int>>::iterator it = que.begin();while (position--) { // 寻找在插入位置it++;}que.insert(it, people[i]);}return vector<vector<int>>(que.begin(), que.end());}
};

耗时如下:

大家都知道对于普通数组,一旦定义了大小就不能改变,例如int a[10];,这个数组a至多只能放10个元素,改不了的。

对于动态数组,就是可以不用关心初始时候的大小,可以随意往里放数据,那么耗时的原因就在于动态数组的底层实现。

动态数组为什么可以不受初始大小的限制,可以随意push_back数据呢?

首先vector的底层实现也是普通数组

vector的大小有两个维度一个是size一个是capicity,size就是我们平时用来遍历vector时候用的,例如:

for (int i = 0; i < vec.size(); i++) {}

而capicity是vector底层数组(就是普通数组)的大小,capicity可不一定就是size。

当insert数据的时候,如果已经大于capicity,capicity会成倍扩容,但对外暴漏的size其实仅仅是+1。

那么既然vector底层实现是普通数组,怎么扩容的?

就是重新申请一个二倍于原数组大小的数组,然后把数据都拷贝过去,并释放原数组内存。(对,就是这么原始粗暴的方法!)

举一个例子,如图:

原vector中的size和capicity相同都是3,初始化为1 2 3,此时要push_back一个元素4。

那么底层其实就要申请一个大小为6的普通数组,并且把原元素拷贝过去,释放原数组内存,注意图中底层数组的内存起始地址已经变了

同时也注意此时capicity和size的变化,关键的地方我都标红了

而在贪心算法:根据身高重建队列中,我们使用vector来做insert的操作,此时大家可会发现,虽然表面上复杂度是O(n2),但是其底层都不知道额外做了多少次全量拷贝了,所以算上vector的底层拷贝,整体时间复杂度可以认为是O(n2 + t * n)级别的,t是底层拷贝的次数

那么是不是可以直接确定好vector的大小,不让它在动态扩容了,例如在贪心算法:根据身高重建队列中已经给出了有people.size这么多的人,可以定义好一个固定大小的vector,这样我们就可以控制vector,不让它底层动态扩容。

这种方法需要自己模拟插入的操作,不仅没有直接调用insert接口那么方便,需要手动模拟插入操作,而且效率也不高!

手动模拟的过程其实不是很简单的,需要很多细节,我粗略写了一个版本,如下:

// 版本三
// 使用vector,但不让它动态扩容
class Solution {
public:static bool cmp(const vector<int> a, const vector<int> b) {if (a[0] == b[0]) return a[1] < b[1];return a[0] > b[0];}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort (people.begin(), people.end(), cmp);vector<vector<int>> que(people.size(), vector<int>(2, -1));for (int i = 0; i < people.size(); i++) {int position = people[i][1];if (position == que.size() - 1) que[position] = people[i];else { // 将插入位置后面的元素整体向后移for (int j = que.size() - 2; j >= position; j--) que[j + 1] = que[j];que[position] = people[i];}}return que;}
};

耗时如下:

这份代码就是不让vector动态扩容,全程我们自己模拟insert的操作,大家也可以直观的看出是一个O(n^2)的方法了。

但这份代码在leetcode上统计的耗时甚至比版本一的还高,我们都不让它动态扩容了,为什么耗时更高了呢?

一方面是leetcode的耗时统计本来就不太准,忽高忽低的,只能测个大概。

另一方面:可能是就算避免的vector的底层扩容,但这个固定大小的数组,每次向后移动元素赋值的次数比方法一中移动赋值的次数要多很多。

因为方法一中一开始数组是很小的,插入操作,向后移动元素次数比较少,即使有偶尔的扩容操作。而方法三每次都是按照最大数组规模向后移动元素的。

所以对于两种使用数组的方法一和方法三,也不好确定谁优,但一定都没有使用方法二链表的效率高!

一波分析之后,对于贪心算法:根据身高重建队列 ,大家就安心使用链表吧!别折腾了,哈哈,相当于我替大家折腾了一下。

总结

大家应该发现了,编程语言中一个普通容器的insert,delete的使用,都可能对写出来的算法的有很大影响!

如果抛开语言谈算法,除非从来不用代码写算法纯分析,否则的话,语言功底不到位O(n)的算法可以写出O(n^2)的性能,哈哈。

相信在这里学习算法的录友们,都是想在软件行业长远发展的,都是要从事编程的工作,那么一定要深耕好一门编程语言,这个非常重要!

我是程序员Carl,可以找我组队刷题,也可以在B站上找到我,本文leetcode刷题攻略已收录,更多精彩算法文章尽在公众号:代码随想录,关注后就会发现和「代码随想录」相见恨晚!

如果感觉对你有帮助,不要吝啬给一个

「leetcode」406.根据身高重建队列【贪心算法】详解-续集!相关推荐

  1. 171. Leetcode 406. 根据身高重建队列 (贪心算法-两个维度权衡题目)

    class Solution:def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:people.sort ...

  2. c语言编程 插队排身高,【C语言刷LeetCode】406. 根据身高重建队列(M)

    [ 假设有打乱顺序的一群人站成一个队列. 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数. 编写一个算法来重建这个队列. 注意: 总人数少于110 ...

  3. 力扣Leetcode之Java解题406根据身高重建队列

    406. 根据身高重建队列 题目: 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序).每个 people[i] = [hi, ki] 表示第 i 个人的身 ...

  4. Java实现 LeetCode 406 根据身高重建队列

    406. 根据身高重建队列 假设有打乱顺序的一群人站成一个队列. 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数. 编写一个算法来重建这个队列. ...

  5. (LeetCode 406)根据身高重建队列 [贪心+sort+条件推理 ]

    406. 根据身高重建队列 假设有打乱顺序的一群人站成一个队列. 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数. 编写一个算法来重建这个队列. ...

  6. 贪心算法|406. 根据身高重建队列|先排序后插队

    贪心算法|406. 根据身高重建队列|先排序后插队 406. 根据身高重建队列 - 力扣(LeetCode) (leetcode-cn.com) 题目 假设有打乱顺序的一群人站成一个队列,数组 peo ...

  7. 406. 根据身高重建队列

    链接:406. 根据身高重建队列 题解: class Solution {public:vector<vector<int>> reconstructQueue(vector& ...

  8. Suzy想吃烤蛋挞了Day35 | 贪心算法进行时:860. 柠檬水找零,406. 根据身高重建队列,452. 用最少数量的箭引爆气球

    860. 柠檬水找零 solution 不要漏掉的一种情况是:如果收到了20,可以找10+5,或者5+5+5 class Solution:def lemonadeChange(self, bills ...

  9. 代码随想录35——贪心:860柠檬水找零、406根据身高重建队列、452用最少数量的箭引爆气球

    文章目录 1.860柠檬水找零 1.1.题目 1.2.解答 2.406根据身高重建队列 2.1.题目 2.2.解答 3.452用最少数量的箭引爆气球 3.1.题目 3.2.解答 1.860柠檬水找零 ...

  10. 代码随想录算法训练营第35天|860.柠檬水找零,406.根据身高重建队列,452. 用最少数量的箭引爆气球

    代码随想录算法训练营第35天|860.柠檬水找零,406.根据身高重建队列,452. 用最少数量的箭引爆气球 860.柠檬水找零 406. 根据身高重建队列 452. 用最少数量的箭引爆气球 860. ...

最新文章

  1. 2.Liunx 系统设置
  2. Graph Embedding方案之DeepWalk
  3. /etc/bashrc和/etc/profile傻傻分不清楚?
  4. 征集活动 | PMCAFF八周年,十万产品经理送祝福
  5. SpringAOP xml 方式和注解简单实现日志处理
  6. main方法 如何去掉http debug日志_在MyBatis中如何使用collection标签实现嵌套查询?...
  7. 为了异常安全(swap,share_ptr)——Effecive C++
  8. 一加10 Pro胖达白512GB至尊版发布 售价5799元
  9. PHP面向对象分析设计的61条军规
  10. 51Nod-1012 最小公倍数LCM【欧几里得算法】
  11. catgroup linux_Linux用户(user)和用户组(group)的日常管理与操作教程概述
  12. iOS网络开发—POST请求和GET请求
  13. ArcView GIS 应用与开发技术(5)-统计图
  14. Python参考手册(第4版)
  15. imx6 android 最新,Re: IMX6D Android6.0 OTA 升级问题
  16. 解决Ubuntu强制获取root权限后只剩下客人会话而无法正常登录
  17. Excel整行数据自动标颜色
  18. 【低智版狗屁不通文章生成器】Python 基于一条语法随机生成简单中文句子的小练习
  19. Redis基于内存非关系型数据库
  20. Linux操作命令符(基本)

热门文章

  1. 类的运用,求矩形面积
  2. file_get_contents(): php_network_getaddresses: getaddrinfo failed: Name or service not known
  3. svn: No repository found in 'svn:..解决方案
  4. Sqlserver表数据误删除后的恢复
  5. 准确率(Precision)、召回率(Recall)、F值(F-Measure)、ROC、AUC
  6. Winform 分页用户自定义控件( ML.Pager.WinControl)
  7. CAS 配置NLB 负载均衡网络无法连接
  8. Http协议中常用字段总结(不定时完善中)
  9. ios- uitextview的详细使用方法
  10. ubuntu 下 升级 nginx