常见排序算法

排序算法:

    • 快速排序   O(nlogn)

    • 堆排序   O(nlogn)

    • 归并排序   O(nlogn)

    • 冒泡排序   O(n^2)

    • 选择排序   O(n^2)

    • 插入排序   O(n^2)

    • 希尔排序

1.快速排序:

快速排序原理就是先找一个基准值,将小于基准值的值放在左端,将大于基准值的值放在右端,然后按基准值划分成两部分,分别进行递归快速排序。

java代码:

public class QuickSort {
    public static void quick_sort(int[] a ,int left ,int right){
        if (left < right) {
            int i = left;
            int j = right;
//以最左端的值作为基准值
            int x = a[left];
            while (i<j){
                //从右端开始寻找小于基准的值
                while (i<j && a[j]>x){
                    j--;
                }
                //找到后再进行赋值
                if (i<j)
                    a[i++]=a[j];
                //再从左端开始寻找大于基准值的数
                while(i<j && a[i]<x){
                    i++;
                }
                //找到后进行赋值
                if (i<j)
                    a[j--]=a[i];
            }
            //当i>=j时,将基准值赋值给左端计数变量位置的元素a[i]
            a[i]=x;
  //
            quick_sort(a,left,i-1);//左端进行递归快速排序
            quick_sort(a,i+1,right);//右端进行递归快速排序
        }
    }
    public static void main(String[] args){
    int[] a = {3,4,6,1,2,5};
    quick_sort(a,0,a.length-1);
        for (int i :
                a) {
            System.out.print(i+" ");
        }
    }
}

快速排序平均比较次数为:O(nlogn);所以时间复杂度为O(nlogn);没有用到辅助数组,所以空间复杂度为O(1)

 

2.堆排序:

原理:将要排序的数据构建成堆结构,然后从最后一个非叶子节点处转换成大顶堆或者小顶堆,最后将堆顶值和最后一个节点值进行交换。循环上述操作,将除了最后一个节点的剩余节点进行再排序。以此类推。

代码:

public class HeapSort {
    /**
     * 排序主函数
     * @param a
     */
    public static void sort(int []a){
        //首先从最后一个非叶子节点处构建堆结构
        for(int i = a.length/2-1;i>=0;i--){
            changeHeap(a,i,a.length);
        }
        //交换数据,前提是堆已经转换为大顶堆
        for (int t = a.length-1;t>0;t--){
            //进行交换
            swap(a,0,t);
            //对交换后的剩余数据从0开始进行再调整,使其成为大顶堆
            changeHeap(a,0,t);
        }
    }

    /**
     * 构建堆结构和转换大顶堆或小顶堆
     * @param a
     * @param i
     * @param length
     */
    public static void changeHeap(int []a ,int i,int length){
        //获取当前元素值
        int temp = a[i];
        //与左右两子节点进行比较
        for (int j=2*i+1;j<length;j=2*j+1){
            //如果右节点大于左节点,则j会指向值大的节点
            if (j+1<length && a[j]<a[j+1]){
                j++;
            }
            //判断子节点中最大值和当前节点值的大小,若大于则赋值给当前节点
            if (a[j] > temp){
                //此时a[j]并未改变
                a[i]=a[j];
                i=j;
            }else
                break;
        }
        //在此处会对原来的a[j]进行赋值
        a[i]=temp;
    }

    /**
     * 进行元素交换函数
     * @param a
     * @param i
     * @param j
     */
    public static void swap(int []a ,int i,int j){
        int temp = a[i];
        a[i]=a[j];
        a[j]=temp;
    }

    public static void main(String[] args){
        int []a={2,6,3,0,1};
        sort(a);
        for (int i :
                a) {
            System.out.print(i+" ");
        }
    }
}

      堆排序是一种选择排序,堆结构是完全二叉树结构,所以每次循环将进行log2(n-i)次比较所以堆排序的时间复杂度为O(nlogn)

       堆排序适合进行百万级别的数据排序,而快速排序和归并排序需要进行递归操作,所以在数据量很大时会出现堆栈溢出风险。

3.归并排序:

原理:通过递归方法将所有数据分成单独的元素,然后再进行两两合并排序,最后得到有序数列。

 

代码:

 

public class MergeSort {   
/**
     * 排序主方法
     * @param a
     * @param left
     * @param right
     * @param temp
     */
    public static void sort(int []a ,int left,int right,int []temp){
        if (left<right){
            int mid = (left+right)/2;//求中间值
            sort(a,left,mid,temp);//左半部分递归排序
            sort(a,mid+1,right,temp);//右半部分递归排序
            merge(a,left,mid,right,temp);//将两部分归并到一起
        }
    }

