前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >常见的五种排序算法

常见的五种排序算法

作者头像
Yif
发布2019-12-26 15:26:30
4720
发布2019-12-26 15:26:30
举报
文章被收录于专栏:Android 进阶Android 进阶
undefined
undefined

冒泡排序

冒泡排序简介

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。

如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。大的数往下沉,小的数往下冒。 稳定、原地排序,最好时间复杂度为O(n),最坏与平均时间复杂度为O(n2)。

原地排序简介

原地排序指的是空间复杂度为O(1)的排序算法。

稳定排序简介

稳定性指的是:如果待排序列中存在值相等的元素,经过排序之后,相等元素的先后顺序没有改变 比如我们有一组数据 2,9,3,4,8,3,按照大小排序之后就是 2,3,3,4,8,9。 这组数据里有两个 3。经过某种排序算法排序之后,如果两个 3 的前后顺序没有改变,那我们就把这种排序算法叫作稳定的排序算法;如果前后顺序发生变化,那对应的排序算法就叫作不稳定的排序算法。

冒泡代码代码实现

代码语言:javascript
复制
public class BubbleSort {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] data=new int[]{2,3,5,4,7,6,9,10,12,11,23,22,18,8};
        bubbleSort(data);
        for(int i=0;i<data.length;i++){
            System.out.print(data[i]+" ");
        }
    }
 
    public static void bubbleSort(int[] data){
        if(data.length<=1) return;
        for(int i=0;i<data.length;i++){
            //提前退出冒泡循环的标志位
            boolean flag = false;
            for(int j=0;j<data.length-1-i;++j){
                if(data[j]>data[j+1]){
                    int temp = data[j];
                    data[j] = data[j+1];
                    data[j+1] = temp;
                    flag = true;//有数据交换
                }
            }
            if(!flag) break;//没有数据交换,退出
        }
    }
}

插入排序

插入排序简介

我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。 稳定、原地排序,最好时间复杂度为O(n),最坏与平均时间复杂度为O(n2)。

代码实现

代码语言:javascript
复制
public class InsertSort {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] data=new int[]{4,3,2,1,5};
  insertSort(data);
        for(int i=0;i<data.length;i++){
            System.out.print(data[i]+" ");
        }
    }
 
    public static void insertSort(int[] data){
        if(data.length<=1) return;
        for(int i=1;i<data.length;++i){
            int value = data[i];
            int j=i-1;
            for(;j>=0;--j){
                if(data[j]>value){
                    data[j+1] = data[j];
                }else {
                    break;
                }
            }
            data[j+1]=value;
        }
    }
}

选择排序

选择排序简介

选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。 不稳定、原地排序,最好时间复杂度为O(n2),最坏与平均时间复杂度为O(n2)。 选择排序是一种不稳定的排序算法。选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性。

代码实现

代码语言:javascript
复制
public class SelectSort {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] data=new int[]{2,3,5,4,7,6,9,10,12,11,23,22,18,8};
        selectSort(data);
        for(int i=0;i<data.length;i++){
            System.out.print(data[i]+" ");
        }
    }
    public static void selectSort(int[] data){
        if(data.length<=1) return;
        for(int i=0;i<data.length-1;++i){
            int minIndex=i;
            for(int j=i+1;j<data.length;++j){
                if(data[j]<data[minIndex]){
                    minIndex = j;
                }
            }
            int temp = data[i];
            data[i] = data[minIndex];
            data[minIndex] = temp;
        }
    }
}

归并排序

归并排序简介

如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。 归并排序用的是一种分治思想,它不是原地排序,即空间复杂度不是O(1),空间复杂度为O(n),不管最好还是最坏或平均,它的时间复杂度都为O(nlogn),是一个稳定的算法。

代码实现

代码语言:javascript
复制
/**
 * 归并排序
 * 将需要排序的数组从中间分成两部分,将这两部分分别排序,再将排序好的两部分合并在一起
 * 时间复杂度O(nlogn)
 * 空间复杂度(n)
 *
 */
public class MergeSort {
 
    public static void main(String[] args) {
        int[] a= new int[]{1,2,4,3,45,34,23,6,7};
        mergeSort(a, 9);
        for(int i=0;i<a.length;i++){
            System.out.print(a[i]+" ");
        }
    }
    private static void mergeSort(int[] a,int n){
        mergeIntaily(a, 0, n-1);
    }
 
