数据结构-排序算法实现

数据结构-排序算法实现

排序

本博客主要展示插入排序、选择排序、冒泡排序、希尔排序、归并排序、快速排序的Java代码实现,以及他们稳定性的测试。

代码

package nwnu.day11.sort;

import java.util.HashSet;
import java.util.Set;
/**
 * 本节主要编写各种排序方法:
 *          1. 插入排序: 稳定排序  O(n^2)
 *                       将待排序数值与已排序的数值相对比,小于向前移动,大于直接插入并且本次循环结束
 *                       优化方法1:将有序部分利用二分查找算法,找到待插入数据的插入位置。
 *                       优化方法2:
 *
 *          2. 选择排序: 不稳定排序  O(n^2)
 *                       每次选取最小值,放在待排序部分的最前面
 *
 *          3. 冒泡排序:稳定排序     O(n^2)
 *                      从头开始,每次交换两个元素位置,直至待排序部分的最大元素移动到最后方。
 *                      排序时,定义标记负flag, 在单次循环没有发生数据交换时,说明数据有序,直接结束排序操作。
 *
 *          4. 希尔排序: 不稳定排序 O(n^(1.3-2)) > O(nlog(n))
 *                      又称缩小增量排序(gap)  gap = gap / 2
 *                      每次将gap的元素分为一组进行排序
 *
 *          5. 归并排序 稳定排序算法  时间复杂度:O(nlog(n))   空间复杂度:O(n)
 *                      分解 -> 解决 -> 合并
 *
 *
 *          6. 快速排序 不稳定排序算法 时间复杂度O(nlog(n))
 *                     快速排序从两边开始,与中间数据进行比较,
 *                     当first>mid && last<mid时,交换first索引和last索引位置的元素
 *

 *什么是稳定排序,什么是不稳定排序
 *      在待排序序列中的相同大小元素,在排序后相对位置未发生改变的是稳定排序,否则是不稳定排序。
 *      假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的 *相对次序保持不变*
 *      即在原序列中,A1=A2,且A1在A2之前,而在排序后的序列中,A1仍在A2之前,则称这种排序算法是稳定的;
 *      否则称为不稳定的。
 */

public class SortMain {

    public static int MAXNUMBER = 100;      //测试用例中最大数的数值为100
    public static int MINNUMBER = 0;      //测试用例中最大数的数值为0

    public static void main(String[] args) {

        //插入排序测试
        int[] arr = new int[]{3,2,1,4,5,4,7,9,8};
        System.out.println("++++++++++++++++  插入排序测试  +++++++++++++++++");
        int[] index = generateIndex(arr);
        //show(index);
        insertSort(arr,index);

        //选择排序测试
        int[] arr2 = new int[]{9,8,7,6,5,3,2,1,1,9,8};
        System.out.println("++++++++++++++++  选择排序测试  +++++++++++++++++");
        index = generateIndex(arr2);
        //show(index);
        selectSort(arr2,index);

        //冒泡排序
        int[] arr3 = new int[]{3,2,1,1,2,3};
        System.out.println("++++++++++++++++  冒泡排序测试  +++++++++++++++++");
        index = generateIndex(arr3);
        bubbleSort(arr3,index);

        //希尔排序
        int[] arr4 = new int[]{9,8,7,6,5,4,3,4,7,8,9,9,8,8};
        System.out.println("++++++++++++++++  希尔排序测试  +++++++++++++++++");
        index = generateIndex(arr4);
        shellSort(arr4,index);

        //测试merge方法 (合并两个已经有序的数组)
        int[] arr5 = new int[]{1,3,5,7,2,4,6,8};
        merge(arr5, 0,3, 7);
        show(arr5);
        System.out.println();

        //归并排序
        int[] arr6 = new int[]{1,3,5,7,2,4,6,8};
        System.out.println("++++++++++++++++  归并排序测试  +++++++++++++++++");
        mergeSort(arr6,0,7);
        show(arr6);
        System.out.println();

        //快速排序 - 1
        int[] arr7 = new int[]{9,8,7,6,5,4,3,2,1};
        int[] arr8 = new int[]{1,3,8,1,5,1,6,7,9};
        System.out.println("++++++++++++++++  快速排序测试  +++++++++++++++++");
        index = generateIndex(arr7);
        quickSort(arr7,0,8,index);
        show(arr7);
        System.out.println();
        show(index);
        System.out.println();

        //快速排序 - 2
        System.out.println("++++++++++++++++  快速排序测试  +++++++++++++++++");
        index = generateIndex(arr8);
        quickSort(arr8,0,8,index);
        show(arr8);
        System.out.println();
        show(index);
        System.out.println();
    }


