常见排序算法的实现(Java)

本文详细介绍了包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序和基数排序在内的十种基本排序算法,分析了每种算法的工作原理、优缺点和适用场景。
摘要由CSDN通过智能技术生成

一、冒泡排序(Bubble Sort)

通过重复地遍历待排序序列,比较相邻的两个元素,如果顺序错误则交换它们的位置,直到没有更多的元素需要交换为止。
在这里插入图片描述

public static void bubbleSort(int[] arr) {  
    int n = arr.length;  
    for (int i = 0; i < n - 1; i++) {  
        for (int j = 0; j < n - i - 1; j++) {  
            if (arr[j] > arr[j + 1]) {  
                int temp = arr[j];  
                arr[j] = arr[j + 1];  
                arr[j + 1] = temp;  
            }  
        }  
    }  
}

简单实现,但效率较低,时间复杂度为O(n^2)。

二、选择排序(Selection Sort)

在待排序序列中找到最小(或最大)的元素,将其放到已排序序列的末尾,然后再从剩余未排序的元素中找到最小(或最大)的元素,放到已排序序列的末尾,重复此过程,直到所有元素都排序完毕。
在这里插入图片描述

public static void selectionSort(int[] arr) {  
    int n = arr.length;  
    for (int i = 0; i < n - 1; i++) {  
        int minIndex = i;  
        for (int j = i + 1; j < n; j++) {  
            if (arr[j] < arr[minIndex]) {  
                minIndex = j;  
            }  
        }  
        int temp = arr[minIndex];  
        arr[minIndex] = arr[i];  
        arr[i] = temp;  
    }  
}

比冒泡排序略好,时间复杂度仍为O(n^2)。

三、插入排序(Insertion Sort)

从第二个元素开始,将每个元素插入到已排序序列中的适当位置,直到所有元素都排序完毕。

在这里插入图片描述

public static void insertionSort(int[] arr) {  
    int n = arr.length;  
    for (int i = 1; i < n; i++) {  
        int key = arr[i];  
        int j = i - 1;  
        while (j >= 0 && arr[j] > key) {  
            arr[j + 1] = arr[j];  
            j = j - 1;  
        }  
        arr[j + 1] = key;  
    }  
}

对于小规模数据或近乎有序的数据,插入排序表现较好,时间复杂度为O(n^2)。

四、希尔排序(Shell Sort)

先将待排序序列分割成若干个子序列,分别进行插入排序,然后逐渐合并子序列,直到整个序列都有序。
在这里插入图片描述

public static void shellSort(int[] arr) {  
    int n = arr.length;  
    int gap = n / 2;  
    while (gap > 0) {  
        for (int i = gap; i < n; i++) {  
            int temp = arr[i];  
            int j = i;  
            while (j >= gap && arr[j - gap] > temp) {  
                arr[j] = arr[j - gap];  
                j -= gap;  
            }  
            arr[j] = temp;  
        }  
        gap /= 2;  
    }  
}

是插入排序的改进版,通过增加间隔元素来减少比较次数,效率比插入排序高,但不稳定。

五、归并排序(Merge Sort)

将待排序序列分成若干个子序列,对每个子序列进行排序,然后将有序的子序列合并成一个有序序列,重复此过程,直到整个序列都有序。

在这里插入图片描述

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

归并排序是有效、稳定、基于比较的排序算法,时间复杂度为O(n log n)

六、快速排序(Quick Sort)

选择一个枢轴元素,将待排序序列分成两个子序列,一个子序列的元素都比枢轴元素小,另一个子序列的元素都比枢轴元素大,然后对两个子序列分别进行快速排序。

在这里插入图片描述

public class QuickSort {  
    public static void sort(int[] array, int left, int right) {  
        if (left < right) {  
            int pivotIndex = partition(array, left, right);  
            sort(array, left, pivotIndex - 1);  
            sort(array, pivotIndex + 1, right);  
        }  
    }  
  
