十大排序算法(Java实现)

一、冒泡排序(Bubble Sort)

基础版

/**
 * 遍历数组,依次比较相邻的元素并交换,每次都将最大元素(根据正序还是逆序决定)放到数组末尾
 * @param arr 待排序数组
 * @return
 */
public static int[] bubbleSort(int[] arr) {
    int temp;

    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                temp = arr[j + 1];
                arr[j + 1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

优化一

//冒泡排序:优化一
public static int[] bubbleSort1(int[] arr) {
    int temp;
    //记录数组是否有序
    boolean isSorted;

    for (int i = 0; i < arr.length - 1; i++) {
        isSorted = true;
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                temp = arr[j + 1];
                arr[j + 1] = arr[j];
                arr[j] = temp;
                //记录本轮排序是否交换了元素,如果交换则置为false
                isSorted = false;
            }
        }
        //没有交换证明已经是有序的了,直接终止循环
        if (isSorted) {
            break;
        }
    }
    return arr;
}

优化二

//冒泡排序:优化二
public static int[] bubbleSort2(int[] arr) {
    int temp;
    boolean isSorted;
    //第一次循环边界
    int sortBorder = arr.length - 1;
    //记录每轮排序最后一次进行交换的位置
    int lastSwapIndex = 0;

    for (int i = 0; i < arr.length - 1; i++) {
        isSorted = true;
        for (int j = 0; j < sortBorder; j++) {
            if (arr[j] > arr[j + 1]) {
                temp = arr[j + 1];
                arr[j + 1] = arr[j];
                arr[j] = temp;
                isSorted = false;
                lastSwapIndex = j;
            }
        }
        sortBorder = lastSwapIndex;
        if (isSorted) {
            break;
        }
    }
    return arr;
}

二、选择排序(Selection Sort)

/**
 * 遍历数组,每次遍历都找到最小的元素,记录其下标,内层循环结束后再根据下标将其与数组头部元素交换
 * 与冒泡排序不同的是,冒泡排序每次循环可能交换多次,而选择排序最多交换一次
 * @param arr 待排序数组
 * @return
 */
public static int[] selectionSort(int[] arr) {
    int temp, minIndex;
    for (int i = 0; i < arr.length - 1; i++) {
        minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        temp = arr[minIndex];
        arr[minIndex] = arr[i];
        arr[i] = temp;
    }
    return arr;
}

三、插入排序(Insertion Sort)

/**
 * 用一个临时变量存储待插入的值,从后往前找,如果找到比这个值大的元素,则将其前面的元素依次后移,
 * 结束后再将带插入的值放到该插入的位置,减去了许多不必要的交换操作
 * @param arr
 * @return
 */
