• 起:

力扣的912题 数组排序 ,想着先用快速排序来写写,在实际用c++编写的时候,有一些之前没注意到的细节问题造成了一些麻烦。

912. 排序数组 - 力扣(LeetCode)

  • 快排思想

  每次以数组最后一个数为基准,按照波兰国旗问题对数组进行分层(partition)。假设最后一个数为P,则将数组分为小于P、等于P、大于P的3层。之后对小于P和大于P的层进行此过程的迭代。迭代完成后,目标数组即排列完成。

  1.   问题:最坏的结果的O(n^2),因为每次最后一个数当成分层基准,最坏的情况是左右两层极度不平衡
  2.   改进:引入随机数,每次进行分层之前,随机将数组前面的一个数与最后一个数P进行swap,这样分层就成了一个随机事件。在数学证明中,可证明时间复杂度收敛于0(N*LogN)。
  • C++中的随机数

  在c++中,获取随机数一般广为人知的方法是 rand() ,但是这种方法获得的随机数是伪随机数,其原理是从一个已经确定了的序列中按顺序返回整数。

  在直接使用 rand()时,默认的 srand(1)。srand可以认为是种子。

    只使用rand()时

int main() {
for (int i = 0; i < 10; i++) {
cout << rand() << "\t";
}
return 0;
}

  输出结果(因电脑而异):

    41      18467   6334    26500   19169   15724   11478   29358   26962   24464

  你每次运行,答案和该结果都一致,这是因为种子没变,永远输出的是该序列前十个数字。

  所以有一个思路就是改变种子,想让每次运行的种子发生变化,那么就想到了时间,时间是每秒都在变化的,可以让时间来充当种子参数

  

int main() {
srand((int)time(NULL)); // #include<ctime> for time()
for (int i = 0; i < 10; i++) {
cout << rand() << "\t";
}
return 0;
}

  输出结果:

    31937   9951    11817   1295    252     30339   22649   12202   9420    16246

  与之前结果不同了。似乎这达到了我们获取真随机数的目的。但是依旧有一个问题,那就是time 是以秒为单位的,如果你的项目要在一秒之内调用多次随机数呢?这样一来,种子在这一秒之内是不会发生变化的。

int getrd_1() {
srand((int)time(NULL)); // #include<ctime> for time()
return rand();
} int main() {
for (int i = 0; i < 10; i++) {
cout << getrd_1() << "\t";
}
return 0;
}

   输出结果:

      32753   32753   32753   32753   32753   32753   32753   32753   32753   32753

    果然是一样的,因为这十次调用都是在1秒之内。

    这种情况下,可以使用random_device 来生成真随机数

    

int getrd(const int &min, const int&max) {//范围 [min,max)
std::random_device rd; //#include<random> for std::random_device
return min + rd() % max;
}
int main() {
for (int i = 0; i < 10; i++)
std::cout << getrd(0, 10) << "\t";
return 0;
}

    输出结果:

        3       0       0       9       7       8       5       4       9       2

    达到了我们预期的要求。

    但是,需要注意,这个实现要看你的库支持不支持,如果不支持,将继续使用伪随机数发生器。可以通过调用random_device的成员函数 entropy()来查看熵,如果是伪随机发生器,熵恒为零

  • swap

    

//通过一个临时变量来储存b的值,完成交换
void swap(int &a, int &b) {
int tmp(b);
b = a;
a = tmp;
}
//通过异或^来完成交换
void swap(int &a, int &b) {
if (a == b)
return;
a = a ^ b;
b = a ^ b;
a = a ^ b;
}

    第一种交换司空见惯了,第二种则用到了位操作 异或 ^ 的性质:

            a ^ 0 等于 a                           a ^ a 等于 0

            满足结合律,交换律

    因此:

        第一句:a = a ^ b ;   第二句:b = a ^ b,此时 b 等于 a^b^b,结合性质 结果是 a  ; 第三句 : a = a ^ b ,此时 a等于 a ^ b ^ a,结果是b ,交换完成

    需要注意的是,如果a 与  b 的地址相同 则 a^b 等于0。

最后贴上我的快排:

class Solution {
private:
void swap(int& a, int& b) {
if (a == b)
return;
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
int getrd(const int &min,const int& max) {
random_device rd;
return min + rd() % (max - min);
}
public:
//快排
int* Mypartition(vector<int>& nums, int L, int R) {
int less(L - 1), more(R);
int i(L);
while (i < more) {
if (nums[i] < nums[R]) {
swap(nums[++less], nums[i++]);
}
else if (nums[i] > nums[R]) {
swap(nums[i], nums[--more]);
}
else
i++;
}
swap(nums[more], nums[R]);
return new int [2]{ less, more+1 };
}
void process(vector<int>& nums, int L, int R) {
if (L < R) {
// cout<<"L ="<<L<<"\t R="<<R<<endl;
swap(nums[getrd(L,R)], nums[R]);
int *p = Mypartition(nums,L,R);
process(nums, L, p[0]);
process(nums, p[1], R);
}
}
vector<int> sortArray(vector<int>& nums) {
process(nums, 0, nums.size()-1);
return nums;
}
};

C++ 由快排学习到的的随机数等知识的更多相关文章

  1. C++学习(三十八)(C语言部分)之 排序(冒泡 选择 插入 快排)