    /**
     * 插入排序
     * @param arr 待排序数组
     */
    public static void insertSort(int[] arr, int[] index){
        for(int i=0; i<arr.length-1; i++){
            for(int j=i+1; j>0; j--){
                if(arr[j-1] > arr[j]){      //相同元素未交换,是稳定排序
                    swap(arr,j-1,j);
                    swap(index,j-1,j);
                }else{
                    break;
                }
            }
            System.out.print("第"+i+"次排序结果 = ");
            show(arr);
            System.out.println();
        }
        System.out.print(" 结果相对位置 = ");
        show(index);
        System.out.println();
    }

    /**
     * 选择排序
     * @param arr 待排序数组
     * @param index 数组重复元素顺序值
     */
    public static void selectSort(int[] arr, int[] index){
        for(int i=0; i<arr.length-1; i++){
            for(int j=i; j<arr.length; j++){
                if(arr[i] > arr[j]){
                    swap(arr,i,j);
                    swap(index,i,j);
                }
            }
            System.out.print("第"+i+"次排序结果 = ");
            show(arr);
            System.out.println();
        }
        System.out.print(" 结果相对位置 = ");
        show(index);
        System.out.println();
    }

    /**
     * 冒牌排序
     * @param arr 待排序数组
     * @param index 数组重复元素顺序值
     */
    public static void bubbleSort(int[] arr, int[] index){

        //从后向前,每个大循环去除一个最大值
        for(int i=0; i<arr.length-1; i++){
            int flag = 0;
            for(int j=0; j<arr.length-i-1; j++){
                if(arr[j] > arr[j+1]){
                    swap(arr,j+1,j);
                    swap(index,j+1,j);
                    flag = 1;
                }
            }
            System.out.print("第"+i+"次排序结果 = ");
            show(arr);
            System.out.println();
            if(flag == 0){      //如果一次遍历没有发生数据交换,那么说明数据已经有序,此时不需要再进行后续操作
                break;
            }
        }
        System.out.print(" 结果相对位置 = ");
        show(index);
        System.out.println();
    }

    /**
     * 希尔排序
      * @param arr 待排序数组
     * @param index 数组重复元素顺序值
     */
    public static void shellSort(int[] arr, int[] index){
        int gap = arr.length / 2;   //由于gap特殊规定,这里就取长度的一半作为其排序间隔
        while(true) {
            for (int i = 0; i < gap; i++) {     //循环所有分组
                for(int j = i; (j+gap)<arr.length; j = j+gap){    //对于每一个分组内,进行插入排序
                    for(int k = j+gap; k>i; k = k-gap) {          //k>i => k-gap >= i
                        if (arr[k-gap] > arr[k]) {
                            swap(arr, k-gap, k);
                            swap(index, k-gap, k);
                        }
                    }
                }
            }
            System.out.print("gap = "+gap+"时, 排序结果 = ");
            show(arr);
            System.out.println();

            if(gap == 1){   //当间隔变为1时排序结束
                System.out.print("        结果相对位置 = ");
                show(index);
                System.out.println();
                break;
            }else{
                gap = gap / 2;
            }
        }

    }

    /**
     * 归并排序-递归
     * @param arr 待排序数组
     */
    public static void mergeSort(int[] arr, int l, int r){
        if(l >= r){
            return;
        }

        int mid = (l+r)/2;
        mergeSort(arr,l,mid);
        mergeSort(arr,mid+1,r);

        if(arr[mid] > arr[mid+1]) {     //左边有序部分最大的大于右边最小的,进行合并, 提高效率
            merge(arr, l, mid, r);
        }
    }