public static int[] insertionSort(int[] arr) {
    for (int i = 1; i < arr.length; i++) {
        int insertValue = arr[i];//待插入元素
        int preIndex = i - 1;
        while (preIndex >= 0 && insertValue < arr[preIndex]) {
            arr[preIndex + 1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex + 1] = insertValue;
    }
    return arr;
}

四、希尔排序(Shell Sort)

/**
 * 希尔排序
 * 插入排序的升级版,设定步长为数组长度的一半,每次都除以二,直到步长为1
 * @param arr
 * @return
 */
public static int[] shellSort(int[] arr) {
    int len = arr.length;
    for (int k = len / 2; k > 0; k /= 2) {
        for (int i = k; i < len; i++) {
            int temp = arr[i];
            int j = i - k;
            while (j >= 0 && temp < arr[j]) {
                arr[j + k] = arr[j];
                j -= k;
            }
            arr[j + k] = temp;
        }
    }
    return arr;
}

五、归并排序(Merge Sort)

//归并排序
public static int[] mergeSort(int[] arr) {
    return mergeSort(arr, 0, arr.length - 1, new int[arr.length]);
}

/**
     * 归并排序通过递归将数组分解为只有两个元素,按照它们的大小放入到一个临时数组中,直到全部合并
     *
     * @param arr   待排序数组
     * @param left  左索引
     * @param right 右索引
     * @param temp  临时数组,存储每次合并后的元素
     * @return
     */
public static int[] mergeSort(int[] arr, int left, int right, int[] temp) {
    if (left < right) {
        int mid = (left + right) >> 1;
        //向左分解
        mergeSort(arr, left, mid, temp);
        //向右分解
        mergeSort(arr, mid + 1, right, temp);
        //合并
        merge(arr, left, mid, right, temp);
    }
    return arr;
}

//合并
public static int[] merge(int[] arr, int left, int mid, int right, int[] temp) {
    int i = left, j = mid + 1, k = 0;
    //按大小放入临时数组中
    while (i <= mid && j <= right) {
        if (arr[i] < arr[j]) {
            temp[k] = arr[i];
            k++;
            i++;
        } else {
            temp[k] = arr[j];
            k++;
            j++;
        }
    }

    //将剩余元素放到temp剩余位置
    while (i <= mid) {
        temp[k] = arr[i];
        k++;
        i++;
    }
    while (j <= right) {
        temp[k] = arr[j];
        k++;
        j++;
    }

    //将排好序的temp数组元素赋值给原数组
    k = 0;
    int l = left;
    while (l <= right) {
        arr[l] = temp[k];
        k++;
        l++;
    }

    return arr;
}

六、快速排序(Quick Sort)

//快速排序
public static int[] quickSort(int[] arr) {
    return quickSort(arr, 0, arr.length - 1);
}

public static int[] quickSort(int[] arr, int startIndex, int endIndex) {
    if (startIndex < endIndex) {
        //获取基准对应的下标
        int pivotIndex = partition1(arr, startIndex, endIndex);
        quickSort(arr, startIndex, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, endIndex);
    }
    return arr;
}

/**
     * 方式一:双边循环交换
     * 先从右边找到比基准元素小的值,再从左边找到比基准元素大的值,然后交换两者
     * 直到左右指针相遇,再交换基准和相遇位置的值
     *
     * @param arr        待排序数组
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     * @return 返回基准索引
     */
public static int partition1(int[] arr, int startIndex, int endIndex) {
    //取第一个元素作为基准,也可以取一个随机元素与第一个元素交换
    int pivot = arr[startIndex];
    int left = startIndex, right = endIndex;
    int temp;
    while (left != right) {
        while (left < right && arr[right] > pivot) {
            right--;
        }
        while (left < right && arr[left] <= pivot) {
            left++;
        }
        //交换左右元素
        if (left < right) {
            temp = arr[right];
            arr[right] = arr[left];
            arr[left] = temp;
        }
    }
    //交换重合位置元素和基准
    arr[startIndex] = arr[left];
    arr[left] = pivot;

    return left;
}

//方式二:单边循环
public static int partition2(int[] arr, int startIndex, int endIndex) {
    int pivot = arr[startIndex];
    //定义一个mark,数组向右寻找比pivot小的元素,找到后mark+1,然后互相交换
    int mark = startIndex;
    for (int i = startIndex + 1; i <= endIndex; i++) {
        if (arr[i] < pivot) {
            mark++;
            int temp = arr[i];
            arr[i] = arr[mark];
            arr[mark] = temp;
        }
    }
    //交换mark位置的值和pivot
    arr[startIndex] = arr[mark];
    arr[mark] = pivot;
    return mark;
}

七、堆排序(Heap Sort)

public static int[] heapSort(int[] arr) {
    //以最后一个非叶子结点构建大顶堆
    for (int i = arr.length / 2 - 1; i >= 0; i--) {
        adjustHeap(arr, i, arr.length);
    }
    //此时顶部元素是最大的,交换顶部元素和末端元素
    for (int i = arr.length - 1; i > 0; i--) {
        swap(arr, 0, i);
        //末端元素已经是最大的了,无需考虑排序
        adjustHeap(arr, 0, i);
    }
    return arr;
}

/**
     * 形成大顶堆
     *
     * @param arr 数组元素
     * @param i   当前结点位置
     * @param len 结点个数
     */
public static void adjustHeap(int[] arr, int i, int len) {
    //保存当前结点
    int temp = arr[i];
    //遍历当前结点的左子结点
    for (int k = 2 * i + 1; k < len; k = 2 * k + 1) {
        //如果右结点存在 且 右结点比左结点大,指向右结点
        if (k + 1 < len && arr[k] < arr[k + 1]) {
            k++;
        }
        //判断当前结点和左(右)结点哪个大
        if (temp < arr[k]) {
            //交换
            swap(arr, k, i);
            //交换后,下次遍历以该子结点作为根节点的子树就会受到影响,因此需要重新指定下次的根节点
            i = k;
        } else {
            //不用交换,直接终止循环
            break;
        }
    }
}

public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

八、计数排序(Counting Sort)

/**
     * 计数排序:
     * 将待排序数组的值(或者差值)作为新数组的下标,新数组的值是排序元素在此位置的个数
     * 使用max-min+1作为数组长度可以减少空间浪费
     * 
     * @param arr
     * @return
     */
public static int[] countingSort(int[] arr){
    int max = arr[0];
    int min = arr[0];
    for (int i = 1; i < arr.length; i++) {
        if(max < arr[i]){
            max = arr[i];
        }
        if(min > arr[i]){
            min = arr[i];
        }
    }
    int[] count = new int[max - min + 1];
    //将待排序数组放到count中
    for (int value : arr) {
        count[value - min]++;
    }
    //将count放到arr中
    int k = 0;
    for (int i = 0; i < count.length; i++) {
        while(count[i] > 0){
            arr[k++] = i;
            count[i]--;
        }
    }
    return arr;
}


优化

//计数排序优化,变为稳定排序
public static int[] countingSort1(int[] arr){
    int max = arr[0];
    int min = arr[0];
    for (int i = 1; i < arr.length; i++) {
        if(max < arr[i]){
            max = arr[i];
        }
        if(min > arr[i]){
            min = arr[i];
        }
    }
    int[] count = new int[max - min + 1];
    //将待排序数组放到count中
    for (int value : arr) {
        count[value - min]++;
    }
    //当前的元素等于前面的元素加上当前
    for (int i = 1; i < count.length; i++) {
        count[i] += count[i-1];
    }

    //倒序遍历count
    int[] storedArr = new int[arr.length];
    for (int i = arr.length - 1; i >= 0; i--) {
        storedArr[count[arr[i] - min]-1] = arr[i];
        count[arr[i] - min]--;
    }
    return storedArr;
}

九、桶排序(Bucket Sort)

public class BucketSort {
    /**
     * 桶排序:
     * 将数据分为n个区间,区间的跨度为 (max - min) / (n - 1)
     *
     * @param arr
     * @return
     */
    public static int[] bucketSort(int[] arr){
        int max = arr[0];
        int min = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
            if (min > arr[i]) {
                min = arr[i];
            }
        }
        int bucketNum = arr.length;//桶个数
        int d = max - min;//差值
        ArrayList<LinkedList<Integer>> bucketList = new ArrayList<>(bucketNum);//模拟5个桶

        //初始化桶
        for (int i = 0; i < bucketNum; i++) {
            bucketList.add(new LinkedList<>());
        }

        //将待排序元素放到桶中
        for (int i = 0; i < arr.length; i++) {
            int num = (arr[i] - min) * d / (bucketNum - 1);//应该存入的桶号
            bucketList.get(num).add(arr[i]);
        }

        //对每个桶的数据进行排序
        for (int i = 0; i < bucketNum; i++) {
            //JDK 底层采用了归并排序(1.7之前)或归并的优化版本
            Collections.sort(bucketList.get(i));
        }
        
        //将桶的数据取出
        int k = 0;
        for (LinkedList<Integer> nums : bucketList) {
            for (Integer num : nums) {
                arr[k++] = num;
            }
        }

        return arr;
    }
}

