四种排序方法

94 阅读1分钟

冒泡排序、选择排序、快速排序、插入排序

冒泡排序

package com.academicdog.sorting;

import java.util.Random;

/**
 * 练习数组冒泡排序,前小后大
 *
 * 出现的问题:
 * 1.忘记多次排序
 * 2.前后弄反
 * 3.从后往前的时候,arr.length忘记减1
 */

public class bubble {
    public static void main(String[] args) {
        Random rd = new Random();

        int[] arr = new int[10];

        for (int i = 0; i < arr.length; i++) {
            arr[i] = rd.nextInt(99) + 1;
        }

        System.out.println("这是原数组:");
        printArr(arr);

        /*
        //从前往后,把大的往后移(前面的需要多次排序)
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j + 1]){
                    arr[j] = arr[j] + arr[j + 1];
                    arr[j + 1] = arr[j] - arr[j + 1];
                    arr[j] = arr[j] - arr[j + 1];
                }
            }
        }
        */

        //从后往前,把小的往前(后面需要多次排序)
        for (int i = 0; i < arr.length; i++) {
            for (int j = arr.length - 1; j > i; j--) {
                if (arr[j] < arr[j - 1]){
                    arr[j] = arr[j] + arr[j - 1];
                    arr[j - 1] = arr[j] - arr[j - 1];
                    arr[j] = arr[j] - arr[j - 1];
                }
            }
        }


        System.out.println("\n这是整理后的数组:");
        printArr(arr);
    }

    public static void printArr(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

选择排序

package com.academicdog.sorting;

import java.util.Random;

/**
 * 练习选择排序,前小后大
 *
 * 出现的问题:
 * 1.使用下标交换时,在交换的时候没有排除i = minN的情况,此时这两个容器一样,没法使用这种相差的方式交换,结果为0
 * 2.因为出现上述情况,想到使用数字比较而不是下标,没有考虑选择排序实质是数组中两个数交换,不能用数字比较
 */

public class select {
    public static void main(String[] args) {
        int[] arr = createArr();

        System.out.println("这是初始数组");
        printArr(arr);

        //选择最小的,放在最前面(使用下标)
        int minIndex = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = i; j < arr.length; j++) {
                if(arr[j] < arr[minIndex]){
                    minIndex = j;
                }
            }
            if(i != minIndex) {
                arr[i] = arr[i] + arr[minIndex];
                arr[minIndex] = arr[i] - arr[minIndex];
                arr[i] = arr[i] - arr[minIndex];
            }
            minIndex = i + 1;
        }

        System.out.println("\n这是处理后的数组");
        printArr(arr);
    }

    //生成随机数组
    public static int[] createArr(){
        Random rd = new Random();
        int[] arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = rd.nextInt(99) + 1;
        }
        return arr;
    }

    //打印数组
    public static void printArr(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

}

快速排序(见百度百科)

package com.academicdog.sorting;

import java.util.Random;

/**
 * 快速排序算法通过多次比较和交换来实现排序,不停的设定分界值并进行两边的排序,达到整组数组排序的效果
 * 练习快速排序,前小后大
 *
 * 出现的问题:
 * 双边指针交换法
 * 1.从左向右比较时,是找大的数,并和右边小的数交换。双边指针交换法中,循环思路是如果不符合交换的条件,则对指针进行++或--,所以比较时,使用arr[leftIndex] <= pivot,右边则相反。
 * 2.因为pivot是找的首元素,所以左边比较时,要注意要加上=,否则左指针循环将卡在第一个位置无法移动。
 * 3.交换两数的时候,因为交换的是数组的数,数组存储的是地址而不是内容,不能用简单的a和b,需要用arr[a]、arr[b]
 * 4.如代码中所示,每次比较后将当前指针数据与分界值互换的操作是必要的。应该是用来激活每次pivot所处的值的比较
 * 双边指针挖坑法
 * 1.挖坑指顶替、直接复制而不是交换,第一个顶掉的是pivot的值,最后再将pivot复制给最后一个空的坑
 * 2.在挖坑法中,左边比较加不加=不影响程序运行结果。pivot被顶替了,不存在左边比较时一直和pivot相等而卡住的情况。
 * 单边指针法
 * 1.将找到的小的值跟mark++交换,因为mark原来在pivot的位置,改变后原来在已经交换过的位置,所以要++。
 */

public class quick {
    public static void main(String[] args) {
        int[] arr = createArr();

        System.out.println("这是初始数组");
        printArr(arr);

        //在此处进行快速排序
        quickSort(arr, 0, arr.length - 1);

        System.out.println("\n这是处理后的数组");
        printArr(arr);
    }

    //排序函数,用到递归(所以创建函数,在函数中调用函数)
    public static void quickSort(int[] arr, int startIndex, int endIndex){
        if(startIndex >= endIndex){return;}

//        int pivotIndex = doublePointSwap(arr, startIndex, endIndex); //双边指针交换法
//        int pivotIndex = doublePointHole(arr, startIndex, endIndex); //双边指针挖坑法
        int pivotIndex = singlePoint(arr, startIndex, endIndex); //单边指针法


        quickSort(arr, startIndex, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, endIndex);
    }

