八大排序算法源码及分析

很久之前自己为了实习招聘就编程并梳理了八大排序算法,但一直没有时间写博客,今天趁着将要下班的闲暇之余,写篇博客来再梳理下
八大排序:
八大排序有分成了五类排序:
(1)交换排序:冒泡排序,快速排序
(2)选择排序:简单选择排序,堆排序
(3)插入排序:直接插入排序,希尔排序
(4)归并排序
(5)基数排序

一、冒泡排序(时间复杂度为O(n^2))
先来看下我们第一种排序方法,也是考的最常用的冒泡排序。
冒泡排序的思路:
(1)先从左边开始两两比较,如果逆序则交换,这样一轮就将最大的或者最小的分割到最右边或者最左边
(2)将得到的最大的和最小的值分隔开,对剩下的值在进行排序,直到最剩下一个数则结束
ps:就是两层for循环,外层控制每轮,内层控制每轮每相邻两个的比较,注意每轮后数组的个数要减少一个
代码:

public static int[] BubbleSort(int[] array){
        int temp;
        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]){
                    temp = array[j];
                    array[j]=array[j+1];
                    array[j+1]=temp;
                }
            }
        }
        return array;
    }

二、快速排序(时间复杂度为O(N*logN))
快速排序也是我们常考的一种排序方法!
快速排序的思路:
选取最左边的值为标杆,从左右同时开始,右边找比标杆小的值,左边找比标杆大的值,找到后即交换,然后继续找,直到左右两边的指针相遇则结束,然后将最左边的标杆和相遇的地方的值交换,这样再对左右的进行递归快排(先从右边找,在从左找)
2856194->2156894->1256894
再对两边分别进行快排,直到有序

代码:

public static void QuickSort(int[] array,int left,int right){
        if(left>right)
            return;
        int i=left;
        int j=right;
        int temp = array[i];
        while(i<j){
            while(temp<array[j]&&i<j){//找右边比哨兵小的值
                j--;
            }
            while(temp>=array[i]&&i<j){//找左边比哨兵大的值
                i++;
            }
            if(i<j){//找到后满足条件则交换
                int t =array[j];
                array[j]=array[i];
                array[i]=t;
            }
        }
        array[left]=array[j];//每次交换完了,再交换哨兵
        array[j]=temp;
        QuickSort(array,left,j-1);//递归左半数组
        QuickSort(array,j+1,right);//递归右半数组
    }

注意:一定是先从右边开始找比标杆小的值,再从左边开始找比标杆大的值

三、简单选择排序(时间复杂度为O(n^2))
思路:从数组中找出最小值,记下坐标,然后跟第一个进行交换,然后把第一个剥离,对然后对剩下的数组找出最小的,再跟第二个交换,直到只剩下一个值即结束
ps:一定要记下坐标,这个较为简单
代码:

public static int[] SelectionSort(int[] array){
        int temp;
        for (int i = 0; i < array.length-1; i++) {
            int k=i;
            for (int j = i+1; j <array.length; j++) {
                if(array[k]>array[j]){
                    k=j;
                }
            }
            temp=array[i];
            array[i]=array[k];
            array[k]=temp;
        }
        return array;
    }

四、堆排序(时间复杂度为O(N*logN))
堆排序就是将数组形成一个大根堆,堆顶是数值最大的,然后再跟最末尾的最小值进行交换,剥离出最后的那个最大值,然后再对堆进行调整为一个大根堆,在继续迭代下去。
ps:这个排序比较难,如何构建大根堆,以及堆如何排序都需要我们进行多思考
代码:

import java.util.Arrays;

/**
 * 堆排序demo
 */