十、基数排序(Radix Sort)

public class RadixSort {
    /**
     * 基数排序:
     * 根据每个数的个位、十位、百位...的值(0~9)放入桶中(规则和计数排序相同),因此需要10个桶
     *
     * @param arr
     * @return
     */
    public static int[] radixSort(int[] arr){
        //创建并初始化10个桶
        ArrayList<LinkedList<Integer>> bucketList = new ArrayList<>(10);
        for (int i = 0; i < 10; i++) {
            bucketList.add(new LinkedList<>());
        }

        //找出数据中最大值
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }

        //获取最大值的位数
        int maxRadix = (max + "").length();
        //从个位开始
        for (int i = 0; i < maxRadix; i++) {
            //将待排序元素放入桶中
            for (int j = 0; j < arr.length; j++) {
                //获取数字对应位上的值
                int radix = arr[j] / (int) Math.pow(10, i) % 10;
                //放入对应的桶中
                bucketList.get(radix).add(arr[j]);
            }
            //将桶中元素放回原数组
            int k = 0;
            for (int j = 0; j < 10; j++) {
                for (Integer number : bucketList.get(j)) {
                    arr[k++] = number;
                }
                bucketList.get(j).clear();
            }
        }
        return arr;
    }
}

最后:测试

public static void main(String[] args) {
    int[] arr = {3, 1, 9, 10, 4, 0, 6, 5, 2, 12, 8, 7, 11};
    //[0, 3, 4, 5, 6, 7, 8, 10, 16, 22, 29, 33, 38]
    System.out.println("冒泡排序:"+ Arrays.toString(bubbleSort(arr)));
    System.out.println("冒泡排序-优化1:"+ Arrays.toString(bubbleSort1(arr)));
    System.out.println("冒泡排序-优化2:"+ Arrays.toString(bubbleSort2(arr)));
    System.out.println("选择排序:"+ Arrays.toString(selectionSort(arr)));
    System.out.println("插入排序:"+ Arrays.toString(insertionSort(arr)));
    System.out.println("希尔排序:"+ Arrays.toString(shellSort(arr)));
    System.out.println("归并排序:"+ Arrays.toString(mergeSort(arr)));
    System.out.println("快速排序:"+ Arrays.toString(quickSort(arr)));
    System.out.println("堆排序:"+ Arrays.toString(heapSort(arr)));
    System.out.println("计数排序:"+ Arrays.toString(countingSort(arr)));
    System.out.println("计数排序-优化1:"+ Arrays.toString(countingSort1(arr)));
    System.out.println("桶排序:"+ Arrays.toString(bucketSort(arr)));
    System.out.println("基数排序:"+ Arrays.toString(radixSort(arr)));
}
posted @ 2020-01-26 21:19  农夫三拳有点疼~  阅读(592)  评论(0编辑  收藏  举报