Java数据结构之基于比较的排序算法基本原理及具体实现

目录
  • 1. 七大基于比较的排序-总览
    • 1.1常见基于比较的排序分类
    • 1.2时间复杂度,空间复杂度以及稳定性。
  • 2.直接插入排序
    • 2.1 直接插入排序的基本思想
    • 2.2 直接插入排序动画演示
    • 2.3 代码示例
    • 2.4 时间复杂度,空间复杂度以及稳定性
  • 3. 希尔排序
    • 3.1 算法思想
    • 3.2 图片演示
    • 3.3 代码示例
    • 3.4 时间复杂度,空间复杂度以及稳定性
  • 4.选择排序
    • 4.1 算法思想
    • 4.2 动画演示
    • 4.3 代码示例
    • 4.4 时间复杂度,空间复杂度以及稳定性
  • 5.堆排序
    • 5.1 算法思想
    • 5.2 动画演示
    • 5.3 代码示例
    • 5.4 时间复杂度,空间复杂度以及稳定性
  • 6.冒泡排序
    • 6.1 算法思想
    • 6.2 动画演示
    • 6.3 代码示例
    • 6.4 时间复杂度,空间复杂度以及稳定性
  • 7.快速排序(十分重要)
    • 7.1 算法思想
    • 7.2 动画演示
    • 7.3 代码示例
    • 7.4 时间复杂度,空间复杂度以及稳定性
  • 8.归并排序
    • 8.1 算法思想
    • 8.2 动画演示
    • 8.3 代码示例
    • 8.4 时间复杂度,空间复杂度以及稳定性

基于比较的排序算法基本原理及Java实现

1. 七大基于比较的排序-总览

1.1常见基于比较的排序分类

1.2时间复杂度,空间复杂度以及稳定性。

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

2.直接插入排序

2.1 直接插入排序的基本思想

直接插入排序的基本思想,就是每次选取一个待排序元素,按照一定规定插入到前面已经排好序的一组元素的适当位置。当每个元素都插入完毕,整个序列已经有序。

当插入第i(i >= 1)时,前面的V[0],V[1],……,V[i-1]已经排好序。这时,用V[I]的排序码与V[i-1],V[i-2],…的排序码顺序进行比较,找到插入位置即将V[i]插入,原来位置上的元素向后顺移。

2.2 直接插入排序动画演示

2.3 代码示例

public static void insertSort(int[] arr){
        for(int i=1;i<arr.length;i++){//从第二个元素开始,遍历整个数组
            int j=i-1;//i之前的序列 已经有序
            int temp=arr[i];
            for(;j>=0;j--){//此循环用于寻找插入位置
                if(arr[j]>temp){//此时逐个向后移动元素
                    arr[j+1]=arr[j];
                }else break;//找到插入位置 直接break
            }
            arr[j+1]=temp;//直接插入
        }
}

2.4 时间复杂度,空间复杂度以及稳定性

  • 时间复杂度最坏情况:数组整体逆序O(n^2);
  • 时间复杂度最好情况:数组已经有序O(n^2);
  • 空间复杂度:O(1);
  • 稳定性:稳定;

3. 希尔排序

3.1 算法思想

希尔排序是特殊的插入排序,直接插入排序每次插入前的遍历步长为1,而希尔排序是将待排序列分为若干个子序列,对这些子序列分别进行直接插入排序,当每个子序列长度为1时,再进行一次直接插入排序时,结果一定是有序的。常见的划分子序列的方法有:初始步长(两个子序列相应元素相差的距离)为要排的数的一半,之后每执行一次步长折半。

3.2 图片演示

3.3 代码示例

public static void hell(int[] arr,int gap){//当gap=1时 其实就是直接插入排序
        for(int i=gap;i<arr.length;i++){
            int j=i-gap;
            int temp=arr[i];
            for(;j>=0;j-=gap){
                if(arr[j]>temp){
                    arr[j+gap]=arr[j];
                }else break;
            }
            arr[j+gap]=temp;
        }
}
public static void hellSort(int[] arr){
        int gap=arr.length;
        while(gap>1){
            gap=gap/2+1;
            hell(arr,gap);
        }
    }

