今天才把go中的排序算法这节课看完,起始课上三种排序算法都讲的比较一笔带过,好久没有实践都有点忘记饿了,这里重新把三种算法实现一下,加深一下记忆,用到的例子是在力扣上找的:912. 排序数组 - 力扣(LeetCode)
1、插入排序
思路:
假设前n-1个元素已经有序,现将第n个元素插入到前面n-1个有序序列中,使得前n个元素有序。按照这样的思路对所有元素进行插入直到整体有序。
因为不知道从哪里开始有序,但是第一个元素只有一个元素肯定是有序的,所以从第一个开始,依次往后面遍历进行插入,直到整体有序。
步骤:
- 从一个元素开始,认为是有序的
- 取下一个元素,从以排序的元素序列中从后往前扫描
- 该元素小于最后一个元素,则将有序序列元素后移
- 重复上一步骤,该元素大于最后一个元素则将元素插入到后一个位置
- 重复步骤2-4,直到整体有序
代码:
class Solution {
public int[] sortArray(int[] nums) {
// 插入排序
for(int i = 1; i < nums.length; i++){
// 记录当前有序数组的最后一个的下标
int end = i - 1;
// 记录当前要插入的元素
int insert = nums[i];
// 单趟逐一比较元素
while(end >= 0){
// 插入数据比较小则将有序数组元素后移
if(insert < nums[end]){
nums[end + 1] = nums[end];
end--;
}else{
// 插入数据大直接跳出
break;
}
}
// 插入该元素
nums[end + 1] = insert;
}
return nums;
}
}
总结:
**时间复杂读:**最好情况下是有序序列,时间复杂度为O(N);最坏情况下是逆序序列,时间复杂度为O(N * N);平均复杂度O(N * N)
**空间复杂度:**O(1)
2、快速排序
思路:
通过一趟排序将数据分割成两个部分,一部分所有数据比另外一部分所有数据都要小,用于分割的数据是处于正确的位置,然后左右两边用同样的方法递归进行达到整体有序。
步骤:
1、选出一个key,这里选择最左边作为key
2、定义两个指针,一左一右,左指针右走,右指针左走
3、左指针找到第一个比key大的值,右指针找到第一个比key小的值,以此内推,直到两个指针相遇
4、将key与相遇的节点交换 ,到达key左都比key小,key右都比key大
5、左右两边分别递归
代码:
public void quickSort(int[] nums, int start, int end){
// 只有一个元素或者区间不存在
if(start >= end){
return;
}
int left = start;
int right = end;
// 选择最左边的数字为key
int key = start;
// 使所有小于key的值在左边,大于key的值在右边
while(left < right){
// 右边好到小于key的值 ,先移动右指针,这样才能道道交换过去值比key小
while(nums[right] >= nums[key] && left < right){
right--;
}
// 左边找到大于key的值 再判断一次防止越界
while(nums[left] <= nums[key] && left < right){
left++;
}
// 交换两个位置的值
swap(nums[right],nums[left]);
}
// left和right最终会指向同一位置,与key值交换
swap(nums[key],nums[left]);
key = left;
// key的左边和右边分别递归
quickSort(nums,start,key - 1);
quickSort(nums,key + 1,end);
}
总结:
**时间复杂度:**最好情况O(N * logN);最差情况O(N * N);平均O(N * logN)
3、堆排序
思路:
利用大顶堆的性质,每次找出最大值,然后放在最后,然后继续在剩余的数继续找
步骤:
- 构造大顶堆
- 根节点是最大值,将根节点与末尾值进行交换
- 将剩余n-1个值重新构造成堆,重复上操作
代码:
class Solution {
public int[] sortArray(int[] nums) {
// 堆排序
int len = nums.length;
// 将数组先整理成大顶堆
heapify(nums);
for(int i = len - 1; i > 0;){
// 将根元素(最大值)和最后一个元素互换,达到一个排序的效果
swap(nums, 0, i);
i--;
// 将根元素下沉,去掉已经排序好的元素
siftDown(nums,0,i);
}
return nums;
}
// 将数组整理成堆
public void heapify(int[] nums){
int len = nums.length;
for(int i = (len - 1)/2; i >= 0; i--){
// 元素逐一下沉
siftDown(nums, i, len - 1);
}
}
// 元素下沉 k是下沉元素, end为范围
public void siftDown(int[] nums, int k, int end){
while(2 * k + 1 <= end){
int j = 2 * k + 1;
// 下沉元素
if(j + 1 <= end && nums[j + 1] > nums[j]){
j++;
}
if(nums[j] > nums[k]){
swap(nums,j,k);
}else{
break;
}
k = j;
}
}
// 交换数组元素
public void swap(int[] nums, int index1, int index2){
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
总结:
**时间复杂度:**O(N * logN)