八大排序算法Java实现(下)-快排、归排、基数排序

cout<<a[j] <<" ";

}

cout<<endl;

}

void swap(int *a, int *b)

{

int tmp = *a;

*a = *b;

*b = tmp;

}

int partition(int a[], int low, int high)

{

int privotKey = a[low]; //基准元素

while(low < high){ //从表的两端交替地向中间扫描

while(low < high && a[high] >= privotKey) --high; //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端

swap(&a[low], &a[high]);

while(low < high && a[low] <= privotKey ) ++low;

swap(&a[low], &a[high]);

}

print(a,10);

return low;

}

void qsort_improve(int r[ ],int low,int high, int k){

if( high -low > k ) { //长度大于k时递归, k为指定的数

int pivot = partition(r, low, high); // 调用的Partition算法保持不变

qsort_improve(r, low, pivot - 1,k);

qsort_improve(r, pivot + 1, high,k);

}

}

void quickSort(int r[], int n, int k){

qsort_improve(r,0,n,k);//先调用改进算法Qsort使之基本有序

//再用插入排序对基本有序序列排序

for(int i=1; i<=n;i ++){

int tmp = r[i];

int j=i-1;

while(tmp < r[j]){

r[j+1]=r[j]; j=j-1;

}

r[j+1] = tmp;

}

}

int main(){

int a[10] = {3,1,5,7,2,4,9,6,10,8};

cout<<“初始值:”;

print(a,10);

quickSort(a,9,4);

cout<<“结果:”;

print(a,10);

}

7 归并排序(Merge Sort)

=================================================================================

思想


将两个(或以上)有序表合并成一个新的有序表:

  • 把待排序列分为若干子序列,每个子序列是有序的

  • 再把有序子序列合并为整体有序序列

示例

合并方法


设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。

  1. j=m+1;k=i;i=i; //置两个子表的起始下标及辅助数组的起始下标

  2. 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束

  3. //选取r[i]和r[j]较小的存入辅助数组rf

如果r[i]<r[j],rf[k]=r[i]; i++; k++; 转⑵

否则,rf[k]=r[j]; j++; k++; 转⑵

  1. //将尚未处理完的子表中元素存入rf

如果i<=m,将r[i…m]存入rf[k…n] //前一子表非空

如果j<=n , 将r[j…n] 存入rf[k…n] //后一子表非空

  1. 合并结束。

public class MergeSort {

public static void mergeSort(int[] arr) {

if (arr == null || arr.length < 2) {

return;

}

mergeSort(arr, 0, arr.length - 1);

}

public static void mergeSort(int[] arr, int l, int r) {

if (l == r) {

return;

}

int mid = l + ((r - l) >> 1);

mergeSort(arr, l, mid);

mergeSort(arr, mid + 1, r);

merge(arr, l, mid, r);

}

public static void merge(int[] arr, int l, int m, int r) {

int[] help = new int[r - l + 1];

int i = 0;

int p1 = l;

int p2 = m + 1;

while (p1 <= m && p2 <= r) {

help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];

}

while (p1 <= m) {

help[i++] = arr[p1++];

}

while (p2 <= r) {

help[i++] = arr[p2++];

}

for (i = 0; i < help.length; i++) {

arr[l + i] = help[i];

}

}

/**

  • @param arr

*/

public static void comparator(int[] arr) {

Arrays.sort(arr);

}

/**

  • for test

  • @param maxSize

  • @param maxValue

  • @return

*/

public static int[] generateRandomArray(int maxSize, int maxValue) {

int[] arr = new int[(int) ((maxSize + 1) * Math.random())];

for (int i = 0; i < arr.length; i++) {

arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());

}

return arr;

}

/**]

  • for test

  • @param arr

  • @return

*/

public static int[] copyArray(int[] arr) {

if (arr == null) {

return null;

}

int[] res = new int[arr.length];

for (int i = 0; i < arr.length; i++) {

res[i] = arr[i];

}

return res;

}