    /**
     * 归并方法
     * @param a
     * @param left
     * @param mid
     * @param right
     * @param temp
     */
    public static void merge(int []a,int left,int mid,int right,int []temp) {
        int i = left;
        int j = mid + 1;
        int t = 0;
 //判断是否越界
        while (i <= mid && j <= right){
            if (a[i] <= a[j]) {
                temp[t++] = a[i++];
            } else {
                temp[t++] = a[j++];
            }
        }
 //执行到此处时,肯定有一部分已经全部排完
        while (i <= mid) {//将左边剩余元素填充进temp中
            temp[t++] = a[i++];
        }
        while (j <= right) {//将右序列剩余元素填充进temp中
            temp[t++] = a[j++];
        }
        t = 0;
        //将temp中的元素全部拷贝到原数组中
        while (left <= right) {
            a[left++] = temp[t++];
        }
    }
    //主函数
    public static void main(String[] args){
        int a[] = {4,1,5,2,3};
        //准备一个与原程序一样长度的副本数组
        int []temp = new int[a.length];
        sort(a,0,a.length-1,temp);
        for (int i :
                a) {
            System.out.print(i+" ");
        }
    }
}

 

归并排序是稳定排序,java自带的排序算法就是归并排序的优化版本,其最好最坏平均的时间复杂度均为O(nlogn)。因为用到了一个辅助数组所以空间复杂度为O(n)。

在递归过程中先对短的那段进行递归,可以使递归深度最小。

 

4.简单选择排序:

原理:在所有数列中选出最小值,并且与第一个元素交换,然后从剩余的元素中再选择一个最小值并与第二个元素交换,依次循环直至结尾。

 

代码:

 public class ChooseSort {
/**
     * 选择排序主方法
     * @param a
     */
    public static void sort(int []a){
        int min=0;
        for (int i =0 ;i < a.length;i++){
            min=i;
            for (int k = i+1;k<a.length;k++){
                if (a[k] < a[min]){
                    min=k;
                }
            }
//如果最小值发生了改变则进行交换
            if (min != i){
                int temp =a[i];
                a[i]=a[min];
                a[min]=temp;
            }
        }
    }
    public static void main(String[] args){
        int a[]={2,4,1,5,3};
        sort(a);
        for (int i :
                a) {
            System.out.print(i+" ");
        }
    }
}

选择排序在排序过程中每次循环均需要比较n-i次,所以其时间复杂度为:O(n^2),空间复杂度为O(1)

 

5.冒泡排序:

原理:从数列头进行两两比较,然后经过多轮比较,将最大值交换到最后端,然后再进行多伦比较将次大数交换到最大值后,以此循环。

 

代码:

public class BubbleSort {
    public static void sort(int []a){
        int temp =0;
        //需要冒的泡泡数
        for (int i = 0;i < a.length-1;i++){
            //每个泡泡需要比较的次数
            for (int k = 0;k < a.length-1-i;k++){
                if (a[k]>a[k+1]){
                  temp =a[k];
                  a[k]=a[k+1];
                  a[k+1]=temp;
                }
            }
        }
    }
    public static void main(String[] args){
        int []a={2,1,5,4,3,6};
        sort(a);
        for (int i :
                a) {
            System.out.print(i+" ");
        }
    }
}

冒泡排序在排序过程中每次循环都需要进行n-i次比较,所以时间复杂度和选择排序一样都是O(n^2)。

 

6.插入排序(步长为1):

       原理:因为步长为1,所以从第二个元素开始与前一个元素进行比较两两比较,若小于前面的元素则与其交换,然后在与前一个元素比较交换,直至没有前一个元素,然后再从第三个元素开始进行循环比较。

       希尔排序也是插入排序的一种,只不过其步长不为1,它的步长在排完一次后减小1,最后减小为1,然后进行步长为1的插入排序。

 

代码:

public class InsertSort {
/**
     *
     * @param a
     */
    public static void sort(int []a){
        for (int i =1 ; i < a.length ; i++){
//这里可以使用fori()循环替代
            int j =i;
            while (j > 0 && a[j]<a[j-1]){
                int temp=a[j];
                a[j]=a[j-1];
                a[j-1]=temp;
                j--;
            }
        }
    }
    public static void main(String[] args){
        int a[]={5,4,3,2,1};
        sort(a);
        for (int i :
                a) {
            System.out.print(i+ " ");
        }
    }
}

插入排序,选择排序和冒泡排序的时间复杂度均为O(n^2).

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值