快速排序总结_legend



       快速排序

(一) 简介 :

 快速排序是由Hoare提出的,采用了分治法(Divide-and-ConquerMethod)。
分治法一般分成三个组成部分:分解,解决,合并。
快速排序不需要合并,只有分解 ,以及解决(递归)两个步骤。
(二) 基本思想 :(挖坑填数+分治法)

 1.先从数列中取出一个数作为基准数。
 2.将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边,得到基准数的 下标 I 。
 3.再对左右区间(左区间[left , i-1], 右区间[ i+1, right ]  )重复第二步,直到各区间只有一个数。

(三) 步骤:

 (挖坑填数+分治法):

(1)挖坑填数图文解析 :

以一个数组作为示例,取区间第一个数为基准数。
0   1   2   3   4   5   6   7   8  9
72 6 57 88 60 42 83 73 48 85
初始时,i = 0;  j = 9;   X = a[i] = 72,基准数为左边元素,双向扫描。
由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。
从j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; ;  这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; 
 
数组变为:
0   1   2   3   4   5   6   7   8  9
48 6 57 88 60 42 83 73 88 85
 i = 3;   j = 7;   X=72
再重复上面的步骤,先从后向前找,再从前向后找。
从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;
从i开始向后找,当i=5时,由于i==j退出。
此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]。
 
数组变为:
0   1   2  3   4   5   6   7   8   9
48 6 57 42 60 72 83 73 88 85
可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。

(2) 挖坑填数总结 :

1.i =L; j = R; 将基准数挖出形成第一个坑a[i] , 即将a[i] 的值保存到temp 中。
(注意保存的是基准值,而并不是基准值的下标。)

2.由后向前找比基准数小的数,找到后挖出此数填前一个坑a[ i ]中,即a[ j] 的值赋给a[ i ]

3.由前向后找大于等于基准数的数,找到后也挖出此数填到前一个坑a[j]中,即将a[ i] 的值赋值给 a[ j ]。

4.再重复执行2,3二步,直到i==j,将基准数填入a[i] 这个坑中。
5.  结果为返回基准数的下标 i .


(刚开始时是将array[left]赋值给temp ,所以要从右边找数填坑。
即左边挖坑,右边找数填坑;
右边挖坑,左边找数 填坑。)

(3) 实现代码 :

int partition(int s[], int left, int right) //分解函数,返回调整后基准数的位置
{
       int leftIndex = left, rightIndex = right;
       int x = s[left]; //s[l]即s[i]就是第一个坑
       while (leftIndex < rightIndex)
       {
              // 从右向左找小于x的数来填s[i]
              while(leftIndex < rightIndex && s[rightIndex] >= x)
                     rightIndex--;
              if(leftIndex < rightIndex)
              {
                     s[leftIndex] = s[rightIndex]; //将s[j]填到s[i]中,s[j]就形成了一个新的坑
                                   }
 
              // 从左向右找大于或等于x的数来填s[j]
              while(leftIndex < rightIndex && s[leftIndex] < x)
                     leftIndex++;
              if(leftIndex <rightIndex)
              {
                     s[rightIndex] = s[leftIndex]; //将s[i]填到s[j]中,s[i]就形成了一个新的坑
                                  }
       }
       //退出时,i等于j。将x填到这个坑中。
       s[leftIndex] = x;
 
       return leftIndex;
}
 (
注意: 此中x保存的是基准值,而不是基准值的下标,因为在后来的比较中,通过下标找到的基准值可能已经发生了替换。

再写分治法的代码:
void quick_sort1(int s[], int left, int right)
{
       if (left < right)
    {
              int pivot = partition(s, left, right);//先成挖坑填数法调整s[]
              quick_sort1(s, left, pivot - 1); // 递归调用
              quick_sort1(s, pivot + 1, right);
       }
}


(四) 快速排序扩展 :

(1) 算法描述 :
(2) 时间复杂度分析 :
(3) 具体实现细节 :
3.1)划分 :
 3.1.1 选取基准数(枢纽点)
  1. 左边元素
  2. 随机元素
  3. 三数取中

 3.1.2 分割
  1. 单向扫描
  2. 双向扫描
  3. Hoare的双向扫描
  4. 改进的双向扫描
  5. 双向扫描的其他问题
3.2)分治 :
 3.2  尾递归

(4) 时间复杂度分析:
快速排序最佳运行时间O(nlogn),最坏运行时间O(N^2)。
两点很重要:
1. 选取枢纽元的不同, 决定了快排算法时间复杂度的数量级;
2. 划分方法的划分方法总是O(n), 所以其具体实现的不同只影响算法时间复杂度的系数。

(5) 选取基准值 :select_pivot( ElementType  A[], int left, int right)(枢纽元)
1. 左边元素
2. 随机元素
3. 三数取中
1) 左边元素:
对于完全随机的数据,枢纽元的选取不是很重要,往往直接取左端的元素作为枢纽元。

 ElementType selecet_pivot(ElementType A[ ] , int left ,int right ){
     Return A[left ];
}

问题:但是实际应用中,数据往往是部分有序的,如果仍用两端的元素最为枢纽元,则会产生很不好的划分,使算法退化成O(n^2)。

所以要采用一些手段避免这种情况,我知道的有“随机选取法”和“三数取中法”。

2)随机元素 :

 ElementType select_pivot(ElementType A[ ] ,int left ,int right ){
Int i=randInt(left ,right );
Swap(&A[left ] ,& A[ i ]);
Return A[left ];
}

分析:此中获取的基准值始终是A[ left ] ,这样 选择不同 基准值,后面的代码不用改变 。

(3) 三数取中 :

1) ElementType select_pivot(ElementType A[ ] ,int left ,int right ){
Int mid=(left+right)/2;


// swap to ensure A[mid ]<=A[left ]<=A[ right ]


If(A[left] < A[mid] ) swap(&A[left], &A[mid]);
If(A[right]<A[left])  swap(&A[left], &A[right]);
If(A[right]<A[mid])   swap(&A[left] ,& A[right]);
Return A[left ];


}

(5)  改进快速排序 :


按照上面的方法,递归会持续到分区只有一个元素。而事实上,当分割到一定大小后,继续分割的效率比插入排序要差。

所以 改进为 :

Void  quicksort( ElementType A [ ] ,int left ,int right){
If (q-p>cutoff ) // cutoff  is constant
{
Int pivot=partition(A,left ,right);
quicksort(A, left ,pivot-1);
quicksort(A,pivot+1,right);
}
Else
insertSort(A,left ,right);
}

分析: 本来快速排序的递归条件是if (left < right) ,
此中将其改为 if( right-left>cutoff) ,说明在前面一部分是用快速排序,
到后面一部分直接是用插入排序 来处理更加小的数组。
(6) 总结 :
1. 快速排序 (分治法中的划分,解决):
1. 选取基准值(左边元素,随机元素,三数取中)
2. 划分函数 partition 之 挖坑填数,返回枢纽点下标(中心点下标)。
3. 递归


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此微信小程序医院挂号预约系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。微信小程序医院挂号预约系统有管理员,用户两个角色。管理员功能有个人中心,用户管理,医生信息管理,医院信息管理,科室信息管理,预约信息管理,预约取消管理,留言板,系统管理。微信小程序用户可以注册登录,查看医院信息,查看医生信息,查看公告资讯,在科室信息里面进行预约,也可以取消预约。微信小程序医院挂号预约系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值