    private static int partition(int[] array, int left, int right) {  
        int pivot = array[right];  
        int i = left;  
        for (int j = left; j < right; j++) {  
            if (array[j] < pivot) {  
                swap(array, i, j);  
                i++;  
            }  
        }  
        swap(array, i, right);  
        return i;  
    }  
  
    private static void swap(int[] array, int i, int j) {  
        int temp = array[i];  
        array[i] = array[j];  
        array[j] = temp;  
    }  
}

快速排序是一种高效的排序算法,平均时间复杂度为O(n log n)。它使用分治策略,通过选择一个基准元素将数组分为两部分,然后递归地对这两部分进行排序。然而,在最坏情况下(当输入数组已经有序或逆序时),快速排序的时间复杂度会退化到O(n^2)。

七、堆排序(Heap Sort)

将待排序序列构造成一个大根堆(或小根堆),然后将堆顶元素(最大值或最小值)与末尾元素交换,再调整剩余元素为堆结构,重复此过程,直到所有元素都排序完毕。

在这里插入图片描述

public class HeapSort {  
    public static void sort(int[] array) {  
        int n = array.length;  
          
        // 构建最大堆  
        for (int i = n / 2 - 1; i >= 0; i--) {  
            heapify(array, n, i);  
        }  
          
        // 一个个从堆顶取出元素  
        for (int i = n - 1; i >= 0; i--) {  
            int temp = array[0];  
            array[0] = array[i];  
            array[i] = temp;  
              
            heapify(array, i, 0);  
        }  
    }  
  
    private static void heapify(int[] array, int n, int i) {  
        int largest = i;  
        int left = 2 * i + 1;  
        int right = 2 * i + 2;  
  
        if (left < n && array[left] > array[largest]) {  
            largest = left;  
        }  
  
        if (right < n && array[right] > array[largest]) {  
            largest = right;  
        }  
  
        if (largest != i) {  
            int swap = array[i];  
            array[i] = array[largest];  
            array[largest] = swap;  
  
            heapify(array, n, largest);  
        }  
    }  
}

堆排序是一种基于二叉堆的排序算法,时间复杂度为O(n log n)。它首先构建一个最大堆,然后交换堆顶元素(最大值)和最后一个元素,接着调整剩余元素以维持最大堆的性质,重复此过程直到所有元素排序完毕。堆排序是不稳定的排序算法。

八、计数排序(Counting Sort)

根据待排序序列中每个元素出现的次数,计算小于等于每个元素的元素个数,然后从后往前遍历待排序序列,将每个元素放在相应位置。

在这里插入图片描述

public class CountingSort {  
    public static void sort(int[] array, int maxValue) {  
        int n = array.length;  
        int[] count = new int[maxValue + 1];  
        int[] output = new int[n];  
  
        // 计算每个元素的频率  
        for (int i = 0; i < n; i++) {  
            count[array[i]]++;  
        }  
  
        // 累计频率  
        for (int i = 1; i <= maxValue; i++) {  
            count[i] += count[i - 1];  
        }  
  
        // 根据频率将元素放到正确的位置  
        for (int i = n - 1; i >= 0; i--) {  
            output[count[array[i]] - 1] = array[i];  
            count[array[i]]--;  
        }  
  
        // 将排序后的元素复制回原数组  
        System.arraycopy(output, 0, array, 0, n);  
    }  
  
    public static void main(String[] args) {  
        int[] array = {4, 2, 2, 8, 3, 3, 1};  
        sort(array, 9);  
        for (int i : array) {  
            System.out.print(i + " ");  
        }  
    }  
}

计数排序是一种线性时间复杂度的排序算法,适用于一定范围内的整数排序。它通过统计每个元素出现的次数,然后根据统计结果将元素放到正确的位置上。计数排序是稳定的排序算法,但只适用于整数且范围不大的情况。

九、桶排序(Bucket Sort)

将待排序序列分到有限数量的桶中,每个桶单独排序,然后将所有桶的元素按照顺序合并成一个序列。

在这里插入图片描述