    算法是解决一类问题的方法排序算法 根据元素大小关系排序 从小到大 从大到小冒泡 选择 插入 快排希尔排序 归并排序 堆排序 冒泡排序 从头到尾比较 每一轮将最大的数沉底 或者最小数字上浮 选择排序 1 ...

  2. Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等

    本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排 ...

  3. poj 2804 字典 (特里 要么 快排+二分法)

    2804:词典 总时间限制:  3000ms  内存限制:  65536kB 描写叙述 你旅游到了一个国外的城市.那里的人们说的外国语言你不能理解.只是幸运的是,你有一本词典能够帮助你. 输入 首先输 ...

  4. 排序 之 快排、归并、插入 - <时间复杂度>----掌握思想和过程

    俗话说:天下武功无坚不破,唯快不破.对于算法当然也是要使用时间最短.占用空间最小的算法来实现了. 注意:我代码里面打的备注仅供参考,建议不要背模板(因为没有固定的模板),可以写一个数列按着代码跑两圈或 ...

  5. <泛> 多路快排

    今天写一个多路快排函数模板,与STL容器兼容的. 我们默认为升序排序 因为,STL容器均为逾尾容器,所以我们这里采用的参数也是逾尾的参数 一.二路快排 基本思路 给你一个序列,先选择一个数作为基数,我 ...

  6. 【C语言编程入门笔记】排序算法之快速排序,一文轻松掌握快排!

    排序算法一直是c语言重点,各个算法适应不用的环境,同时,在面试时,排序算法也是经常被问到的.今天我们介绍下快速排序,简称就是快排. 1.快速排序思想: 快排使用 分治法 (Divide and con ...

  7. Java基础进阶:APi使用,Math,Arrarys,Objects工具类,自动拆装箱,字符串与基本数据类型互转,递归算法源码,冒泡排序源码实现,快排实现源码,附重难点,代码实现源码,课堂笔记,课后扩展及答案

    要点摘要 Math: 类中么有构造方法,内部方法是静态的,可以直接类名.方式调用 常用: Math.abs(int a):返回参数绝对值 Math.ceil(double a):返回大于或等于参数的最 ...

  8. 【PHP数据结构】交换排序:冒泡、快排

    上篇文章中我们好好地学习了一下插入类相关的两个排序,不过,和交换类的排序对比的话,它们真的只是弟弟.甚至可以说,在所有的排序算法中,最出名的两个排序都在今天要介绍的交换排序中了.不管是冒泡.还是快排, ...

  9. F#之旅4 - 小实践之快排

    参考文章:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-quicksort.html F#之旅4 - 小 ...

随机推荐

  1. OpenSSF安全计划:SBOM将驱动软件供应链安全

    在 软件成分分析(SCA)一文中,我们简单提到软件物料清单(SBOM)在安全实践中的价值. 本期文章将带你深入了解 "SBOM 无处不在"计划是什么,以及 SBOM 对未来软件供应 ...

  2. ssh-免密钥登陆

    实现openssh免密钥登陆(公私钥验证) 在主机A上,通过root用户,使用ssh-keygen生成的两个密钥:id_rsa和id_rsa.pub 私钥(id_rsa)保存在本地主机,公钥(id_r ...

  3. ShardingSphere-proxy-5.0.0企业级分库分表、读写分离、负载均衡、雪花算法、取模算法整合(八)

    一.简要说明 以下配置实现了: 1.分库分表 2.每一个分库的读写分离 3.读库负载均衡算法 4.雪花算法,生成唯一id 5.字段取模 二.配置项 # # Licensed to the Apache ...

  4. 基于UniApp社区论坛多端开发实战

    什么是移动端WebApp 移动端WebApp: 泛指手持设备移动端的web 特点: - 类App 应用,运行环境是浏览器 - 可以包一层壳,成为App - 常见的混合应用: ionic, Cordov ...

  5. mesi--cpu内存一致性协议

    目录 cpu缓存一致性问题 mesi协议 mesi协议4种状态,及状态转换 模拟工具演示 cpu缓存一致性问题 一个服务器中有多个核,每个核中有多个cpu,每个cpu有多个线程.缓存最少分为3级,1级 ...

  6. 线程池的概念&原理和线程池的代码实现

    线程池:一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源.工作原理:可以用一张图来简洁明了说明: 合理利用线程池能够带来三个好处∶1.降低 ...

  7. Etcd 使用场景:通过分布式锁思路实现自动选主

    分布式锁?选主? 分布式锁可以保证当有多台实例同时竞争一把锁时,只有一个人会成功,其他的都是失败.诸如共享资源修改.幂等.频控等场景都可以通过分布式锁来实现. 还有一种场景,也可以通过分布式锁来实现, ...

  8. HBase学习(二) 基本命令 Java api

    一.Hbase shell 1.Region信息观察 创建表指定命名空间 在创建表的时候可以选择创建到bigdata17这个namespace中,如何实现呢? 使用这种格式即可:'命名空间名称:表名' ...

  9. P4289 【一本通提高篇广搜的优化技巧】[HAOI2008]移动玩具

    [HAOI2008]移动玩具 题目描述 在一个 4 × 4 4\times4 4×4 的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方 ...

  10. 虚言妙诀终虚见,面试躬行是致知,Python技术面试策略与技巧实战记录

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_183 2021年,对于正在找工作的朋友来说,笼罩在新冠肺炎疫情之下,今年的就业季显得更加具有挑战性,更有意思的是,每当这个时候,各 ...