八大排序算法总结Java实现

大二时候学的数据结构,也没有好好实现一下,上个星期课上写选择排序,选择排序是啥来着,课下就整理了一下。

八种排序方法

常见的八种排序方法:冒泡排序  快速排序  插入排序  希尔排序  选择排序  堆排序  归并排序  基数排序

冒泡排序,快速排序都属于交换排序,插入希尔排序都是属于插入排序,希尔排序的基础也是插入排序,选择排序和堆排序都是属于选择排序。

各排序方法详细介绍

冒泡排序

冒泡排序是我最先听到的,感觉是最简单的一个排序方法了,就像这个名字一样,每一次遍历数组,都要把最大(或者最小)的数字放到最后面(或最前面)。

每次遍历数组时,依次比较相邻的数字大小,进行交换或者是不做处理继续向下比较,需要遍历arrays.len-1次。

过程如下图,红色数字代表本次冒泡的数字。

但是加入是一个有序序列 1,2,3,4,5,6,7那不也得依次依次遍历,没有必要,那么就加一个标记,如果某次遍历没有进行数字交换,那么就代表其实现在数组已经是有序的啦,不用再继续遍历,提前退出; 还有一个地方可以改进的就是,如果在第二次遍历的时候,最后一位数字已经是最大的了,没有必要再次比较,同样的最后一次遍历的时候,也只是前面两位数字需要进行比较,没有必要后面的再次遍历一次。

下面分别是未改进和改进后的代码:

    public static void bubbleSort(int[] arrays){

        int len = arrays.length;

        for (int i = 0; i < len-1; i++) {
            for (int j = 0; j < len - 1; j++) {
                if (arrays[j] > arrays[j+1]){
                    swap(arrays,j,j+1);
                }
            }
        }
        
    }
    public static void bubbleSort(int[] arrays){

        int len = arrays.length;
        for (int i = 0; i < len-1; i++) {
            boolean flag = false;
            for (int j = 0; j < len - i - 1; j++) {
                if (arrays[j] > arrays[j+1]){
                    flag = true;
                    swap(arrays,j,j+1);
                }
            }
            if (!flag) break;
        }        
    }

快速排序

快速排序,和冒泡都是属于交换排序,快速排序用二分法不断把一个无序序列分为两半,调整之后成为有序序列,具体方法如下:

1.找基准数字(一般是第一个数字),对数组进行一次迭代交换之后,基准数字找到了自己的最终位置,即 基准数字左半边 都是比它小的,右半边都是比它大的。

2.基准数字左半边可以看作为一个新的无序数组,用步骤 1 的方法迭代一遍,又可以分成两部分,继续用同样的方法迭代,直到无法再分;基准数字右半边也可以看作一个新的无序数组,用步骤1的方法迭代一遍,也可以分为两部分,继续继续,直到无法在分。

3.现在数组就已经有序啦。

迭代交换时:在每次遍历数组的时候,一个指针i一个指针j,i指针负责找到比基准数字大的数儿,而j负责找到比基准数字小的数。每次遍历,基准数字由temp暂放,基准位为空位;先由j开始找,当找到比基准数字小的数字时,将其值赋给空位,之后i开始向后寻找,寻找到大于基准数则将其值赋给空位,之后再继续寻找j赋值给空位,继续寻找i赋值给空位……直到i>=j停止,将temp放入空位。

下图为基准数字找自己的最终位置,这次迭代过后,基准数字左<基准数字,右>基准数字,左右两部分可以进行下一次迭代啦

    public static void quickSort(int[] arrays, int left, int right){
        if (left >= right){
            return;
        }

        int index = getMiddle(arrays, left, right);

        quickSort(arrays,left,index-1);
        quickSort(arrays,index+1,right);
    }

    public static int getMiddle(int[] arrays, int left, int right){

        int index = arrays[left];//基准点
        while(left < right){

            while (arrays[right] >= index && right > left){
                right--;
            }

            arrays[left] = arrays[right];

            while(arrays[left] <= index && right > left){
                left++;
            }
            arrays[right] = arrays[left];

        }

        arrays[right] = index;
        return right;
    }

