首页 > 其他分享 >七大排序详解

七大排序详解

时间:2024-10-11 22:48:00浏览次数:7  
标签:int 复杂度 七大 详解 static array 排序 left

大家好呀,在今天我们学习目前阶段中,我们最常用的七种排序——插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序,强烈建议大家配合代码和图片一起食用

一,排序简介

二,插入排序

排序思想

直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

1.1直接插入排序

排序原理

给定一个数组和一个临时变量tmp,直接插入排序大概分以下几步,其中变量 i 从数组下标 1 开始遍历数组,变量j每次从 i-1 开始向前调整,直到 i 前面的数据有序

 代码实现

public class Main {
    public static void main(String[] args) {
        int[] array={10,2,3,7,9,8,6,59,40};
        Main.DirectSort(array);
        System.out.println(Arrays.toString(array));
    }
    public static void DirectSort(int[] array){//遍历数组的i
        for (int i = 1; i <array.length; i++) {
            int tmp=array[i];
            int j = i-1;
            for (; j>=0 ; j--) {//向前调整的j
                if(array[j]>tmp){
                    array[j+1]=array[j];
                }else {
                    break;//这个时候j不用向下调整了
                }
            }//for循环走完后j=-1;
            array[j+1]=tmp;
        }
    }
}

复杂度分析

时间复杂度:O(N^2)

空间复杂度O(1)

稳定性:稳定

2.2 希尔排序

排序原理
 

希尔排序也可以说是对直接插入排序的优化,用一个需要排序的数据之间的下表距离变量Gap先把数据分为一组组数据,这样数据组数在变多,但是每组数据更加有序,在数据不可再分时,这个时候也就有序了。可以达到让直接插入排序时间复杂度降低的效果

代码实现

这个方法对我们刚才写的直接插入排序也有一定修改,具体见代码

 public static void ShellSort(int[] array){
        int Gap=array.length;
        while(Gap>=1){
            shell(array,Gap);
            Gap/=2;//调整Gap 
        }
    }
    /*
    这里的i和j都要以Gap为单位调整了,但是任然要i++,
    无非就是每组交替排序,这样可以一次排序更多组数据
    * */
    private static void shell(int[] array,int Gap){
        for (int i = Gap; i <array.length; i++) {
            int tmp=array[i];
            int j = i-Gap;
            for (; j>=0 ; j-=Gap) {//向前调整的j
                if(array[j]>tmp){
                    array[j+Gap]=array[j];
                }else {
                    break;//这个时候j不用向下调整了
                }
            }
            array[j+Gap]=tmp;
        }
    }

复杂度分析

希尔排序的时间复杂度分析是一个非常麻烦的过程,会根据Gap的不同而不同,这里只给出一般认为的时间负载度,具体大家可自行了解

时间复杂度:O(N^1.3)

空间复杂度O(1)

稳定性:不稳定

容易看出,希尔排序比直接插入排序还是快上不少的

三,选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

3.1 直接选择排序

排序原理

1.在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素

2.若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
3.在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

这里我们用midIndex记录最小数据对应的下标,然后用两个指针i完成对数组的遍历,j完成最小数据下标的寻找

代码实现

public static void SelectSort(int[] array) {
        for (int i = 0; i < array.length; i++) {//i用于遍历数组
            int minIndex = i;
            for (int j = i; j < array.length; j++) {//j一直向前找一个最小值对应的下标
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            //交换操作
            int tmp = array[i];
            array[i]=array[minIndex];
            array[minIndex]=tmp;
        }
    }

复杂度分析

时间复杂度:O(N^2)

空间复杂度O(1)

稳定性:不稳定

3.2 堆排序

排序原理

堆排序原理比较复杂,主要是围绕建创建堆来的,创建堆这部分我们在之前的博客中有介绍过,详情大家可以看看CSDN这篇文章,这里就不多赘述了,主要是在于如何通过创建好的堆来实现一个堆排序,我们这里以创建好一个大堆为例:

代码实现

/*
堆排序
**/
public static void HeapSort(int[] array){
        CreateHeap(array);
        int flag=array.length-1;
        while(flag>0){
            swap(array,0,flag);
            shiftDown(0,array,flag);
            flag--;
        }
    }
//建堆
    public static void CreateHeap(int[] array){
        int i = (array.length-2)>>1;
        for ( ; i >=0 ; i--) {
            shiftDown(i,array,array.length);
        }
    }
    private static void shiftDown(int parent,int[] array,int length){
        int child=parent*2+1;
        while(child<length){
            if(child+1<length&&array[child+1]>array[child]){
                child++;
            }
            if(array[parent]>=array[child]){
                break;
            }else {
                swap(array,parent,child);

                parent=child;
                child=parent*2+1;
            }
        }
    }
    public static void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x]=array[y];
        array[y]=tmp;
    }

