选择排序
选择排序的基本思想是:每次从待排序的文件中选择出排序码最小的记录将该记录放于已排序文件的最后一个位置,直到已排序文件记录个数等于初始待排序文件的记录个数为止。
直接选择排序是一种简单的方法,首先从所有n个待排序记录中选择排序码最小的记录举该记录,将该记录与第1个记录交换,再从剩下的n-1个记录中选出排序码最小的记录和第 2个记录交换。重复这样的操作直到剩下两个记录时,再从中选出排序码最小的记录和第n-1个记录交换。剩下的肯定是最大的记录,排序完成。
时间复杂度O(n^2)
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
该数组从逻辑上讲就是一个堆结构,用公式来描述:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。(从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1),从左至右,从下至上进行调整)
堆排序的基本思路:
a.将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
具体算法参考博客:–>参考博客
建大顶堆就是不断从最后一个非叶子结点一直调整到根节点。
数组中的第K个最大元素
#include
#include
using namespace std;
class Solution {
public:
void adjustHeap(vector<int>& nums,int cur,int len){
int tmp = nums[cur-1]; //数组下标从0开始 第cur个元素的数组下标需要减一
for(cur = 2*cur;cur<=len;cur=cur*2){
if(cur<len && nums[cur-1]<nums[cur+1-1]) cur++;
if(nums[cur-1]>tmp) nums[cur/2-1] = nums[cur-1];
else break;
}
nums[cur/2-1] = tmp;
}
int findKthLargest(vector<int>& nums, int k) {
int len = nums.size();
// nums.insert(nums.begin(), 0);
//建堆 从第一个非叶子结点开始调整
for(int i=len/2;i>=1;i--){
adjustHeap(nums,i,len);
}
//堆排 已经是一个大顶堆了
for(int i=len;i>=2;i--){ //其实不用到2
swap(nums[0],nums[i-1]);
adjustHeap(nums,1,i-1);
}
return nums[len-k];
}
};
交换排序
交换排序的基本思路:对待排序记录两两进行排序码比较,若不满足排序顺序,则交换这对记录,直到任何两个记录的排序码都满足排序要求为止。
对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序。
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
合并相邻有序子序列
再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
int mid = (l+r) >> 1
//循环右移一位表示除以2;(同理循环左移一位相当于✖️2)
参考博客