插入排序

直接插入排序是:按一定的顺序,取无序序列的数字,插入到有序序列中,使得有序序列成为一个新的有序序列,直到无序序列取完为止。

    public static void insertSort(int[] arrays){
        int len = arrays.length;
        for (int i = 1; i < len; i++) {
            int j = i;
            while ( j > 0 && arrays[j] < arrays[j-1]){
                swap(arrays,j,j-1);
                j--;
            }
        }
    }

希尔排序

希尔排序,也是插入排序的一种,在直接插入排序的基础上,进行了序列分组,组内数字进行直接插入排序;分组需要分多次,组内数字量由少到多,排序好之后进入下一次分组。

比如说,第一次分组时,我设置增量=5,那么每隔4个数字取出一个数字,那些取出的数字组为一组,进行排序。第二次分组,我的增量=3,那么每隔2个取一个数字,取出的组为一组,进行排序。第三次增量=1,每隔0个数字取一个数字,也就是整个序列是一组,直接排序。过程如图,相同颜色为一组,组间进行直接插入排序没有给出详细过程,只给出排序后结果。

注意最后的增量必须要=1。增量自己看情况设置。

public static void shellSort(int[] arrays){
    int len = arrays.length;
    int d = len/2;
    while(d >= 1){
        insertSort(arrays,d);
        d /= 2;
    }
}

public static void insertSort(int[] arrays, int d){
    int len = arrays.length;
    for (int i = d; i < len; i++) {
        int j = i;
        while ( j-d >= 0 && arrays[j] < arrays[j-d]){
            swap(arrays,j,j-d);
            j -= d;
        }
    }
}

选择排序

选择排序很简单,就是每次遍历数组找到最小的数字,和前面的数字交换:

第一次遍历找到最小的数字,和第一个数字进行交换,

第二次遍历找到第二小的数字,和第二个数字进行交换,

第三次遍历找到第三小的数字,和第三个数字进行交换……

    public static void choiceSort(int[] arrays){

        int len = arrays.length;
        for (int i = 0; i < len; i++) {
            int min = i;
            for (int j = i+1; j < len; j++) {
                if (arrays[min] > arrays[j]) min = j;
            }
            swap(arrays,i,min);
        }

    }

堆排序

堆排序是一种完全二叉树的结构,升序是大顶堆,降序为小顶堆,小顶堆的根节点数值大小左右子节点的数值,大顶堆的根节点数值大于左右字节点的数值,也就是说小顶堆中的根节点是最小的,而大顶堆的根节点是最大的。再让大顶堆(小顶堆)的根节点不断的输出输出,就可以得到一个有序序列

大顶堆对应的数组结构为:arrays=[9,7,5,6,4,3]

堆排序的大概几个过程:(以升序排序为例)

1.首先将无序序列调整为大顶堆;

2.调整好的大顶堆的根节点同最后一个叶子节点交换,也就是把最大的数字沉到最低下;

3.根节点与叶子节点交换后,树结构被打乱,重新调整数结构,使其成为一个新的大顶堆;

4.重复2.3步骤,一直到所有节点都交换完,也就是成为了一个有序序列

画那个图实在是太难受了,代码就参考了,嘻嘻嘻,其实思路啥的也是从这儿看来的,参考地址

    public static void heapSort(int[] arrays){
        int len = arrays.length;
        for (int i = len/2-1; i >= 0; i--) {//先调整为大顶堆 步骤一
            adjust(arrays,i,len);
        }

        for (int i = len-1; i > 0 ; i--) {//步骤二三,交换调整的过程
            swap(arrays,0,i);
            adjust(arrays,0,i);
        }

    }

    public static void adjust(int[] arrays, int i, int len){
        int temp = arrays[i];
        for (int j = i*2+1; j < len; j = j*2+1) {//这层循环是为了更新子树用的
            if (j+1 < len && arrays[j] < arrays[j+1]){
                j++;//判断是左右子节点谁比较大,那么j就指向谁
            }
            if(arrays[j] > temp){
                arrays[i] = arrays[j];
                i = j;
            }else{
                break;
            }
        }
        arrays[i] = temp;
    }