/**

  • for test

  • @param arr1

  • @param arr2

  • @return

*/

public static boolean isEqual(int[] arr1, int[] arr2) {

if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {

return false;

}

if (arr1 == null && arr2 == null) {

return true;

}

if (arr1.length != arr2.length) {

return false;

}

for (int i = 0; i < arr1.length; i++) {

if (arr1[i] != arr2[i]) {

return false;

}

}

return true;

}

/**

  • for test

  • @param arr

*/

public static void printArray(int[] arr) {

if (arr == null) {

return;

}

for (int i = 0; i < arr.length; i++) {

System.out.print(arr[i] + " ");

}

System.out.println();

}

/**

  • for test

  • @param args

*/

public static void main(String[] args) {

int testTime = 500000;

int maxSize = 100;

int maxValue = 100;

boolean succeed = true;

for (int i = 0; i < testTime; i++) {

int[] arr1 = generateRandomArray(maxSize, maxValue);

int[] arr2 = copyArray(arr1);

mergeSort(arr1);

comparator(arr2);

if (!isEqual(arr1, arr2)) {

succeed = false;

printArray(arr1);

printArray(arr2);

break;

}

}

System.out.println(succeed ? “Nice!” : “Fucking fucked!”);

int[] arr = generateRandomArray(maxSize, maxValue);

printArray(arr);

mergeSort(arr);

printArray(arr);

}

}

归并的迭代算法


1个元素的表总是有序的。所以对n个元素的待排序列,每个元素可看成1个有序子表。对子表两两合并生成n/2个子表,所得子表除最后一个子表长度可能为1,其余子表长度均为2。再进行两两合并,直到生成n个元素按关键码有序的表。

void print(int a[], int n){

for(int j= 0; j<n; j++){

cout<<a[j] <<" ";

}

cout<<endl;

}

//将r[i…m]和r[m +1 …n]归并到辅助数组rf[i…n]

void Merge(ElemType *r,ElemType *rf, int i, int m, int n)

{

int j,k;

for(j=m+1,k=i; i<=m && j <=n ; ++k){

if(r[j] < r[i]) rf[k] = r[j++];

else rf[k] = r[i++];

}

while(i <= m) rf[k++] = r[i++];

while(j <= n) rf[k++] = r[j++];

print(rf,n+1);

}

void MergeSort(ElemType *r, ElemType *rf, int lenght)

{

int len = 1;

ElemType *q = r ;

ElemType *tmp ;

while(len < lenght) {

int s = len;

len = 2 * s ;

int i = 0;

while(i+ len <lenght){

Merge(q, rf, i, i+ s-1, i+ len-1 ); //对等长的两个子表合并

i = i+ len;

}

if(i + s < lenght){

Merge(q, rf, i, i+ s -1, lenght -1); //对不等长的两个子表合并

}

tmp = q; q = rf; rf = tmp; //交换q,rf,以保证下一趟归并时,仍从q 归并到rf

}

}

int main(){

int a[10] = {3,1,5,7,2,4,9,6,10,8};

int b[10];

MergeSort(a, b, 10);

print(b,10);

cout<<“结果:”;

print(a,10);

}

两路归并的递归算法


void MSort(ElemType *r, ElemType *rf,int s, int t)

{

ElemType *rf2;

if(s==t) r[s] = rf[s];

else

{

int m=(s+t)/2; /平分p 表*/

MSort(r, rf2, s, m); /递归地将p[s…m]归并为有序的p2[s…m]/

MSort(r, rf2, m+1, t); /递归地将p[m+1…t]归并为有序的p2[m+1…t]/

Merge(rf2, rf, s, m+1,t); /将p2[s…m]和p2[m+1…t]归并到p1[s…t]/

}

}

void MergeSort_recursive(ElemType *r, ElemType *rf, int n)

{ /对顺序表p 作归并排序*/

MSort(r, rf,0, n-1);

}

