文章目录
整理一下学习数据结构算法时遇到的排序问题,将几个经典的算法记录下来。包括冒泡,选择,插入,希尔,快速,归并和基数排序。前三个比较简单,剩下的算法还是有难度的,本文按照先原理后实现的模式来介绍。
快速排序(平均O(nlogn)、最好O(nlogn)、最差O(n2)、不稳定,空间O(logn) )
思想:对于一个数组,定义左右指针分别指向最左和最后的元素,然后定义一个标杆,比如选择最左边的元素。每次快排中,首先从从右往左遍历,直到找到一个比标杆小的元素,把它赋给左指针指向的位置;然后从左往右遍历,直到找到一个比标杆大的元素,把它赋给刚刚右指针停留的位置。重复上述的过程,最后把标杆值赋给左右指针相遇的位置。这样一轮快排下来,就做到了标杆左边都是比他小的,标杆右边都是比他大的。然后对标杆左右子数组递归地调用快排。
class Solution {
public int[] sortArray(int[] nums) {
quickSort(nums, 0, nums.length - 1);
return nums;
}
public void quickSort(int[] a, int left, int right){
if(left >= right) return;
int temp = a[left];//标杆
int i = left, j = right;//左右指针
while(i < j){
while(i < j && a[j] >= temp) j--;
a[i] = a[j];
while(i < j && a[i] <= temp) i++;
a[j] = a[i];
}
a[i] = temp;
quickSort(a, left, i - 1);
quickSort(a, i + 1, right);
}
}
堆排序(平均O(nlogn)、最好O(nlogn)、最差O(nlogn)、不稳定,空间O(1) )
堆排序的理解
思想:堆的每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
数组从逻辑上满足下面的条件,就是一个堆结构,
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:将待排序数组构造成一个大顶堆,此时,整个数组的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
class Solution {
public int[] sortArray(int[] nums) {
heapSort(nums);
return nums;
}
public void heapSort(int[] nums) {
int len = nums.length - 1;
buildMaxHeap(nums, len);
for (int i = len; i >= 1; --i) {
swap(nums, i, 0);
len -= 1;
maxHeapify(nums, 0, len);
}
}
public void buildMaxHeap(int[] nums, int len) {//构建堆
for (int i = len / 2; i >= 0; --i) {
maxHeapify(nums, i, len);
}
}
public void maxHeapify(int[] nums, int i, int len) {
for (; (i << 1) + 1 <= len;) {
int lson = (i << 1) + 1;
int rson = (i << 1) + 2;
int large;
if (lson <= len && nums[lson] > nums[i]) {
large = lson;
} else {
large = i;
}
if (rson <= len && nums[rson] > nums[large]) {
large = rson;
}
if (large != i) {
swap(nums, i, large);
i = large;
} else {
break;
}
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
偷懒写法,用优先队列
class Solution {
public int[] sortArray(int[] nums) {
PriorityQueue<Integer> q = new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer o1, Integer o2){
return o1 - o2;
}
});
for(int i = 0; i < nums.length; i++){
q.offer(nums[i]);
}
for(int i = 0; i < nums.length; i++){
nums[i] = q.poll();
}
return nums;
}
}
归并排序(平均O(nlogn)、最好O(nlogn)、最差O(nlogn)、稳定、O(n) )
思想:分治。把要排序的数组也分开,方法就是对半分,一直递归地分组,直到不能再分。然后开始治,采用线性合并的方法。需要定义一个辅助数组来存放排序好的部分。
class Solution {
int[] tmp;
public int[] sortArray(int[] nums) {
tmp = new int[nums.length];
mergeSort(nums, 0, nums.length - 1);
return nums;
}
public void mergeSort(int[] nums, int l, int r) {//递归地分
if (l >= r) {
return;
}
int mid = (l + r) >> 1;
mergeSort(nums, l, mid);
mergeSort(nums, mid + 1, r);
int i = l, j = mid + 1;
//开始线性合并
int cnt = 0;
while (i <= mid && j <= r) {
if (nums[i] <= nums[j]) {
tmp[cnt++] = nums[i++];
} else {
tmp[cnt++] = nums[j++];
}
}
while (i <= mid) {
tmp[cnt++] = nums[i++];
}
while (j <= r) {
tmp[cnt++] = nums[j++];
}
for (int k = 0; k < r - l + 1; ++k) {
nums[k + l] = tmp[k];
}
}
}
本文概述了快速排序(O(nlogn)复杂度,不稳定,空间O(logn))、堆排序(O(nlogn),不稳定,空间O(1))和归并排序(O(nlogn),稳定,空间O(n))的原理、实现与特点。深入理解排序算法在数据结构中的应用。
259

被折叠的 条评论
为什么被折叠?