    /**
     * 双边指针交换法
     * 1.记录分界值pivot(首元素)
     * 2.从左向右找比分界值大的值,从右往左找比分界值小的值
     * 3.左右指针数据交换,进入下次循环
     * 4.结束后将当前指针数据与pivot交换,返回pivot值下标(见quickSort函数)
     *
     * @return
     */
    public static int doublePointSwap(int[] arr, int startIndex, int endIndex){
        int pivot = arr[startIndex];
        int leftIndex = startIndex;
        int rightIndex = endIndex;

        while (leftIndex < rightIndex){
            while (arr[rightIndex] > pivot && leftIndex < rightIndex){ rightIndex--; } //从右向左
            while (arr[leftIndex] <= pivot && leftIndex < rightIndex){ leftIndex++; } //从左向右
            if(leftIndex < rightIndex){ swap(arr, leftIndex, rightIndex); } //两指针数据交换
        }
        /*
        为什么要有这两句?
        如果没有这两句,每次排序的pivot就都在第一位,将右指针的值和第一个位置的值交换,可以激活第一个值的比较
        这里用右指针和左指针都可以,毕竟上面筛下来,到这左右指针应该相等
         */
        arr[startIndex] = arr[rightIndex];
        arr[rightIndex] = pivot;

        return rightIndex; //返回分界值所在下标
    }

    /**
     * 双边指针挖坑法
     * 1.记录分界值pivot(首元素),创建双边指针
     * 2.左右指针轮流为坑,先判断右边比分界值小的值,填入左指针的坑中,此时右指针变成坑
     * 3.判断左边比分界值大的值,填入右指针的坑中,左指针变成坑
     * 4.结束后将分界值放到当时的坑中,返回坑的指针位置
     *
     * @return
     */
    public static int doublePointHole(int[] arr, int startIndex, int endIndex){
        int pivot = arr[startIndex];
        int leftIndex = startIndex;
        int rightIndex = endIndex;

        while (leftIndex < rightIndex){
            while (arr[rightIndex] > pivot && leftIndex < rightIndex){ rightIndex--; }
            if (leftIndex < rightIndex){
                arr[leftIndex] = arr[rightIndex];
                leftIndex++;
            }
            while (arr[leftIndex] <= pivot && leftIndex < rightIndex){ leftIndex++; }
            if (leftIndex < rightIndex){
                arr[rightIndex] = arr[leftIndex];
                rightIndex--;
            }
        }

        arr[rightIndex] = pivot;
        return rightIndex;
    }

    /**
     * 单边指针法
     * 1.记录分界值pivot(首元素),创建左边单指针
     * 2.遍历指针,如果比分界值小,则跟指针++所在的数字交换
     * 3.结束后,将分界值所在位置与指针所在位置交换
     *
     * @return
     */
    public static int singlePoint(int[] arr, int startIndex, int endIndex){
        int pivot = arr[startIndex];
        int mark = startIndex;

        for (int i = startIndex + 1; i <= endIndex; i++) {
            if(arr[i] < pivot){
                mark++;
                swap(arr, mark, i);
            }
        }
        arr[startIndex] = arr[mark];
        arr[mark] = pivot;
        return mark;
    }

    //生成随机数组
    public static int[] createArr(){
        Random rd = new Random();
        int[] arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = rd.nextInt(99) + 1;
        }
        return arr;
    }

    //打印数组
    public static void printArr(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

    //交换内容
    public static void swap(int[] arr, int a, int b){
        int tank = arr[a];
        arr[a] = arr[b];
        arr[b] = tank;
    }

}

插入排序

package com.academicdog.sorting;

import java.util.Random;

/**
 * 插入排序:针对少量元素排序,将一个记录插入到已经排好序的有序表中
 * 练习插入排序,前小后大
 */

public class insert {
    public static void main(String[] args) {
        int[] arr = createArr();

        System.out.println("这是初始数组");
        printArr(arr);

        //这里进行插入排序
        for (int i = 1; i < arr.length; i++) {
            for (int j = 0; j < i; j++) {
                if(arr[i] < arr[j]){
                    swap(arr, j, i);
                }
            }
        }

        System.out.println("\n这是处理后的数组");
        printArr(arr);
    }

    //生成随机数组
    public static int[] createArr() {
        Random rd = new Random();
        int[] arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = rd.nextInt(99) + 1;
        }
        return arr;
    }

    //打印数组
    public static void printArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

    //交换内容,这里发生了改变,因为插入了一个小的值,所以要先把之间的值往后移
    public static void swap(int[] arr, int a, int b) {
        int tank = arr[b];
        for (int i = b; i > a; i--) {
            arr[i] = arr[i - 1];
        }
        arr[a] = tank;
    }
}