8 桶排序/基数排序(Radix Sort)及计数排序

==========================================================================================

说基数排序前,我们先说桶排序,桶排序是稳定的。

思想


将阵列分到有限数量的桶里,再对每个桶再个别排序(有可能再使用别的排序或以递回方式继续使用桶排序)。

当要被排序的阵列内的数值是均匀分配的时候,桶排序使用线性时间O(N)。但桶排序并非比较排序,他不受 O(NlogN) 下限的影响。

简单来说,就是把数据分组,放在一个个的桶中,然后对每个桶里面的再排序 。例如要对大小为[1…1000]范围内的n个整数A[1…n]排序:

  • 把桶设为大小为10的范围

设集合B[1]存储[1…10]的整数,集合B[2]存储 (10…20]的整数,……集合B[i]存储( (i-1)_10, i_10]的整数,i = 1,2,…100

总共有 100个桶

  • 对A[1…n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中

再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择或快排

  • 依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这样就得到所有数字排好序的一个序列了。

假设有n个数字,m个桶,如果数字均匀分布,则每个桶里面均有n/m个数

如果对每个桶中的数字采用快排,那么整个算法的复杂度是:

O(n + m * n/m*log(n/m)) = O(n + nlogn - nlogm)

当m接近n时,桶排序复杂度接近O(n) 。

以上复杂度的计算基于输入的n个数字是均匀分布。该假设条件是很强的,实际应用中效果并没有这么好。如果所有的数字都落在同一个桶中,那就退化成一般的排序 。

前面说的几大排序算法 ,大部分时间复杂度都是O(n^2),也有部分排序算法O(nlogn)

而桶式排序却能实现O(n)时间复杂度

但桶排序的缺点是

  • 空间复杂度较高,额外开销大

排序有两个数组的空间开销,一个存放待排序数组,一个就是所谓的桶,比如待排序值是从0到m-1,那就需要m个桶,这个桶数组就要至少m个空间

  • 待排序的元素都要在一定的范围内等等

桶式排序是一种分配排序。分配排序的特定是不需要进行关键码的比较,但前提是要知道待排序列的一些具体情况

分配排序思想:说白了就是进行多次的桶式排序。

基数排序过程无须比较关键字,而是通过“分配”和“收集”过程来实现排序

它们的时间复杂度可达到线性阶:O(n)

实例

扑克牌中52 张牌

2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K < A

对扑克牌按花色、面值进行升排

即两张牌,若花色不同,不论面值怎样,花色低的那张牌小于花色高的,只有在同花色情况下,大小关系才由面值的大小确定。这就是多关键码排序。

为得到排序结果,我们讨论两种排序方法。

  • 方法1:先对花色排序,将其分为4 个组,再对每组分别按面值排序,最后,4 组连接

  • 方法2

先给出13 个编号组(2 号,3 号,…,A 号),将牌按面值依次放入对应的编号组,分成13 堆

再按花色给出4 个编号组(梅花、方块、红心、黑心),将2号组中牌取出分别放入对应花色组,再将3 号组中牌取出分别放入对应花色组,……,这样,4 个花色组中均按面值有序,然后,将4 个花色组依次连接起来即可

设n 个元素的待排序列包含d 个关键码{k1,k2,…,kd},则称序列对关键码{k1,k2,…,kd}有序是指:对于序列中任两个记录r[i]和rj都满足下列有序关系:

其中k1 称为最主位关键码,kd 称为最次位关键码 。

两种多关键码排序方法:

多关键码排序按照从最主位关键码到最次位关键码或从最次位到最主位关键码的顺序逐次排序,分两种方法:

最高位优先(Most Significant Digit first)法,简称MSD 法:

1)先按k1 排序分组,将序列分成若干子序列,同一组序列的记录中,关键码k1 相等。

2)再对各组按k2 排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd 对各子组排序后。