复杂度分析

四,交换排序

4.1 冒泡排序

排序原理

冒泡排序相信大家都很熟悉,排序原理就在与通过两个循环一次只把一个数据放在正确的位置

代码实现

public static void BubbleSort(int[] array){
        for (int i = 0; i <array.length-1 ; i++) {
            for (int j = 0; j <array.length-1-i ; j++) {
                if(array[j]>array[j+1]){
                    int tmp=array[j+1];
                    array[j+1]=array[j];
                    array[j]=tmp;               
               }
           }
        }
    }

冒泡排序优化

当然,我们也可以用标记法优化一下冒泡排序,如果在一次 j 向后走过程中没法生任何交换的话说明原来的数据已经有序了

public static void BubbleSort(int[] array){
        for (int i = 0; i <array.length-1 ; i++) {
            boolean flag=true;
            for (int j = 0; j <array.length-1-i ; j++) {
                if(array[j]>array[j+1]){
                    int tmp=array[j+1];
                    array[j+1]=array[j];
                    array[j]=tmp;
                    flag=false;
                }
            }
            if(flag){
                break;
            }
        }
    }

复杂度分析

时间复杂度:O(N^2)

空间复杂度O(1)

稳定性:稳定

4.2  快速排序

  快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:

1.任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列

2.通过交换让左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值

3.左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

排序原理


代码实现

public static void QuickSort(int[] array){//这里多一步是为了和之前的方法参数统一
        quickSort(array,0,array.length-1);
    }
    public static void quickSort(int[] array,int start,int end){
        if(start>=end){//结束条件
            return;
        }
        int div=getDiv(array,start,end);
        quickSort(array,start,div-1);//递归排序
        quickSort(array,div+1,end);

    }
    public static int  getDiv(int[] array,int left,int right){
         int tmp=array[left];
         int LeftFlag=left;
        while(left<right){//从右往左,遇到比基准值小的停
            while(left<right&&array[right]>=tmp){
                right--;
            }//必须先从右往左找否则交换的时候可能会出问题
            while(left<right&&array[left]<=tmp){//从左往右,遇到比基准值大的停
                left++;
            }
            swap(array,left,right);
        }
        swap(array,LeftFlag,left);
        return left;
    }
    public static void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x]=array[y];
        array[y]=tmp;
    }

快速排序优化

快速排序的缺点在与在排序有序的数据时,时间复杂度会达到O(N^2), 这似乎很奇怪,其实关键就在于基准值的选取,基准值最好是一个比较折中的数,但是数据有序显然不满足这种要求,这个时候我们提出下面两种解决办法

1. 三数取中法选key

顾名思义,选取最左,最右,和最中间的数,并选择他们中第二大的数为基准

代码实现

 private static int getMiddle(int[] array,int left,int right){
        int mid=(left+right)/2;
        if(array[left]<array[right]){
            if(array[mid]<array[left]){
                return left;
            }else return mid;
        }else {
            if(array[mid]<array[right]){
                return right;
            }else return mid;
        }
    }
//加入了求中间值的方法
    public static void QuickSort(int[] array){//这里多一步是为了和之前的接口统一
        quickSort(array,0,array.length-1);
    }
    public static void quickSort(int[] array,int start,int end){
        if(start>=end){//结束条件
            return;
        }

        int MiddleIndex=getMiddle(array,start,end);
        swap(array,start,MiddleIndex);//这里有所改动
        int div=getDiv(array,start,end);
        quickSort(array,start,div-1);//递归排序
        quickSort(array,div+1,end);

    }
    public static int  getDiv(int[] array,int left,int right){
         int tmp=array[left];
         int LeftFlag=left;
        while(left<right){//从右往左,遇到比基准值小的停
            while(left<right&&array[right]>=tmp){
                right--;
            }//必须先从右往左找否则交换的时候可能会出问题
            while(left<right&&array[left]<=tmp){//从左往右,遇到比基准值大的停
                left++;
            }
            swap(array,left,right);
        }
        swap(array,LeftFlag,left);
        return left;
    }
    public static void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x]=array[y];
        array[y]=tmp;
    }