    private static void mergeIntaily(int[] a,int p,int r){
        if(p>=r) return;
        int q = p+(r-p)/2;
        mergeIntaily(a, p, q);
        mergeIntaily(a, q+1, r);
        merge(a, p, q, r);
    }
 
    private static void merge(int[] a,int p,int q,int r){
        int i=p;
        int j = q+1;
        int k=0;
        //申请一个数组,大小与a[p..r]相同
        int[] tmp = new int[r-p+1];
        //用两个游标,一个i指向a[p..q]中的第一个元素,一个j指向a[q+1..r]的第一个元素,
        //如果a[i]<=a[j],就将a[i]的值放进tmp数组中,i后移,否则将a[j]元素放进tmp数组中,j后移
        while(i<=q&&j<=r){
            if(a[i]<=a[j]){
                tmp[k++] = a[i++];
            }else {
                tmp[k++] = a[j++];
            }
        }
 
        //判断那部分数组剩余
        int start = i;
        int end = q;
        if(j<=r){
            start = j;
            end = r;
        }
 
        //将剩余数组放进tmp中
        while(start<=end){
            tmp[k++] = a[start++];
        }
 
        //在将tmp数组中的元素放回原数组中
        for(i=0;i<=r-p;i++){
            a[p+i] = tmp[i];
        }
    }
}

merge(A[p…r], A[p…q], A[q+1…r]) 这个函数的作用就是,将已经有序的 A[p…q]A[q+1…r] 合并成一个有序的数组,并且放入 A[p…r]。 申请一个临时数组 tmp,大小与 A[p…r] 相同。我们用两个游标 i 和 j,分别指向 A[p…q] 和 A[q+1…r] 的第一个元素。比较这两个元素 A[i] 和 A[j],如果 A[i]<=A[j],我们就把 A[i] 放入到临时数组 tmp,并且 i 后移一位,否则将 A[j] 放入到数组 tmp,j 后移一位。 继续上述比较过程,直到其中一个子数组中的所有数据都放入临时数组中,再把另一个数组中的数据依次加入到临时数组的末尾,这个时候,临时数组中存储的就是两个子数组合并之后的结果了。最后再把临时数组 tmp 中的数据拷贝到原数组 A[p…r] 中。

快速排序

快速排序简介

要排序的数组下标从p到r的一组数据,选取从p到r之间的任意一个数据作为pivot(分区点),也是分治思想。 快速排序是原地排序,不稳定,最坏时间复杂度为O(n2),最好和平均时间复杂度都为O(nlogn),空间复杂度为O(1)。

代码实现

代码语言:javascript
复制
public class QuickSort {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] a= new int[]{1,2,4,3,45,34,23,6,7};
        quickSort(a, 9);
        for(int i=0;i&lt;a.length;i++){
            System.out.print(a[i]+&quot; &quot;);
        }
    }
 
    private static void quickSort(int[] a,int n){
        quickSortIntaily(a, 0, n-1);
    }
 
    private static void quickSortIntaily(int[] a,int p,int r){
        if(p&gt;=r) return; //递归终止条件
 
        int q= partition(a, p, r);
 
        quickSortIntaily(a, p, q-1);
        quickSortIntaily(a, q+1, r);
    }
 
    //分区函数,随机选择一个元素作为pivot,一般选取从p到r的最后一个元素,然后对a[p..r]分区,返回pivot下标
    private static int partition(int[] a,int p,int r){
        int pivot = a[r];
        int i=p;
        for(int j=p;j&lt;r;++j){
            if(a[j]&lt;pivot){
                int tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
                ++i;
            }
        }
        int tmp = a[i];
        a[i] = a[r];
        a[r] = tmp;
        return i;
    }
}

遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的。

归并与快排区别

  • 归并排序的处理过程是由下到上的,先处理子问题,然后再合并。而快排处理过程是由上到下的,先分区,然后再处理子问题。归并排序虽然是稳定的、时间复杂度为 O(nlogn) 的排序算法,但是它是非原地排序算法;
  • 归并排序和快速排序是两种稍微复杂的排序算法,都是使用分治的思想,代码都通过递归来实现,过程相似;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年8月4日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 冒泡排序
    • 冒泡排序简介
      • 原地排序简介
        • 稳定排序简介
          • 冒泡代码代码实现
          • 插入排序
            • 插入排序简介
              • 代码实现
              • 选择排序
                • 选择排序简介
                  • 代码实现
                  • 归并排序
                    • 归并排序简介
                      • 代码实现
                      • 快速排序
                        • 快速排序简介
                          • 代码实现
                          • 归并与快排区别
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档