3)再将各组连接起来,便得到一个有序序列。扑克牌按花色、面值排序中介绍的方法一即是MSD 法。

最低位优先(Least Significant Digit first)法,简称LSD 法:

  1. 先从kd 开始排序,再对kd-1进行排序,依次重复,直到按k1排序分组分成最小的子序列后。

  2. 最后将各个子序列连接起来,便可得到一个有序的序列, 扑克牌按花色、面值排序中介绍的方法二即是LSD 法。

基于LSD方法的链式基数排序的基本思想

“多关键字排序”的思想实现“单关键字排序”。对数字型或字符型的单关键字,可以看作由多个数位或多个字符构成的多关键字,此时可以采用“分配-收集”的方法进行排序,这一过程称作基数排序法,其中每个数字或字符可能的取值个数称为基数。比如,扑克牌的花色基数为4,面值基数为13。在整理扑克牌时,既可以先按花色整理,也可以先按面值整理。按花色整理时,先按红、黑、方、花的顺序分成4摞(分配),再按此顺序再叠放在一起(收集),然后按面值的顺序分成13摞(分配),再按此顺序叠放在一起(收集),如此进行二次分配和收集即可将扑克牌排列有序。

基数排序(radixsort)


1887年赫尔曼·何乐礼发明。

基数排序属“分配式排序”(distributionsort),又称“桶子法”(bucketsort)或binsort,顾名思义,通过键值的各个位的值,将要排序的元素分配至某些“桶”,达到排序的作用。

基数排序法的是效率高的稳定性排序法,是桶排序的扩展。

基本思想

将整数按位数切割成不同的数字,然后按每个位数分别比较。

将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

低位先排序,然后收集;再按高位排序,然后再收集;依次类推,直到最高位。

有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前

基数排序基于分别排序,分别收集,所以是稳定的。

代码实现

总结

谈到面试,其实说白了就是刷题刷题刷题,天天作死的刷。。。。。

为了准备这个“金三银四”的春招,狂刷一个月的题,狂补超多的漏洞知识,像这次美团面试问的算法、数据库、Redis、设计模式等这些题目都是我刷到过的

并且我也将自己刷的题全部整理成了PDF或者Word文档(含详细答案解析)

我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...

66个Java面试知识点

架构专题(MySQL,Java,Redis,线程,并发,设计模式,Nginx,Linux,框架,微服务等)+大厂面试题详解(百度,阿里,腾讯,华为,迅雷,网易,中兴,北京中软等)

我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...

算法刷题(PDF)

我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...

式排序”(distributionsort),又称“桶子法”(bucketsort)或binsort,顾名思义,通过键值的各个位的值,将要排序的元素分配至某些“桶”,达到排序的作用。

基数排序法的是效率高的稳定性排序法,是桶排序的扩展。

基本思想

将整数按位数切割成不同的数字,然后按每个位数分别比较。

将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

低位先排序,然后收集;再按高位排序,然后再收集;依次类推,直到最高位。

有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前

基数排序基于分别排序,分别收集,所以是稳定的。

代码实现

总结

谈到面试,其实说白了就是刷题刷题刷题,天天作死的刷。。。。。

为了准备这个“金三银四”的春招,狂刷一个月的题,狂补超多的漏洞知识,像这次美团面试问的算法、数据库、Redis、设计模式等这些题目都是我刷到过的

并且我也将自己刷的题全部整理成了PDF或者Word文档(含详细答案解析)

[外链图片转存中…(img-E2x3WLjN-1720028644546)]

66个Java面试知识点

架构专题(MySQL,Java,Redis,线程,并发,设计模式,Nginx,Linux,框架,微服务等)+大厂面试题详解(百度,阿里,腾讯,华为,迅雷,网易,中兴,北京中软等)

[外链图片转存中…(img-VtAoCgiE-1720028644547)]

算法刷题(PDF)

[外链图片转存中…(img-6U3YjSZs-1720028644548)]

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值