排序算法比较与总结

一、插入排序

1. 核心思想

共进行 N - 1 趟排序,对于 P = 1 到 P = N - 1 趟,保证从位置0到位置P上的元素为排序状态。在第P趟,将位置P上的元素移动到前P + 1个元素的正确位置上,比其更大的元素都向右移动一个位置。

2. 分析

如果输入数据已预先排序,则时间复杂度为O(N);平均时间复杂度和最坏时间复杂度均为O(N^2),空间复杂度为O(1)。插入排序只用在小的或是非常接近排好序的输入数据上。

3. 代码实现 

public void InsertSort(int[] arr) {
    int tmp, p, i;
    for(p = 1; p < arr.length; p++) {
        tmp = arr[p];
        for(i = p; i > 0 && arr[i - 1] > tmp; i--) {
            arr[i] = arr[i - 1];
        }
        arr[i] = tmp;
    }
}

二、选择算法

1. 核心思想

每趟均在数组中找到第k小的元素文章,将其放置k - 1的位置,共进行N - 1趟排序。

2. 分析

无论什么样本数据,时间复杂度均为O(N^2),空间复杂度为O(1),不适合大量数据的排序。

3. 代码实现

public void selectionSort(int[] arr) {
    for(int i = 0; i < arr.length - 1; i++) {
        int min = i;
        for(int j = i + 1; j < arr.length; j++) {
            if(arr[min] > arr[j]) {
                min = j;
            }
        }
        swap(arr, min, i);
    }
}

三、冒泡算法

1. 核心思想

每趟排序都对数组中未排序数据进行遍历,相邻元素两两比较,如果顺序与预先规定的顺序不一致,则交换,每次遍历的结果都会使最大元素上浮到顶端。之后再重复操作直至数组有序,共进行N - 1趟排序。

(图片链接: https://www.wandouip.com/t5i109169/

2. 分析

当输入数据为正序时,时间复杂度最小,为O(N),当输入数据为反序时,时间复杂度最大,为O(N^2),平均时间复杂度为O(N^2)。当数据量很大时,冒泡排序效率不高。

3. 代码实现

public void bubbleSort(int[] 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]) {
                swap(arr, j, j + 1);
            }
        }
    }
}

四、希尔排序(缩小增量排序)

1. 核心思想

希尔排序使用一个增量序列h1,h2,...,ht,在使用增量hk的一趟排序后,对于每个i都有arr[i] <= A[i + hk],所有相隔hk的元素都被排序。

 

2. 分析

希尔排序的时间复杂度与增量序列的选取有关。使用希尔增量时希尔排序的时间复杂度的最坏时间复杂度为O(N^2),最好时间复杂度为O(NlogN)。

3. 代码实现