2. 递归到小的子区间时,可以考虑使用插入排序

在区间长度较小时,选择插入排序效率高于快排,所以这时可以采用快排减少递归深度,达到提高效率的效果,注意直接插入排序需要相应改动,配合上刚才的优化便是完整版快排

 private static int getMiddle(int[] array,int left,int right){
        int mid=(left+right)/2;
        if(array[left]<array[right]){
            if(array[mid]<array[left]){
                return left;
            }else return mid;
        }else {
            if(array[mid]<array[right]){
                return right;
            }else return mid;
        }
    }
    public static void QuickSort(int[] array){//这里多一步是为了和之前的接口统一
        quickSort(array,0,array.length-1);
    }
    public static void quickSort(int[] array,int start,int end){
        if(start>=end){//结束条件
            return;
        }
        if(end-start+1<=3){
            DirectSortRange(array,start,end);
            return;
        }
        int MiddleIndex=getMiddle(array,start,end);
        swap(array,start,MiddleIndex);
        int div=getDiv(array,start,end);
        quickSort(array,start,div-1);//递归排序
        quickSort(array,div+1,end);

    }
    public static int  getDiv(int[] array,int left,int right){
         int tmp=array[left];
         int LeftFlag=left;
        while(left<right){//从右往左,遇到比基准值小的停
            while(left<right&&array[right]>=tmp){
                right--;
            }//必须先从右往左找否则交换的时候可能会出问题
            while(left<right&&array[left]<=tmp){//从左往右,遇到比基准值大的停
                left++;
            }
            swap(array,left,right);
        }
        swap(array,LeftFlag,left);
        return left;
    }
    public static void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x]=array[y];
        array[y]=tmp;
    }
 public static void DirectSortRange(int[] array,int start,int end){
        for (int i = start+1; i <= end; i++) {
            int tmp = array[i];
            int j = i - 1;
            for (; j >= start; j--) {//向前调整的j
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
                    break;//这个时候j不用向下调整了
                }
            }//for循环走完后j=-1;
            array[j + 1] = tmp;
        }
    }

4.3 快速排序非递归

刚才的快排涉及到递归,数据量一多很容易栈溢出,因此这里给出非递归代码实现方式,主要是用栈模拟了递归过程

排序原理

初始化栈来模拟递归过程,需要用到上面用于调整数组的Getdiv方法,推荐配合代码食用

代码实现

public static void QuickSortNor(int[] array){
        quickSortNor(array,0,array.length-1);

    }
    public static void quickSortNor(int[] array,int start,int end){
        Stack<Integer> stack=new Stack<>();
        int div=getDiv(array,start,end);
        if(div>start+1){
            stack.push(start);
            stack.push(div-1);
        }
        if(div<end-1){
            stack.push(div+1);
            stack.push(end);
        }
        while(!stack.empty()){
            end=stack.pop();
            start=stack.pop();
            div=getDiv(array,start,end);
            if(div>start+1){
                stack.push(start);
                stack.push(div-1);
            }
            if(div<end-1){
                stack.push(div+1);
                stack.push(end);
            }
        }

4.4 快速排序总结

快速排序整体性能和应用场景都是比较好的

时间复杂度: O(N*logN)

空间复杂度:O(logN)

稳定性:不稳定

五,归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使
子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤

排序原理

核心原理就在与先将数组分为一个个小块,再调用一个合并有序数组的方法将各个数组合并和方法,先让每个子序列有序,再合并起来

这里合并两个有序数组的算法可以见. - 力扣(LeetCode)必刷的简单算法,就不赘述了

代码实现

 public static void MergeSort(int[] array){
        mergeSort(array,0,array.length-1);
    }

    private static void mergeSort(int[] array, int left, int right) {
        if(left>=right){
            return;
        }
        int mid=(left+right)/2;
        mergeSort(array,left,mid);
        mergeSort(array,mid+1,right);
        merge(array,left,mid,right);
    }

    private static void merge(int[] array, int left, int mid, int right) {
        int[] newArray=new int[right-left+1];
        int start1=left;
        int start2=mid+1;
        int index=0;
        while(start1<=mid&&start2<=right) {
            if (array[start1] < array[start2]) {
                newArray[index++] = array[start1++];
            } else {
                newArray[index++] = array[start2++];
            }
        }

            while(start1<=mid){
                newArray[index++]=array[start1++];
            }

            while(start2<=right ){
                newArray[index++]=array[start2++];
            }
//在将新数组的值赋给原数组时必须在原数组下标加上left,否则会出现每次都从新数组0开始赋值给原数组,不
//合理
            for (int i = 0; i <index; i++) {
                array[i+left]=newArray[i];
            }
        }
    }

