八大排序算法的Java实现(下)

}

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. 合并结束。

package com.sss;

import java.util.Arrays;

/**

  • @author Shusheng Shi

*/

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);

}

}

cpp版本

//将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++];

}

归并的迭代算法

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,顾名思义,通过键值的各个位的值,将要排序的元素分配至某些“桶”,达到排序的作用。

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

基本思想

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

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

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

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

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

代码实现

import java.util.Arrays;

public class BucketSort {

/**

  • only for 0~200 value

  • @param arr

*/

public static void bucketSort(int[] arr) {

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

return;

}

int max = Integer.MIN_VALUE;

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

max = Math.max(max, arr[i]);

}

int[] bucket = new int[max + 1];

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

bucket[arr[i]]++;

}

int i = 0;

for (int j = 0; j < bucket.length; j++) {

while (bucket[j]-- > 0) {

arr[i++] = j;

}

}

}

// for test

public static void comparator(int[] arr) {

Arrays.sort(arr);

}

// for test

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());

}

return arr;

}

// for test

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

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

public static void printArray(int[] arr) {

if (arr == null) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

看完上述知识点如果你深感Java基础不够扎实,或者刷题刷的不够、知识不全面

小编专门为你量身定制了一套<Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法>

image

针对知识面不够,也莫慌!还有一整套的<Java核心进阶手册>,可以瞬间查漏补缺

image

全都是一丢一丢的收集整理纯手打出来的

更有纯手绘的各大知识体系大纲,可供梳理:Java筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的xmind手绘图~

image

image
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
链图片转存中…(img-6VOPgkge-1713529420343)]

[外链图片转存中…(img-QcoM3Tnv-1713529420344)]

[外链图片转存中…(img-W5VHBaXB-1713529420344)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

看完上述知识点如果你深感Java基础不够扎实,或者刷题刷的不够、知识不全面

小编专门为你量身定制了一套<Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法>

[外链图片转存中…(img-2BqR0uJh-1713529420344)]

针对知识面不够,也莫慌!还有一整套的<Java核心进阶手册>,可以瞬间查漏补缺

[外链图片转存中…(img-NvIW6SPu-1713529420345)]

全都是一丢一丢的收集整理纯手打出来的

更有纯手绘的各大知识体系大纲,可供梳理:Java筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的xmind手绘图~

[外链图片转存中…(img-sedNcRHl-1713529420345)]

[外链图片转存中…(img-LKz5q4gW-1713529420345)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 30
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值