版权声明:本文为博主原创文章,转载注明出处即可。 https://blog.csdn.net/bskfnvjtlyzmv867/article/details/82497352
I. 归并排序
归并排序思想
- 将数组一分为二(折半);
- 分别将两部分数组进行排序;
- 将排序好的两部分数组进行合并成新的有序数组。
动态图演示
算法实现
利用分治算法,自顶向下的进行递归排序。动态图演示则为自下往上的排序演示。
public static void main(String[] args) {
int[] array = {53, 34, 32, 56, 62, 121, 55, 41};
sort(array, 0, array.length - 1);
ArrayUtils.printArray(array);
}
private static void sort(int[] array, int start, int end) {
// 递归结束
if (start >= end) {
return;
}
int mid = (end + start) / 2;
sort(array, start, mid); // 左半排序
sort(array, mid + 1, end); // 右半排序
merge(array, start, mid + 1, end); // 归并
}
/**
* 两个有序的数组(start->mid - 1, mid->end)原地归并
* @param array 数组
* @param start 开始
* @param mid 中间
* @param end 结束
*/
public static void merge(int[] array, int start, int mid, int end) {
int[] copyArray = new int[end - start + 1];
for (int i = 0; i < copyArray.length; i++) {
copyArray[i] = array[i + start];
}
// 定义两个数组的指针,向后遍历比较
int leftIndex = start, rightIndex = mid;
for (int i = start; i <= end; i++) {
if (leftIndex >= mid) {
// 如果左边搞完
array[i] = copyArray[rightIndex - start];
rightIndex++;
} else if (rightIndex > end) {
// 如果右边边搞完
array[i] = copyArray[leftIndex - start];
leftIndex++;
} else if (copyArray[leftIndex - start] <= copyArray[rightIndex - start]) {
// 如果左边比右边小
array[i] = copyArray[leftIndex - start];
leftIndex++;
} else if (copyArray[leftIndex - start] > copyArray[rightIndex - start]) {
// 如果右边比左边小
array[i] = copyArray[rightIndex - start];
rightIndex++;
}
}
}
算法评价
排序方法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
归并排序 | 稳定 |
- 归并排序的时间复杂度都是
,但其需要额外的空间开销;
- 和选择排序一样,归并排序不受输入序列的影响,但表现比选择排序好的多;
- 归并排序并不会影响相同大小数据之间的顺序,所以稳定。
II. 快速排序
快速排序思想
- 将数组分为两部分,使得前一部分的最大值不大于后一部分的最小值(切分处的元素已经排定);
- 将两部分数组进行分别排序;
- 前一部分、切分元素与后一部分合并成有序数组。
选定切分位置:
- 随意的选取左边数组的第一个元素作为切分元素;
- 定义首尾指针,头指针从左向右遍历,寻找比切分元素大的元素,尾指针从右向左遍历,寻找比切分元素小的元素,将两个元素进行交换,使得大的在后,小的在前;
- 不断的进行首尾指针移动,直到首尾相遇,这样就能保证小的元素在头指针左侧,大的元素在尾指针右侧;
- 将首位的切分元素与头指针指向的元素位置进行交换,这样快速排序的第一步便完成。
动态图演示
算法实现
public static void main(String[] args) {
int[] array = {53, 34, 32, 56, 62, 121, 55, 41};
sort(array, 0, array.length - 1);
ArrayUtils.printArray(array);
}
public static void sort(int[] array, int start, int end) {
if (start >= end) {
return;
}
int mid = partition(array, start, end);
sort(array, start, mid - 1);
sort(array, mid + 1, end);
}
/**
* 寻找切分元素,将数组分成左右两部分
* @param array 数组
* @param start 开始
* @param end 结束
* @return 切分元素索引
*/
private static int partition(int[] array, int start, int end) {
// 定义首尾指针位置
int leftIndex = start, rightIndex = end + 1;
while (true) {
// 寻找左边大于切分元素
while (array[++leftIndex] <= array[start]) {
if (leftIndex == end) {
break;
}
}
// 寻找右边小于切分元素
while (array[--rightIndex] >= array[start]) {
if (rightIndex == start) {
break;
}
}
// 寻找到的进行替换
if (leftIndex >= rightIndex) {
break;
}
int temp = array[leftIndex];
array[leftIndex] = array[rightIndex];
array[rightIndex] = temp;
}
// 最终交换切分位置
int temp = array[start];
array[start] = array[rightIndex];
array[rightIndex] = temp;
return rightIndex;
}
算法分析
排序方法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
快速排序 | 不稳定 |
- 快速排序的时间复杂度与归并排序相当,但是最坏时间复杂度仍然是
;
- 空间复杂度由于需要交换位置,需要额外开辟空间;
- 快速排序由于寻找切分元素会进行交换位置,切分元素最后也会交换位置,破坏了稳定性;
- 快速排序的切分需要尽可能的将数组切成差不多长的两部分,才能保证快速。否则,每一只切一个元素,导致一个大数组需要切很多次。解决该问题,一般排序前将数组重新混洗,或者寻找切分元素时,随机确定一个起始切分元素。
算法改进
如果数组中存在大量重复元素,我们可以进行三向切分,每次分为小于切分元素、等于切分元素和大于切分元素三部分。对应的一次排布操作如下:
- 维护三个指针,i、lt 和 gt ,分别代表遍历指针、小于切分元素部分的边界和大于切分元素部分的边界;
- 初始 lt 为数组的起始,gt 为数组的末尾,i 为数组的第二位,切分元素也定义为数组的起始元素;
- i 开始向右遍历,当该元素小于切分元素,该元素与 lt 所在元素位置交换, lt 和 i 都加 1;
- 当该元素大于切分元素,该元素与 gt 所在元素位置交换,gt 减 1;
- 当该元素与切分元素相等,则直接 i 加 1。
改进实现
/**
* 三向切分的快速排序
* @param array
* @param start
* @param end
*/
public static void sort3way(int[] array, int start, int end) {
// 递归条件
if (start >= end) {
return;
}
// 定义三个指针和切分元素
int lt = start, i = start + 1, gt = end;
int div = array[start];
while (i <= gt) {
if (array[i] < div) {
int temp = array[i];
array[i] = array[lt];
array[lt] = temp;
i++;
lt++;
} else if (array[i] > div) {
int temp = array[i];
array[i] = array[gt];
array[gt] = temp;
gt--;
} else {
i++;
}
}
sort(array, start, lt - 1);
sort(array, gt + 1, end);
}
参考文章
- 十大经典排序算法(动图演示)
- 《算法 第四版》