    /**
     * 合并两个有序数组
     * @param a1 数组1
     * @param a2 数组2
     * @return   返回一个长度为两数组之和的新数组
     */
    public static int[] merge(int[] a1, int[] a2){
        int len1 = a1.length;
        int len2 = a2.length;
        int[] newArr = new int[len1+len2];
        int index1 = 0;  //a数组的索引
        int index2 = 0;  //b数组的索引
        for(int i=0; i<len1+len2; i++){
            if(a1[index1] < a2[index2]){    //如果a1元素小,插入到新数组中
                newArr[i] = a1[index1];
                index1++;
            }else{
                newArr[i] = a2[index2];
                index2++;
            }
            if(index1 >= len1 && index2 < len2){
                for(int j=index2; j<len2; j++){
                    newArr[++i] = a2[j];
                }
                break;
            }
            if(index2 >= len2 && index1 < len1){
                for(int j=index1; j<len1; j++){
                    newArr[++i] = a1[j];
                }
                break;
            }
        }
        return newArr;
    }

    /**
     * 合并两个有序的部分为一个有序整体
     * @param arr 待排序数组
     * @param l 左侧待排序的开始索引
     * @param mid 中间索引
     * @param r 右侧待排序的结束索引 (不是数组长度)
     */
    public static void merge(int[] arr, int l, int mid, int r){
        int i = l;
        int j = mid+1;
        int[] newArr = new int[arr.length];
        for(int k=l; k<=r; k++){        //这里使用 <=
            if(arr[i] < arr[j]){    //左边小,先进
                newArr[k] = arr[i];
                i++;
            }else{
                newArr[k] = arr[j];
                j++;
            }
            if(i>mid && j <= r){    //前半部分都排序完成,将后半部分全部存入newArr
                for(int p=j; j<=r; j++){
                    newArr[++k] = arr[p];       //由于循环未结束,newArr[k] = arr[j];赋值后,k值未加一,此处需要先加加
                }
                break;
            }
            if(i<=mid && j>r){
                for(int p=i; i<=mid; i++){
                    newArr[++k] = arr[p];       //同上
                }
                break;
            }
        }
        for(int k=l; k<=r; k++){
            arr[k] = newArr[k];
        }
    }

    /**
     * 快速排序
     * @param arr 待排序数组
     * @param l 左侧索引
     * @param r 右侧索引
     */
    public static void quickSort(int[] arr, int l, int r, int[] index){
        if(l>=r){
            return;
        }
        int mid = (l+r)/2;
        int midNum = arr[mid];
        int i = 0;
        int j = r;
        while(true){
            while(i<mid  && arr[i] < midNum)
                i++;
            while(j>mid && arr[j] > midNum)
                j--;
            if(i >= j){
                break;
            }
            swap(arr,i,j);
            swap(index,i,j);
            i++;
            j--;
        }
        System.out.print("快速排序结果 = ");
        show(arr);
        System.out.println();

        quickSort(arr,l,mid-1,index);
        quickSort(arr,mid+1,r,index);
    }

    /**
     * 交换i j位置的元素
     * @param arr 数组
     * @param i 索引
     * @param j 索引
     */
    public static void swap(int[] arr, int i, int j){
        int o = arr[i];
        arr[i] = arr[j];
        arr[j] = o;
    }

    /**
     * 输出int数组中的元素
     * @param arr 被输出数组
     */
    public static void show(int[] arr){
        for (int i : arr) {
            System.out.print(i+" ");
        }
    }

    /**
     * 根据arr中的元素,生成相同元素的相对位置,单个元素用0表示,多个相同元素根据位置前后依次编号为 0 1 2 3 4 ...
     * @param arr 待标记数组
     */
    public static int[] generateIndex(int[] arr) throws NumberFormatException {
        int size = arr.length;
        int[] num = new int[MAXNUMBER+1];   //用于记录相同数字出现的次数
        int[] index = new int[size];
        Set<Integer> set = new HashSet<Integer>();
        for(int i=0; i<size; i++){
            if(set.contains(arr[i])){
                if(arr[i] <0 || arr[i] >100){
                    throw new NumberFormatException();
                }
                index[i] = ++num[arr[i]];   //每次查询到包含时,增加1
            }else{
                set.add(arr[i]);
            }
        }
        return index;
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值