public void shellSort(int[] arr) {
    int n = arr.length;
    for(int increment = n / 2; increment > 0; increment /= 2) {
        for(int i = increment; i < n; i++) {
            int tmp = arr[i], j;
            for(j = i; j >= increment && arr[j] < arr[j - increment]; j -= increment) {
                arr[j] = arr[j - increment];
            }
            arr[j] = tmp;
        }
    }

五、堆排序

1. 核心思想

将无序数组构造成一个大顶堆(排序后可获得升序数组),完成后将堆顶元素与末尾元素交换,此时末尾为最大值。然后继续调整堆,再次将堆顶与末尾元素互换,此时可得到第二大元素,如此反复进行交换、重建。

2. 分析

堆排序的最好、最坏、平均时间复杂度均为O(NlogN)。

3. 代码实现

public void heapSort(int[] arr) {
    int n = arr.length;
    for(int i = n / 2; i >= 0; i--) {
        percDown(arr, i, n); // 初始化堆
    }
    for(int i = n - 1; i > 0; i--) {
        swap(arr, 0, i);
        percDown(arr, 0, i);
    }
}

public void percDown(int[] arr, int i, int n) {
    int tmp = arr[i], child;
    for(; 2 * i + 1 < n; i = child) {
        child = 2 * i + 1;
        if(child != n - 1 && arr[child] < arr[child + 1]) child++;
        if(tmp < arr[child])
            arr[i] = arr[child];
        else
            break;
    }
    arr[i] = tmp;
}

六、归并排序

1. 核心思想

该算法是经典的分治策略,它将无序数组分成两个序列,分别对它们进行排序,最后合并。而拆分的子序列继续拆分成两个序列再分别进行排序,直至序列只有一个元素。

2. 分析:

归并算法的最好、最坏、平均时间复杂度均为O(NlogN),空间复杂度为O(N),因此很难用于主存排序,因为合并两个排序的表时需要线性附加内存。

3. 代码实现

public void mergeSort(int[] arr) {
    int n = arr.length;
    int[] tmp = new int[n];
    mergeSort(0, n - 1, arr, tmp);
}

public void mergeSort(int left, int right, int[] arr, int[] tmp) {
    if(left < right) {
        int mid = (left + right) / 2;
        mergeSort(left, mid, arr, tmp);
        mergeSort(mid + 1, right, arr, tmp);
        merge(left, mid, right, arr, tmp);
    }
}

public void merge(int left, int mid, int right, int[] arr, int[] tmp) {
    int k = 0, i = left, j = mid + 1;
    while(i <= mid && j <= right) {
        if(arr[i] <= arr[j])
            tmp[k++] = arr[i++];
        else
            tmp[k++] = arr[j++];
    }
    while(i <= mid) {
        tmp[k++] = arr[i++];
    }
    while(j <= right) {
        tmp[k++] = arr[j++];
    }
    for(int t = 0; t < k; t++) {
        arr[left + t] = tmp[t];
    }
}

七、快速排序

1. 核心思想

也是一种分治算法,在无序数组中选取一个元素作为枢纽元,使得枢纽元左边的元素均小于它,而右边的元素均大于它,随后继续对两边的子序列进行快速排序。

2. 分析

平均时间复杂度为O(NlogN),最坏时间复杂度为O(N^2),但这种情况并不多见,空间复杂度为O(logN)。

3. 代码实现

public void quickSort(int[] arr) {
    int n = arr.length;
    quickSort(arr, 0, n - 1);
}

public void quickSort(int[] arr, int left, int right) {
    if(left >= right) {
        return;
    } else {
        int pivot = arr[left];
        int partition = partition(arr, left, right, pivot);
        quickSort(arr, left, partition);
        quickSort(arr, partition + 1, right);
    }
}

public int partition(int[] arr, int left, int right, int pivot) {
    while(left < right) {
        while (left < right && arr[right] > pivot) right--;
        swap(arr, left, right);
        while (left < right && arr[left] < pivot) left++;
        swap(arr, left, right);
    }
    return left;
}

八、总结

排序算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度稳定性
插入算法O(N^2)O(N)O(N^2)O(1)稳定
选择算法O(N^2)O(N^2)O(N^2)O(1)不稳定
冒泡算法O(N^2)O(N)O(N^2)O(1)稳定
希尔排序O(NlogN)O(Nlog^2N)O(Nlog^2N)O(1)不稳定
堆排序O(NlogN)O(NlogN)O(NlogN)O(1)不稳定
归并排序O(NlogN)O(NlogN)O(NlogN)O(N)稳定
快速排序O(NlogN)O(NlogN)O(N^2)O(logN)不稳定

1. 稳定性即排序前后两个相等的数其位置不变。其中插入、冒泡、归并排序均为稳定排序,选择、希尔、堆、快速排序均为不稳定排序。

2. 除归并、快速排序外,其他排序空间复杂度均为O(1),其中归并排序为O(N),快速排序为O(logN)。

3. 选择、堆、归并排序的时间复杂度在三种情况下均相同,其中选择排序的均为O(N^2),堆、归并排序的均为O(NlogN)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值