3.4 时间复杂度,空间复杂度以及稳定性

  • 时间复杂度最好:O(n);
  • 时间复杂度最坏:O(n^1.5);
  • 时间复杂度平均:O(n^1.3);
  • 空间复杂度:O(1);
  • 稳定性:不稳定;

4.选择排序

4.1 算法思想

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

4.2 动画演示

4.3 代码示例

public static void selectSort(int[] arr){
        for(int i=0;i<arr.length;i++){
            for(int j=i+1;j<arr.length;j++){
                if(arr[j]<arr[i]){
                    int temp=arr[j];
                    arr[j]=arr[i];
                    arr[i]=temp;
                }
            }
      }
}

4.4 时间复杂度,空间复杂度以及稳定性

  • 时间复杂度最好:O(n);
  • 时间复杂度最坏:O(n^2;
  • 时间复杂度平均:O(n^2);
  • 空间复杂度:O(1);
  • 稳定性:不稳定;

5.堆排序

5.1 算法思想

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

  • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  • 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  • 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

5.2 动画演示

5.3 代码示例

public static void heapSort(int[] arr){//排序方法
        creatHeap(arr);
        int end=arr.length-1;
    //因为是大根堆,所以根节点值为最大值,根节点与最后一个节点值交换 输出并删除此时最后一个节点,然后向下调整根节点
        while(end>0){
            int temp=arr[0];
            arr[0]=arr[end];
            arr[end]=temp;
            shiftDown(arr,0,end);
            end--;
        }

    }
public static void creatHeap(int[] arr){//建堆
        for(int parent=(arr.length-1-1)/2;parent>=0;parent--){
            shiftDown(arr,parent,arr.length);
        }
    }
public static void shiftDown(int[]arr,int root,int len){//向下调整操作
        int parent=root;
        int child=parent*2+1;
        while(child<len){
            if(child+1<len&&arr[child]<arr[child+1]){//待调整节点右子树大于左子树
                child++;
            }
            if(arr[child]>arr[parent]){//由于是大根堆,如果儿子节点值大于父亲节点就交换
                int temp=arr[parent];
                arr[parent]=arr[child];
                arr[child]=temp;
                parent=child;//继续向下调整,防止调整后不满足大根堆的条件
                child=parent*2+1;
            }else break;
        }
    }

5.4 时间复杂度,空间复杂度以及稳定性

  • 时间复杂度最好:O(nlogn);
  • 时间复杂度最坏:O(nlogn);
  • 时间复杂度平均:O(nlogn);
  • 空间复杂度:O(1);
  • 稳定性:不稳定;

6.冒泡排序

6.1 算法思想

比较两个相邻的元素,将值大的元素交换到右边

思路:依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面。

  • 1.第一次比较:首先比较第一和第二个数,将小数放在前面,将大数放在后面。
  • 2.比较第2和第3个数,将小数 放在前面,大数放在后面。
  • 3.如此继续,知道比较到最后的两个数,将小数放在前面,大数放在后面,重复步骤,直至全部排序完成
  • 4.在上面一趟比较完成后,最后一个数一定是数组中最大的一个数,所以在比较第二趟的时候,最后一个数是不参加比较的。
  • 5.在第二趟比较完成后,倒数第二个数也一定是数组中倒数第二大数,所以在第三趟的比较中,最后两个数是不参与比较的。
  • 6.依次类推,每一趟比较次数减少依次

6.2 动画演示

6.3 代码示例

public static void bubbleSort(int[] arr){
        for(int i=0;i<arr.length-1;i++){//外层循环控制趟数
            boolean flag=false;//一个标记进行优化
            for(int j=0;j<arr.length-1-i;j++){//内层循环控制比较次数
                if(arr[j]>arr[j+1]){
                    int temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                    flag=true;
                }
            }
            if(flag==false) break;//如果一趟循环走完没有交换,说明已经有序,直接break
        }
    }

6.4 时间复杂度,空间复杂度以及稳定性

  • 时间复杂度最好:O(n);
  • 时间复杂度最坏:O(n^2);
  • 时间复杂度平均:O(n^2);
  • 空间复杂度:O(1);
  • 稳定性:稳定

7.快速排序(十分重要)

7.1 算法思想

我们从数组中选择一个元素,我们把这个元素称之为中轴元素吧,然后把数组中所有小于中轴元素的元素放在其左边,所有大于或等于中轴元素的元素放在其右边,显然,此时中轴元素所处的位置的是有序的。也就是说,我们无需再移动中轴元素的位置。

从中轴元素那里开始把大的数组切割成两个小的数组(两个数组都不包含中轴元素),接着我们通过递归的方式,让中轴元素左边的数组和右边的数组也重复同样的操作,直到数组的大小为1,此时每个元素都处于有序的位置。

7.2 动画演示

7.3 代码示例

//该代码参考acwing创始人yxc的快排模板
public static void quickSort(int[] arr,int l,int r){
        if(l>=r) return;//每次判断传进来左右下标
        int i=l-1,j=r+1;//因为在循环中,要先i++,j--所以i,j定义为两边界偏移1
        int x=arr[(l+r)/2];//取x为数组中间位置数
        while(i<j){
            do i++;while(arr[i]<x);
            do j--;while(arr[j]>x);
            //此时i指向的数字大于中轴数字, j指向数字小于中轴数字 交换
            if(i<j){
                int temp=arr[i];
                arr[i]=arr[j];
                arr[j]=temp;
            }
        }
        quickSort(arr,l,j);//退出循环是i==j 递归排序
        quickSort(arr,j+1,r);
    }

7.4 时间复杂度,空间复杂度以及稳定性

  • 时间复杂度最好:O(nlog2n);
  • 时间复杂度最坏:O(n^2);
  • 时间复杂度平均:O(nlog2n);
  • 空间复杂度:O(nlog2n);
  • 稳定性:不稳定;

8.归并排序

8.1 算法思想

将一个大的无序数组有序,我们可以把大的数组分成两个,然后对这两个数组分别进行排序,之后在把这两个数组合并成一个有序的数组。由于两个小的数组都是有序的,所以在合并的时候是很快的。

通过递归的方式将大的数组一直分割,直到数组的大小为 1,此时只有一个元素,那么该数组就是有序的了,之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 …… 直到全部小的数组合并起来。

8.2 动画演示

8.3 代码示例

public static void mergerSort(int[]arr){
        mergerSortInternal(arr,0,arr.length-1);
    }
public static void mergerSortInternal(int[]arr,int l,int r) {//
        if (l >= r) return;
        int mid = (l + r) / 2;//把数组分为两个
        mergerSortInternal(arr, l, mid);//对左边数组递归排序
        mergerSortInternal(arr, mid + 1, r);//对右边数组递归排序
        merge(arr,l,mid,r);//排序完成,进行合并
    }
 public static void merge(int[]arr,int low,int mid,int high){
        int s1=low;//mid左侧数组的头和尾
        int e1=mid;
        int s2=mid+1;//mid右侧数组的头和尾
        int e2=high;
        int[]temp=new int[high-low+1];//定义一个数字,用来合并他们
        int k=0;//指示数组下标
        while(s1<=e1&&s2<=e2){//选出两侧数组最小元素 插入temp数组
            if(arr[s1]<=arr[s2]) temp[k++]=arr[s1++];
            else temp[k++]=arr[s2++];
        }
        while(s1<=e1) temp[k++]=arr[s1++];//如果一侧数组插入完毕,另一侧还有剩余,则全部插入
        while(s2<=e2) temp[k++]=arr[s2++];
        for(int i=0;i<temp.length;i++){//返还到arr数组
            arr[i+low]=temp[i];
        }
    }
}

