版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FrankieHello/article/details/83315436
选择类排序
选择类排序算法的核心就是选择,也就是每趟排序选出最大(小)的元素,然后将其放入它的最终位置。如排队时,先从中找出最矮的将其与队首的元素进行交换,然后选出其次大的与队头的第二个位置上的元素进行交换位置。常用的有简单选择排序和堆排序。
简单选择排序(Selection Sort)
简单选择排序的选择思想最简单,也就是从头到尾遍历一遍选出最大(小)的关键字,然后与第一个关键字进行交换位置。
网上的gif动图:
代码:
void selectionSort(int a[], int n){
int i, j, k;
int temp;
for(i = 0;i<n;i++){
k = i;
for(j = i + 1;j < n;j++){
if(a[j]<a[k]){
k = j;
}
}
if(k!=i){
temp = a[i];
a[i] = a[k];
a[k] = temp;
}
}
}
int main() {
int a[] = {1,2,3,2,5,6};
int i;
selectionSort(a, 6);
for(i = 0; i<6;i++){
printf("%d ", a[i]);
}
}
分析:
从代码中可以看出,两次循环的执行次数与数据序列的初始状态没有关系,都是外层执行n次,内层执行n-1次,所以时间复杂度是。
排序过程中只需要借助一个辅助空间,所以空间复杂度是。
堆排序(Heap Sort)
可以把堆看成是一棵完全二叉树,这棵二叉树满足任何一个非叶节点的值都不大于(不小于)其左右孩子节点的值。所以它的思想就是通过把原始序列构成的一棵完全二叉树通过不断调整来转换为符合堆定义的完全二叉树。
下面以建立大顶堆为例子。需要注意在建立堆的过程需要按照以下的顺序:
- 从完全二叉树的第一个非叶节点开始。
- 从右到左。
- 从下到上。
网上的gif图:来自 http://www.cnblogs.com/zhuminghui/p/8401129.html
代码:
void sift(int R[], int low, int high){
int i = low; // 非叶节点
int j = 2*i; // 非叶节点的左孩子
int temp = R[i];
while(j <= high){ // j将以i节点为根节点的子树调整为符合大顶堆的规则
if(j<high && R[j+1]>R[j]){
j = j+1; // 从左右子节点中找到最大的那个
}
if(R[j] > temp){
R[i] = R[j];
i = j; // 调换i和j的位置来继续来向下继续调整
j = 2*i;
}
else{
break;
}
}
R[i] = temp;
}
// 建立的完全二叉树节点从0开始计数
void heapSort(int R[], int n){
int i;
int temp;
// 建立堆
for(i = n/2 - 1; i>=0; i--){ // 从第一个非叶节点开始调整,从而得到一个大顶堆
sift(R, i, n);
}
for(i = n-1; i>=1; i--){
temp = R[0];
R[0] = R[i];
R[i] = temp;
sift(R, 0, i-1);
}
}
int main() {
int a[] = {1,2,3,2,5,6};
int i;
heapSort(a, 6);
for(i = 0; i<6;i++){
printf("%d ", a[i]);
}
}
分析:
代码中包含了两部分,一部分是建立最大堆的过程,另一部分是将堆顶元素与末尾元素进行交换,所以时间复杂度是
也就是,最好和最坏的情况下都是。
堆排序的空间复杂度是,这也是相对于快速排序的好处。例如从很多关键字中选择前最大或者最小的,使用堆排序效果最好。
另外,堆排序和简单选择排序一样,也是一种不稳定的排序。
ref:
《2019数据结构高分笔记》(天勤版本)