• 起:

力扣的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. SAP FICO 常用table

    Table 描 述 "Table Type" "Application Class" "Data Class" Description &q ...

  2. Servlet之Request和Response 解析

    原理 tomcat服务器会根据请求url中的资源路径,创建对应的Servlet的对象 tomcat服务器.会创建request和response对象,request对象中封装请求消息数据. tomca ...

  3. zabbix配置邮件报警

    1.yum源安装sendmail,sendmail-cf和mailx 2.关闭postfix,/etc/init.d/postfix stop chkconfig posfix off 3.启动sen ...

  4. 【前端面试】(二)JavaScript加法运算

    视频链接:JavaScript加法运算 - Web前端工程师面试题讲解 数值 + 数值 首先看菜鸟教程有关于数值对象的教程 JavaScript Number 对象 可以知道Infinity , -I ...

  5. sql-DCL用户及权限管理及其他常用命令-mysql

    查看系统信息 SHOW PROCESSLIST -- 显示哪些线程正在运行 SHOW VARIABLES -- 显示系统变量信息 SELECT now(), user(), version(); -- ...

  6. Python教程:执行cmd命令

    我们通常可以使用os模块的命令进行执行cmd 方法一:os.system def system(*args, **kwargs): # real signature unknown "&qu ...

  7. Python|range函数用法完全解读

    写在前面的一些过场话: 迭代器是 23 种设计模式中最常用的一种(之一),在 Python 中随处可见它的身影,我们经常用到它,但是却不一定意识到它的存在.在关于迭代器的系列文章中(链接见文末),我至 ...

  8. 用 40 块搞个游戏机「GitHub 热点速览 v.22.27」

    作者:HelloGitHub-小鱼干 最便宜的小霸王游戏机都超过了五十,但是现在有了 PicoBoot 你用 40 块的树莓派就能搞出个任天堂游戏机(NGC).PicoBoot 替换了 NGC 的 I ...

  9. windows 安全

    Windows基础篇html { overflow-x: initial !important } :root { --bg-color: #ffffff; --text-color: #333333 ...

  10. linux 配置集群需要修改的东西

    1. 服务器主机名 vi /etc/hostname 按Esc,然后:wq! ,保存,然后重启电脑 reboot 2.修改IP和mac,也可以设置成自动的,但一般是固定的 cd /etc/syscon ...