public class BucketSort {  
    public static void sort(int[] array, int bucketSize) {  
        if (array.length == 0) {  
            return;  
        }  
  
        int minValue = array[0];  
        int maxValue = array[0];  
        for (int i = 1; i < array.length; i++) {  
            if (array[i] < minValue) {  
                minValue = array[i];  
            }  
            if (array[i] > maxValue) {  
                maxValue = array[i];  
            }  
        }  
  
        int bucketCount = (maxValue - minValue) / bucketSize + 1;  
        ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucketCount);  
        for (int i = 0; i < bucketCount; i++) {  
            buckets.add(new ArrayList<>());  
        }  
  
        // 将元素放入桶中  
        for (int i = 0; i < array.length; i++) {  
            int index = (array[i] - minValue) / bucketSize;  
            buckets.get(index).add(array[i]);  
        }  
  
        // 对每个桶中的元素进行排序  
        int k = 0;  
        for (int i = 0; i < buckets.size(); i++) {  
            Collections.sort(buckets.get(i));  
            for (int j = 0; j < buckets.get(i).size(); j++) {  
                array[k++] = buckets.get(i).get(j);  
            }  
        }  
    }  
  
    public static void main(String[] args) {  
        int[] array = {4, 2, 2, 8, 3, 3, 1};  
        sort(array, 5);  
        for (int i : array) {  
            System.out.print(i + " ");  
        }  
    }  
}

桶排序是计数排序的升级版,它将待排序的元素分到有限数量的桶中,然后对每个桶中的元素进行排序。桶排序的时间复杂度取决于桶的数量和桶内元素的排序算法。当桶的数量足够多,且桶内元素足够少时,桶排序的时间复杂度接近O(n)。桶排序是稳定的排序算法,但不适合元素分布不均匀的情况。

十、基数排序(Radix Sort)

将待排序元素按位数切割成不同的数字,然后按每个位数分别进行排序,重复此过程,直到所有元素都排序完毕。

public class RadixSort {  
    // 基数排序  
    public static void radixSort(int[] arr) {  
        // 找到最大数,确定位数  
        int max = getMax(arr);  
        int exp = 1;  
        while (max / exp > 0) {  
            // 使用稳定排序算法如计数排序对每一位进行排序  
            countingSort(arr, exp);  
            exp *= 10;  
        }  
    }  
  
    // 计数排序,用于基数排序中对每一位的排序  
    private static void countingSort(int[] arr, int exp) {  
        int n = arr.length;  
        int[] output = new int[n]; // 输出数组  
        int[] count = new int[10]; // 计数数组  
  
        // 初始化计数数组  
        for (int i = 0; i < 10; i++) {  
            count[i] = 0;  
        }  
  
        // 统计每个桶中的元素个数  
        for (int i = 0; i < n; i++) {  
            count[(arr[i] / exp) % 10]++;  
        }  
  
        // 修改计数数组,使每个元素表示小于等于当前元素的个数  
        for (int i = 1; i < 10; i++) {  
            count[i] += count[i - 1];  
        }  
  
        // 从后往前遍历原数组,将元素放到正确的位置上  
        for (int i = n - 1; i >= 0; i--) {  
            output[count[(arr[i] / exp) % 10] - 1] = arr[i];  
            count[(arr[i] / exp) % 10]--;  
        }  
  
        // 将排序后的元素复制回原数组  
        for (int i = 0; i < n; i++) {  
            arr[i] = output[i];  
        }  
    }  
  
    // 找到数组中的最大数  
    private static int getMax(int[] arr) {  
        int max = arr[0];  
        for (int i = 1; i < arr.length; i++) {  
            if (arr[i] > max) {  
                max = arr[i];  
            }  
        }  
        return max;  
    }  
}

基数排序是一种稳定的排序算法,时间复杂度为O(d * (n + r)),其中d是最大数的位数,n是待排序数组的长度,r是桶的数量(在基数排序中通常为10)。由于它只需要O(n + r)的额外空间,因此也是一种空间效率较高的排序算法。基数排序特别适用于对整数进行排序,尤其是当整数的范围比较小或者知道整数的分布时。然而,对于浮点数或者范围很大的整数,基数排序可能不是最佳选择。

在这里插入图片描述

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小全.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值