归并排序

归并排序,先分开,然后合并,把一个无序序列分成单个,之后两个两个的合并到一起,合并的同时进行调整,在合并的时候的调整也是小范围的调整,因为它合并时的序列是将两个有序数列进行合并。

归并有两个步骤,第一个是分开,第二个是调整合并。

分开:一个无序序列,如果要进行排序变成有序序列的话,它的前半部分要是有序的,后半部分也要是有序的,我们可以把它分开两部分,分开的两部分可以看成单独的无序序列,也分成两部分,……直到分到单个数字的时候。

合并:单个数字子序列一定是有序的,那么两个两个进行合并并调整,之后序列个数会少一半,之后再合并调整,合并调整……直到合并为一整个有序序列。

代码实现:

    public static void mergeSort(int[] arrays, int left, int right){

        int mid = (right + left) / 2;
        if (left < right){

            mergeSort(arrays,left,mid);
            mergeSort(arrays,mid+1,right);
            merge(arrays,left,mid,right);
        }


    }

    public static void merge(int[] arrays, int left, int mid , int right){
        int[] temp = new int[right - left +1];
        int i = left;
        int j = mid+1;
        int k = 0;
        while(i <= mid && j <= right){
            if (arrays[i] < arrays[j]){
                temp[k++] = arrays[i++];
            }else{
                temp[k++] = arrays[j++];
            }
        }

        while( i <= mid){
            temp[k++] = arrays[i++];
        }
        while( j <= right){
            temp[k++] = arrays[j++];
        }

        for (int l = 0; l < temp.length; l++) {
            arrays[l+left] = temp[l];
        }

    }

基数排序

基数排序,也说桶排序,10个桶,编号从0-9,来放数字,放数字也不能瞎放吧,它是这么来放的。嘻嘻嘻,我觉得这个排序真的很好玩儿吖,当时上课听了觉得哇好神奇。

从数字的低位到高位,即从个位到十位到百位……,先看在个位的时候,只看每个数字的个位数字,找和个位数字相同的桶序号,放进去,一个桶当然可以放很多个数字啦,也当然有可能某个桶是空的,等着数字都放完了之后,开始往外拿数字,拿也要按顺序拿,先拿0号,然后1号2号……在一个桶里有多个数字的桶里,先拿先进去的数字,就是和队差不多吖,先进去就先出来,取完数字之后,得到一个序列;

之后开始看十位,和看个位步骤一样,只不过是现在忽略掉其他位只看十位,如果是5这种没有十位的那么它的十位就是0,十位放取之后也得到一个序列,是不是这个序列越来越有序啦;那继续进行百位,同前面一样,继续千位,万位……,直到走完最高位,取出后得到一个从小到大的有序序列。

如图啦

 

    public static void radixSort(int[] arrays){
        int len = arrays.length;
        //  找出最大数 并判断有几位
        int max = 0;
        for (int i = 0; i < len; i++) {
            if (arrays[i] > max)
                max = arrays[i];
        }
        int count = 0;
        while( max / 10 != 0 ){
            count++;
            max /= 10;
        }
        count++;

        //初始化10个桶子
        List<List<Integer>> list = new ArrayList<List<Integer>>();

        for (int i = 0; i < 10; i++) {
            List<Integer> tempList = new ArrayList<Integer>();
            list.add(tempList);
        }

        //进行取放
        for (int i = 0; i < count; i++) {

            for (int j = 0; j < len; j++) { // 放入桶中
                int temp = arrays[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
                list.get(temp).add(arrays[j]);
            }

            int index = 0;
            for (int j = 0; j < 10; j++) { // 取出数字
                for (int k = 0; k < list.get(j).size(); k++) {
                    arrays[index++] = list.get(j).get(k);
                }
                list.get(j).clear();
            }

        }
    }

猜你喜欢

转载自blog.csdn.net/qq_38595487/article/details/82777091