复杂度分析

时间复杂度: O(N*logN)

空间复杂度:O(N)

稳定性:稳定

六,总结

好啦,以上就是我们最常见的七大算法啦,有误请在大家指正,感谢大家观看,我们下期见~

标签:int,复杂度,七大,详解,static,array,排序,left
From: https://blog.csdn.net/2302_81982408/article/details/141995821

相关文章

  • 换位操作符详解
    下面是一些简单的换位代码和解释     那么代码就分享到这里,谢谢大家! ......
  • c++知识点多关键字排序
    在C++中,可以使用std::sort函数结合自定义的比较函数对多关键字进行排序。以下是一个示例代码,演示如何根据两个关键字对结构体进行排序:#include<iostream>#include<vector>#include<algorithm>structItem{intfirstKey;intsecondKey;std::stringname;};//自定......
  • 《软件工程导论》—— 1 - 13章习题详解!
    摘要:张海藩的《软件工程导论》(第6版)的课后习题,涵盖软件工程多个关键领域,包括软件危机、可行性研究、需求分析、设计方法(总体设计、详细设计、面向对象设计)、实现、维护以及项目管理等,通过理论阐述、方法介绍以及大量实际案例分析,全面深入地讲解了软件工程的核心知识和实......
  • Spring Cloud Netflix Ribbon 负载均衡详解和案例示范
    1.引言在传统的集中式架构中,负载均衡器一般是放置在服务器端的,例如Nginx等。随着微服务架构的兴起,服务实例的数量和部署地点变得更加动态和分布式,这使得在客户端进行负载均衡成为了一种可行且更灵活的方案。NetflixRibbon提供了一种客户端侧负载均衡策略,使服务消费者在......
  • Spring Cloud Netflix Zuul 网关详解及案例示范
    1.引言在微服务架构中,API网关作为服务间通信的入口,扮演着重要的角色。NetflixZuul是一个提供动态路由、监控、安全等功能的API网关服务器,它可以为微服务系统提供统一的入口,简化服务间的交互。在业务系统中,Zuul可以有效地管理和路由多个微服务的请求,并通过自定义过滤......
  • 避雷!Google Adsense联盟营销七大投放误区
    你是否在使用GoogleAdSense进行广告投放?你是否想进一步优化你的投放策略?那么这篇文章你不可错过啦!GoogleAdSense为跨境商家提供了一个平台,我们可以通过展示相关广告来赚取收入。然而,即使是最有经验的商家也可能在广告投放策略上犯一些常见的错误,而这些错误可能会影响我们的......
  • C代码随笔——冒泡排序
    题目:对一串乱序数字排序并且进行重复元素去重冒泡排序的基本规则:        比较相邻的元素。如果第一个比第二个大(升序排序),就交换它们两个。对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。重复以上的步骤,除了最后已......
  • C语言-常见文件操作函数详解(fgetc,fputc,fgets,fputs,fscanf,fprintf,fread,fwrite)
     ......
  • 【数据结构】深度解析堆排序
    目录......
  • [Git] git stash命令详解
     前言目录gitstash-mgitstashlistgitstashpopgitstashapplyindexgitstashdropindexgitstashclear特定范围文件储存gitstash[-S|--staged]gitstash[-u|--include-untracked]gitstash[-a|--all]将当前未提交的修改(即工作区和暂存区的修改)......