快速排序--全集

快速排序:一听名字就知道这种排序很快的,是吧?没错,它是一种效率比较高的排序算法。

快速排序采用的是分治的思想。

比如,将一串数中的一个元素作为基准,然后将比它小的数排在它的左边,比它大的数排在它的右边。将作为基准的那个元素排在正确的位置。然后通过递归,依次将所有的元素都排好。(实质就是递归子问题)

在这里,快速排序也有几种不同的方式,下面我给大家一一叙述吧。

快速排序的几种方法:

1.左右指针法(交换):
       有一个数组a[size],左指针left指向下标为0的位置,右指针right指向下标为size-1的位置,同时标志key=a[right];left找a[left]>key的元素的下标,right找a[right] < key的元素的下标。left先开始走,当遇到比key大的数时,停下来,再由right开始走,当遇到比key小的数时,停下来。将left和right分别指的数交换,前提是left<right时,如果left>=right就说明一次快排结束。再将left指向的值与key值交换即可。此时的数组呈现出左边的值都比key值小,右边的值都比key值大。一次快排将数组分为两个区间,我们再对每个区间进行上述的排序方式。直到每个小区间已不能再划分。(即一个区间只有两个元素)



代码实现:

int PartSort(int* a,int left,int right)
{int& key = a[right];//int key = right;while(left < right) {while(left < right && a[left] <= key) //a[left] <= a[key]++left;while(left < right && a[right] >= key) //a[right] >= a[key]--right;swap(a[left],a[right]);}swap(a[left],key);//swap(a[left],a[key]);return left;
}
void QuickSort(int* a,int left,int right)
{if(left < right){int div = PartSort(a,left,right);QuickSort(a,left,div-1);QuickSort(a,div+1,right);}
}
void Display(int *a,size_t size)
{for(size_t i = 0; i < size; ++i){cout<<a[i]<<" ";}cout<<endl;
}


2.挖坑法
       有一个数组a[size],左指针left指向下标为0的位置,右指针right指向下标为size-1的位置,同时标志key=a[right];首先将key的位置设为一个坑,从left开始,找比key值大的,找到后将a[left]放到坑里,即a[right]=a[left],同时将left的位置再设为坑;再从right开始找比key小的,找到后将a[right]放到上次设的坑里,即a[left]= a[right];直到left>=right,将key值放到现在的坑中即可。此时的数组呈现出左边的值都比key值小,右边的值都比key值大。一次快排将数组分为两个区间,我们再对每个区间进行上述的排序方式。直到每个小区间已不能再划分。(即一个区间只有两个元素)



代码实现:

int PartSort1(int* a,int left,int right)
{int key = a[right];while(left < right) {while(left < right && a[left] <= key) ++left;a[right] = a[left];while(left < right && a[right] >= key) --right;a[left] = a[right];}a[left] = key;return left;
}
void QuickSort(int* a,int left,int right)
{if(left < right){int div = PartSort1(a,left,right);QuickSort(a,left,div-1);QuickSort(a,div+1,right);}
}


3.前后指针法
      有一个数组a[size],指针cur指向0的位置,prev指向cur-1的位置,key为a[size-1];当cur对应的值大于key值时,cur++,然后将++prev对应的值与cur对应的值交换。继续比它大的值就跳过,找到a[cur]<key的值时,交换a[cur]和a[++prev]的值。直到cur指向最后一个元素,此时将a[++prev]和最后一个元素交换。此时的数组呈现出左边的值都比key值小,右边的值都比key值大。一次快排将数组分为两个区间,我们再对每个区间进行上述的排序方式。直到每个小区间已不能再划分。(即一个区间只有两个元素)



代码实现:

int PartSort2(int* a,int left,int right)
{int key = a[right];int cur = left;int prev = cur-1;while(cur < right){if(a[cur] < key && ++prev != cur)swap(a[cur],a[prev]);++cur;}swap(a[++prev],a[cur]);return prev;
}
void QuickSort(int* a,int left,int right)
{if(left < right){int div = PartSort2(a,left,right);QuickSort(a,left,div-1);QuickSort(a,div+1,right);}
}


以上就是快速排序的几种方法。

当然,分析过时间复杂度和空间复杂度之后,在某些特定的情况下,快速排序依然不是很快。下面就给大家介绍几种快速排序优化后的算法。

1.三数取中法

      在快排算法中,如果数组本来就有序,key值就不会是中间的数字,那么快排每次遍历N次,时间复杂度就为O(N*N)为了避免快排遇到最坏的情况,我们采用三数取中法的方式,取到中间值的数与最后一个数交换,再进行快排方式。 这样可以使数组的key值都在较中间的位置,趋向于二叉树结构。时间复杂度也将提升到N*lgN。

代码实现:

//三数取中法
int MidNum(int* a,int left,int right)
{int mid = left+((right-left)>>1);if(a[mid] < a[right]){if(a[mid] < a[right])return mid;else if(a[left] < a[right])return right;elsereturn left;}else{if(a[left] < a[right])return left;else if(a[mid] < a[right])return right;elsereturn mid;}
}int PartSortMid(int* a,int left,int right)
{int MidKey = MidNum(a,left,right);swap(a[MidKey],a[right]);int key = right;while(left < right){while(left < right && a[left] <= a[key])    //左边如果比key位置的数小,则跳过++left;while(left < right && a[right] >= a[key])   //右边如果比key位置的数大,则跳过--right;swap(a[left],a[right]);}swap(a[left],a[key]);return left;
}
void QuickSort(int* a,int left,int right)
{if(left < right){int div = PartSortMid(a,left,right);	QuickSort(a,left,div-1);QuickSort(a,div+1,right);}
}


2.小区间优化

      对于上述三数取中法的快排算法,每次都是将一小段区间进行快排算法,即使区间很小也重复快排算法。我们前面学过 插入算法,当数组接近有序时,时间复杂度就为O(N),那么在快排算法中,我们可以对小区间进行优化,区间很小时,我们可以将数组视为接近有序,此时用插入排序来代替快排,比如当right-left<一个小区间的值时(自己规定,一般为13或20较好),这样就大大提高了快排的效率。

代码实现:

//小区间优化
void QuickSort(int* a,int left,int right)
{if(left < right){if(right - left < 3)     //当区间比较小,接近有序时,适合使用插入排序{InsertSort(a,right-left+1);}int div = PartSort(a,left,right);QuickSort(a,left,div-1);QuickSort(a,div+1,right);}
}


1.之前写的快排都是递归实现的。我们也可以用非递归实现快速排序。这里会用到栈。

代码实现:

//用栈实现
#include<stack>
int MidNum(int* a,int left,int right)
{int mid = left+((right-left)>>1);if(a[mid] < a[right]){if(a[mid] < a[right])return mid;else if(a[left] < a[right])return right;elsereturn left;}else{if(a[left] < a[right])return left;else if(a[mid] < a[right])return right;elsereturn mid;}
}int PartSortMid(int* a,int left,int right)
{int MidKey = MidNum(a,left,right);swap(a[MidKey],a[right]);int key = right;while(left < right){while(left < right && a[left] <= a[key])++left;while(left < right && a[right] >= a[key])--right;swap(a[left],a[right]);}swap(a[left],a[key]);return left;
}void QuickSortNonR(int *a,int left,int right)
{stack<int> st;st.push(right);st.push(left);while(!st.empty()){if(right-left < 3)InsertSort(a,right-left+1);left = st.top();st.pop();right = st.top();st.pop();int div = PartSortMid(a,left,right);if(div-1 > left){st.push(div-1);     //注意入栈顺序st.push(left);}if(div+1 < right){st.push(right);st.push(div+1);}}
}

运行结果:




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382754.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

task_struct结构体查找

网上有很多解析task_struct结构体的文章&#xff0c;可是都没有说这个结构体到底在哪里&#xff1f; 这个结构体位于头文件 shced.h cd / find -name sched.h 显示结果如下 注意只有 位于内核中的include 才是正确的。 /usr/src/kernels/2.6.32-431.el6.i686/include/linux…

闹钟函数alarm()的解释与实践

alarm 定义 也称为闹钟函数&#xff0c;它可以在进程中设置一个定时器&#xff0c;当定时器指定的时间到时&#xff0c;它向进程发送SIGALRM信号。可以设置忽略或者不捕获此信号&#xff0c;如果采用默认方式其动作是终止调用该alarm函数的进程。 #include "head.h&quo…

Linux下如何设置权限让用户只删除自己的文件(粘滞位)

之前我们知道如何针对用户和用户组来设置文件权限。通常是用三个八进制来设置权限的&#xff0c;这里我要说的是&#xff0c;其实是由四个八进制表示的。其中第一个八进制我们通常是忽略的。第二个到第四个是对应于SUID,SGID,sticky-bit。 SUID&#xff1a;设置了SUID 位的文件…

Vim简单配置

vim配置&#xff1a; &#xff08;在Centos6.5下配置vim&#xff09; 1.找到用户的主工作目录&#xff0c;ls看是否有.vimrc文件&#xff0c;有的话打开即可。没有的话自己touch一个。vim进入.vimrc中&#xff1a; set nu 设置行数 colorscheme desert syntax enabl…

[WPS笔试题]实现栈的push,pop,max且时间复杂度为O(1)

今天做了一下WPS的笔试题&#xff0c;遇到了一道关于栈的题&#xff0c;觉得挺有意思的&#xff0c;就写篇博客分享一下吧~~ 题目要求&#xff1a;要求实现栈的数据结构&#xff0c;在该类型中实现一个能够得到栈的最大元素的max函数&#xff0c;在该栈中&#xff0c;调用max,…

[剑指Offer]替换空格

今天看题的时候&#xff0c;遇到一个替换空格的题目&#xff0c;分析一下哈。 题目要求&#xff1a;把字符串中的每个空格替换成“%20”。例如输入“we are happy”&#xff0c;则输出“we%20are%20happy”。 解题思路&#xff1a;我们首先想到的是&#xff1a;移位思想。遇到…

C语言关键字 ISO/ANSI C90 C99 C11

面试考点 https://blog.csdn.net/csdn_kou/article/details/81113215 * 有的常用的我们都不知道是关键字&#xff0c;比如sizeof.这是面试中的考点&#xff0c;要注意。 * 同时当回答C语言中有多少关键字时&#xff0c;要回答前题条件&#xff0c;时针对哪一个版本

关于sudo

之前&#xff0c;我们使用sudo的时候&#xff0c;是因为其用户本身具有root权限&#xff0c;所以可以sudo后执行相关操作&#xff0c;但是对于普通用户来说&#xff0c;它是既不具有sudo权限&#xff0c;又不在sudo用户组中&#xff0c;那么我们来研究一下如何将新创建的用户添…

Bash入门

Bash简介&#xff1a; Bash&#xff08;GNU Bourne-Again Shell&#xff09;是一个为GNU计划编写的Unix shell&#xff0c;它是许多Linux平台默认使用的shell。 shell是一个命令解释器&#xff0c;是介于操作系统内核与用户之间的一个绝缘层。准确地说&#xff0c;它也是能力…

线程之售票系统pthread_mutex,_lock,_unlock

先看一下这篇文章 https://blog.csdn.net/csdn_kou/article/details/81148268 四个人同时买票票&#xff0c;引出线程 #include "head.h" int ticket 100; void * route(void *arg) {char *id (char *)arg;while(1){if(ticket>0){usleep(1000);printf("…

Bash基本语法

1. 变量赋值 a375 hello$a 这里需要注意的是&#xff0c;等号两边不能有空格 还有一个例子是这样的 例1&#xff1a; 结果为&#xff1a; 关于上述&#xff0c;主要有如下几点&#xff1a; $hello和${hello}是一样的&#xff0c;在bash中如果遇到空格&#xff0c;tab键时&a…

关于fd和fp(fd:file descirptor fp:file pointor)

通常&#xff0c;我们在输入数据或输出数据的设备为键盘或者显示器。当然&#xff0c;我们比较熟悉的输入输出&#xff0c;可能就是对于文件的操作&#xff0c;还有直接从终端输出&#xff0c;显示到显示器上。在C语言中&#xff0c;我们使用fopen,fclose,fread,fwrite对文件进…

[linux]wait详解

wait&#xff1a;进程等待 主要有两种等待方式&#xff1a;阻塞式等待和非阻塞式等待 阻塞式等待&#xff1a;如果子进程正在运行&#xff0c;父进程将会一直等待着子进程运行结束&#xff0c;并且自己什么事都不干 非阻塞式等待&#xff1a;如果子进程正在运行&#xff0c;…

[Linux]消息队列

我们知道进程间通信的方法有多种&#xff0c;主要有管道&#xff0c;消息队列&#xff0c;信号量&#xff0c;共享内存&#xff0c;socket等。之前介绍过管道&#xff0c;今天再介绍一个新的概念–消息队列。 消息队列&#xff1a;将一个进程到另一个进程之间发送数据块的方式…

C++关键字速查手册

[TOC] https://blog.csdn.net/csdn_kou/article/details/81113215 C98关键字 C11 关键字共73个 alignas alignof用于获取取指定表达式指定的&#xff08;类似sizeof&#xff0c;可以直接是类型名&#xff09;的对齐(alignment)。alignas用于声明时指定对齐类似于现有的类型…

[Linux]信号

Linux下的信号是一个什么概念呢。我们在现实生活中也遇到过信号之类的。比如红绿信号灯&#xff0c;班主任叫你去办公室并且脸色不好&#xff0c;诸如此类的都会给你一个信号。让你辨别事情的发生。同样&#xff0c;Linux下也有许多的信号&#xff0c;让你执行相应的操作。比如…

[数据结构]Map和Set

说起map和set&#xff0c;想必我们都学过红黑树了吧&#xff0c;map和set就是红黑树的一个应用领域。它的底层就是由红黑树来实现的。下面简单说一下map和set的使用吧。 首先&#xff0c;有一个栗子是这样的&#xff0c;让我们统计出每种水果出现的次数。 我们会想到怎么解决…

linux之信号

信号&#xff1a;在生活中&#xff0c;我们遇到过不同种类的信号&#xff0c;比如&#xff1a;&#xff08;交通信号&#xff0c;乃至某个人的表情&#xff0c;动作等带给你不同的信号&#xff09;然而&#xff0c;在我们的linux下&#xff0c;我们最熟悉的就是&#xff0c;当遇…

[Linux]继续探究mysleep函数(竞态条件)

之前我们探究过mysleep的简单用法&#xff0c;我们实现的代码是这样的&#xff1a; #include<stdio.h> #include<signal.h>void myhandler(int sig) {}unsigned int mysleep(unsigned int timeout) {struct sigaction act,oact;act.sa_handler myhandler;sigempt…