• 十大排序算法(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)));
    }
    
  • 相关阅读:
    sqlServer的主键只能自增不能手动增加
    TP函数
    TP复习17
    TP复习16
    TP复习15
    TP复习14
    TP复习13
    TP复习12
    TP复习11
    TP复习10
  • 原文地址:https://www.cnblogs.com/songjilong/p/12234856.html
Copyright © 2020-2023  润新知