public class HeapSort {
    public static void main(String []args){
        int []arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int []arr){
        //1.构建大顶堆
        for(int i=arr.length/2-1;i>=0;i--){
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr,i,arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for(int j=arr.length-1;j>0;j--){
            swap(arr,0,j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr,0,j);//重新对堆进行调整
        }

    }

    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
     */
    public static void adjustHeap(int []arr,int i,int length){
        int temp = arr[i];//先取出当前元素i
        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }

    /**
     * 交换元素
     */
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

五、直接插入排序(时间复杂度为O(n^2))
思路:第二个跟第一个比较,逆序则交换,第三个跟第二个比较,逆序则再跟第一个比较,逆序就插入到第一个之前,然后之后的分别跟前面的有序数组依次比较,直到找到比自己小的,则插入到他后面
ps:其实也是两层for循环,外层控制第N个进行插入比较,内层控制跟第N-1个进行…第1个比较,找到自己的位置,然后交换,因为是数组涉及到后面的移动,我们默认如果小于则交换,然后再跟后面的比较
代码:

public static int[] InsertSort(int[] array){
        int temp;
        for (int i = 1; i < array.length; i++) {
            int k=i;//注意用一个变量来记第几个
            for (int j = i-1; j >= 0; j--) {
                if (array[k] < array[j]) {
                    temp=array[k];
                    array[k]=array[j];
                    array[j]=temp;
                    k--;
                }
            }
        }
        return array;
    }

六、希尔排序(时间复杂度为O(N*logN))
希尔排序是插入排序的改良版,并不是每个都比较
思路:
(1)步长设为数组长的一半,然后第n/2+1的值跟第一个比较,如果逆序则交换,如果交换,然后再跟前面步长的值比较(如果没有就下一个),一直比较完一轮,
(2)然后步长变为原来的一半,然后继续比较,直到步长变为1比较完,就结束
ps:步长的选择很重要,一般开始的步长都等于数组长度的一半
代码:

public static void ShellSort(int[] array){
        if(array == null || array.length <= 1){
            return;
        }
        int num=array.length/2;
        while(num>=1){
            for (int i =num;i<array.length; i++) {
                for (int j =i-num; j>=0; j-=num) {
                    if(array[j]>array[j+num]){
                        int temp=array[j];
                        array[j]=array[j+num];
                        array[j+num]=temp;
                    }
                }
            }
            num/=2;
        }
    }

注意:双层循环,可以从后面开始,也可以从步长地方开始比较

七、归并排序(时间复杂度为O(N*logN))
思路:
(1)先让数组中的每个数成为单独的区间,然后相邻两个区间进行比较排序后并合并
(2)然后从1个变成2个,2个变成4个,直到变成一个区间

代码:

public static int[] MergeSort(int[] array,int left,int right) {
        if (left < right) {
            int mid = (left + right)/2;
            MergeSort(array, left, mid);
            MergeSort(array, mid + 1, right);
            Merge(array, left, mid, right);
        }
        return array;
    }
    public static int[] Merge(int[] array,int left,int mid,int right){//合并
        int[] a =new int[right-left+1];
        int i=left;
        int j=mid+1;
        int k=0;
        while(i<=mid&&j<=right){
            if(array[i]<array[j]){
                a[k++]=array[i++];
            }else{
                a[k++]=array[j++];
            }
        }
        while(i<=mid){
            a[k++]=array[i++];
        }
        while(j<=right){
            a[k++]=array[j++];
        }
        for (int l = 0; l<a.length;l++,k++) {
            array[l+left]=a[l];
        }
        return array;
    }

八、基数排序(时间复杂度为O(N))
比较复杂,
思路:将所有数补成相同位数的数,往前面补0,然后找0-9个桶,先开始对个位进行排序,然后把数取出(0-9),然后从序列中对十位排序,然后把数从桶拿出,再对百位排序,直到排到最高位数则结束
代码:

/*
 * 获取数组a中最大值
 *
 * 参数说明:
 *     a -- 数组
 *     n -- 数组长度
 */
int get_max(int a[], int n)
{
    int i, max;

    max = a[0];
    for (i = 1; i < n; i++)
        if (a[i] > max)
            max = a[i];
    return max;
}

/*
 * 对数组按照"某个位数"进行排序(桶排序)
 *
 * 参数说明:
 *     a -- 数组
 *     n -- 数组长度
 *     exp -- 指数。对数组a按照该指数进行排序。
 *
 * 例如,对于数组a={50, 3, 542, 745, 2014, 154, 63, 616};
 *    (01) 当exp=1表示按照"个位"对数组a进行排序
 *    (02) 当exp=10表示按照"十位"对数组a进行排序
 *    (03) 当exp=100表示按照"百位"对数组a进行排序
 *    ...
 */
void count_sort(int a[], int n, int exp)
{
    int output[n];             // 存储"被排序数据"的临时数组
    int i, buckets[10] = {0};

    // 将数据出现的次数存储在buckets[]中
    for (i = 0; i < n; i++)
        buckets[ (a[i]/exp)%10 ]++;

    // 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
    for (i = 1; i < 10; i++)
        buckets[i] += buckets[i - 1];

    // 将数据存储到临时数组output[]中
    for (i = n - 1; i >= 0; i--)
    {
        output[buckets[ (a[i]/exp)%10 ] - 1] = a[i];
        buckets[ (a[i]/exp)%10 ]--;
    }

    // 将排序好的数据赋值给a[]
    for (i = 0; i < n; i++)
        a[i] = output[i];
}

/*
 * 基数排序
 *
 * 参数说明:
 *     a -- 数组
 *     n -- 数组长度
 */
void radix_sort(int a[], int n)
{
    int exp;    // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;...
    int max = get_max(a, n);    // 数组a中的最大值

    // 从个位开始,对数组a按"指数"进行排序
    for (exp = 1; max/exp > 0; exp *= 10)
        count_sort(a, n, exp);
}

又梳理了一遍,顿时清晰多了,排序算法还是非常重要的,要经常熟悉

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值