8.4 时间复杂度,空间复杂度以及稳定性

  • 时间复杂度最好:O(nlog2n);
  • 时间复杂度最坏:O(nlog2n);
  • 时间复杂度平均:O(nlog2n);
  • 空间复杂度:O(n);

到此这篇关于Java数据结构之基于比较的排序算法基本原理及具体实现的文章就介绍到这了,更多相关Java 排序算法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java 排序算法之归并排序

    目录 简单介绍 基本思想 思路分析 代码实现 对代码的一些改进 大数据量耗时测试 复杂度 简单介绍 归并排序(merge sort)是利用 归并 的思想实现的排序方法,该算法采用经典的 分治(divide-and-conquer)策略 : 分(divide):将问题分成一些小的问题,然后递归求解 治(conquer):将分的阶段得到的各答案「修补」在一起 即:分而治之 该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使

  • java 排序算法之希尔算法

    目录 插入排序存在的问题 简单介绍 基本思想 代码实现 大数据量耗时测试 移动法实现希尔排序 移动法-大数据量耗时测试 算法分析 注:学习本篇的前提是要会插入排序,数据结构与算法--排序算法-插入排序 插入排序存在的问题 简单的插入排序可能存在的问题. 如数组 arr = {2,3,4,5,6,1} 这时需要插入的数 1(最小),过程是: 展示的是要移动 1 这个数,的过程,由于在最后,需要前面的所有数都往后移动一位 {2,3,4,5,6,6} {2,3,4,5,5,6} {2,3,4,4,5,

  • java 排序算法之选择排序

    目录 基本介绍 基本思想 思路分析 代码实现 演变过程 优化 算法函数封装 大量数据耗时测试 基本介绍 选择排序(select sorting)也属于内部排序法,是从欲排序的数据中,按指定的规则选出来某个元素,再依规定交换位置后达到排序的目的. 它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾.以此类推,直到所有元素均排序完毕. 基本思想 选择排序(select sorting)也是一种简单直

  • java 排序算法之快速排序

    目录 简单介绍 基本思想 思路分析 代码实现 推导实现 完整实现 大数据量耗时测试 性能分析 简单介绍 快速排序(Quicksort) 是对 冒泡排序的一种改进. 基本思想 快速排序算法通过多次比较和交换来实现排序,其排序流程如下: (1)首先设定一个分界值(基准值),通过该分界值将数组分成左右两部分. (2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边.此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值. (3)然后,左边和右边的数据可以

  • java 排序算法之冒泡排序

    目录 基本介绍 图解冒泡排序算法的过程 代码实现 演变过程 优化 封装算法 大量数据耗时测试 基本介绍 冒泡排序(Bubble Sorting)(时间复杂度为 O(n²))的基本思想:通过对待排序序列 从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水底下的旗袍一样逐渐向上冒. 优化点:因为排序过程中,个元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志判断元素是否进行过交换.从而减

  • Java数据结构之基于比较的排序算法基本原理及具体实现

    目录 1. 七大基于比较的排序-总览 1.1常见基于比较的排序分类 1.2时间复杂度,空间复杂度以及稳定性. 2.直接插入排序 2.1 直接插入排序的基本思想 2.2 直接插入排序动画演示 2.3 代码示例 2.4 时间复杂度,空间复杂度以及稳定性 3. 希尔排序 3.1 算法思想 3.2 图片演示 3.3 代码示例 3.4 时间复杂度,空间复杂度以及稳定性 4.选择排序 4.1 算法思想 4.2 动画演示 4.3 代码示例 4.4 时间复杂度,空间复杂度以及稳定性 5.堆排序 5.1 算法思想

  • Java数据结构之有向图的拓扑排序详解

    目录 前言 拓扑排序介绍 检测有向图中的环 实现思路 API设计 代码实现 基于深度优先的顶点排序 实现思路 API设计 代码实现 拓扑排序 API设计 代码实现 测试验证 前言 在现实生活中,我们经常会同一时间接到很多任务去完成,但是这些任务的完成是有先后次序的.以我们学习java 学科为例,我们需要学习很多知识,但是这些知识在学习的过程中是需要按照先后次序来完成的.从java基础,到 jsp/servlet,到ssm,到springboot等是个循序渐进且有依赖的过程.在学习jsp前要首先掌

  • Java数据结构之图的路径查找算法详解

    目录 前言 算法详解 实现 API设计 代码实现 前言 在实际生活中,地图是我们经常使用的一种工具,通常我们会用它进行导航,输入一个出发城市,输入一个目的地 城市,就可以把路线规划好,而在规划好的这个路线上,会路过很多中间的城市.这类问题翻译成专业问题就是: 从s顶点到v顶点是否存在一条路径?如果存在,请找出这条路径. 例如在上图上查找顶点0到顶点4的路径用红色标识出来,那么我们可以把该路径表示为 0-2-3-4. 如果对图的前置知识不了解,请查看系列文章: [数据结构与算法]图的基础概念和数据

  • Java数据结构之插入排序与希尔排序

    目录 一.正文 1.排序的概念及其运用 1.1排序的概念 1.2排序运用 1.3常见的排序算法 2.插入排序算法的实现 2.1插入排序 二.测试代码 三.结语 一.正文 1.排序的概念及其运用 1.1排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作. 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次 序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[

  • Java实现八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序等

    本文实现了八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 .快速排序.归并排序.堆排序和LST基数排序 首先是EightAlgorithms.java文件,代码如下: import java.util.Arrays; /* * 实现了八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 * 以及快速排序.归并排序.堆排序和LST基数排序 * @author gkh178 */ public class EightAlgorithms { //插入排序:时间复杂度o(n^2) p

  • Python 数据结构之十大经典排序算法一文通关

    目录 1.冒泡排序 算法演示 算法步骤 算法实现 2.选择排序 算法演示 算法步骤 算法实现 3.简单插入排序 算法演示 算法步骤 算法实现 4.希尔排序 算法演示 算法步骤 算法实现 5.归并排序 算法演示 算法步骤 算法实现 6.快速排序 算法演示 算法步骤 算法实现 7.堆排序 算法演示 算法步骤 算法实现 8.计数排序 算法演示 算法步骤 算法实现 9.桶排序 算法演示 算法步骤 算法实现 10.基数排序 算法演示 算法步骤 算法实现 一文搞掂十大经典排序算法 今天整理一下十大经典排序算

  • java中几种常见的排序算法总结

    目录 本节目标: [插入排序] [优化版] [希尔排序] [选择排序] [堆排序]  [冒泡排序] 介绍一个冒泡排序的优化方法:  [快速排序] [归并排序] [正文] [代码简介:]  [排序总结] 本节目标: :分析常见的比较排序算法基本原理及实现 :分析排序算法的性能分析 :分析Java中常用排序方法 1 排序 排序,就是使一串记录,按照其中某个或某些关键字的大小,递增或递减排列的操作. 平时的上下文中,提到排序 通常指排升序. 2 稳定性 两个相同的数据,如果经过排序后,排序算法能保证其

  • Java 超详细讲解十大排序算法面试无忧

    目录 排序算法的稳定性: 一.选择排序 二.冒泡排序 三.插入排序 四.希尔排序 五.堆排序 六.归并排序 七.快速排序 八.鸽巢排序 九.计数排序 十.基数排序 排序算法的稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,如果排序以后,保证这些记录的相对次序保持不变,即在原序列中,a[i]=a[j],且 a[i] 在 a[j] 之前,排序后保证 a[i] 仍在 a[j] 之前,则称这种排序算法是稳定的:否则称为不稳定的. 一.选择排序 每次从待排序的元素中选择最小的元素,依次

  • Java中常用的6种排序算法详细分解

    排序算法很多地方都会用到,近期又重新看了一遍算法,并自己简单地实现了一遍,特此记录下来,为以后复习留点材料. 废话不多说,下面逐一看看经典的排序算法: 1. 选择排序 选择排序的基本思想是遍历数组的过程中,以 i 代表当前需要排序的序号,则需要在剩余的 [i-n-1] 中找出其中的最小值,然后将找到的最小值与 i 指向的值进行交换.因为每一趟确定元素的过程中都会有一个选择最大值的子流程,所以人们形象地称之为选择排序.举个实例来看看: 初始: [38, 17, 16, 16, 7, 31, 39,

  • Java实现几种常见排序算法代码

    稳定度(稳定性)一个排序算法是稳定的,就是当有两个相等记录的关键字R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前. 排序算法分类 常见的有插入(插入排序/希尔排序).交换(冒泡排序/快速排序).选择(选择排序).合并(归并排序)等. 一.插入排序 插入排序(Insertion Sort),它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),

随机推荐