• 起:

力扣的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. WPF开发随笔收录-唯一标识符GUID

    一.前言 该系列博客用于记录本人在WPF开发过程中遇到的各种知识点 二.正文 1.在工作的项目中,软件需要用到在线升级功能,由于第一次弄,在下载服务端的文件到本地时,文件的名称我选择直接生成为固定的格 ...

  2. Linux命令格式、终端类型和获取帮助的方法

    Linux用户类型 Root用户:超级管理员,权限很大 普通用户:权限有限 终端 terminal 终端类型 物理终端:鼠标.键盘.显示器 虚拟终端:软件模拟出来的终端 控制台终端: /dev/con ...

  3. 【python基础】第08回 流程控制 for循环

    本章内容概要 1.循环结构之 for 循环 本章内容详解 1.循环结构之for循环 1.1 语法结构 for 变量名 in 可迭代对象: #字符串 列表 字典 元组 for 循环的循环体代码 针对变量 ...

  4. 手把手教你用 Python 下载手机小视频

    今天为大家介绍使用 mitmproxy 这个抓包工具如何监控手机上网,并且通过抓包,把我们想要的数据下载下来. 启动 mitmproxy 首先我们通过执行命令 mitmweb 启动mitmproxy, ...

  5. 用面向对象的方式操作 JSON 甚至还能做四则运算 JSON 库

    前言 在之前实现的 JSON 解析器中当时只实现了将一个 JSON 字符串转换为一个 JSONObject,并没有将其映射为一个具体的 struct:如果想要获取值就需要先做断言将其转换为 map 或 ...

  6. java中的内存划分和一个数组的内存图

    内存概述 内存是计算机中的重要原件,临时存储区域,作用是运行程序.我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存   Java虚拟机要运行程序 ...

  7. 《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(4)-会话面板和HTTP会话数据操作详解

    1.简介 按照从上往下,从左往右的计划,今天就轮到介绍和分享Fiddler的会话面板了. 2.会话列表 (Session list) 概览 Fiddler抓取到的每条http请求(每一条称为一个ses ...

  8. MIT 6.824 Llab2B Raft之日志复制

    书接上文Raft Part A | MIT 6.824 Lab2A Leader Election. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021 ...

  9. 【CSP-J 2021】总结

    前言:程不在长,能过则行.码不在多,无虫则灵.斯是信竞,惟吾爆零.线段维护快,树状跳的勤.数论剩余系,图论前向星.无数竞之推理,无物竞之劳形.大佬楼教主,超奆姚期智,神犇云:您太强了. 早上5:00就 ...

  10. SpringBoot接口 - API接口有哪些不安全的因素?如何对接口进行签名?

    在以SpringBoot开发后台API接口时,会存在哪些接口不安全的因素呢?通常如何去解决的呢?本文主要介绍API接口有不安全的因素以及常见的保证接口安全的方式,重点实践如何对接口进行签名.@pdai ...