数据结构——选择排序

一、简单选择排序

1.1 基本思想

在待排序数组中选出最小的(或最大)的与第一个位置的数据交换 然后在剩下的待排序数组中找出最小(或最大)的与第二个位置的数据交换,以此类推,直到第 n − 1 n-1 n1个元素。

简单选择排序可以说是冒泡排序的一种改版,它不再两两比较出较小数就进行交换,而是每次遍历比较当前数的后面所有数,最后再把最小的数和当前数进行交换。

1.2 图示

举例:下面是待排序的元素
在这里插入图片描述
黄色为排好序的元素,紫色的表示正在操作的元素
在这里插入图片描述

1.3 简单选择排序代码

void SelectSort(ElemType A[], int n) {
    for(int i = 0;i < n - 1; i++) { 			//一共进行n-1趟
        int min = i; 							//记录最小元素位置
        for(int j = i + 1; j < n; j++) {		//在A[i...n-1]中选择最小的元素
            if(A[j] < A[min]) min = j; 			//更新最小元素位置
        }
        if(min != i) swap(A[i], A[min]); 		//封装的swap函数共移动元素三次
    }
}

1.4 性能分析

1.4.1 复杂度

  • 空间复杂度:仅使用常数个辅助单元, O ( 1 ) O(1) O(1)
  • 时间复杂度:为 O ( n 2 ) O(n^2) O(n2)

1.4.2 稳定性

  • 在第 i i i趟找到最小元素后,和第 i i i个元素交换,可能会导致第 i i i个元素与其含有相同关键字元素的相对位置发生改变,因此简单选择排序是一种不稳定的排序算法。

二、堆排序

2.1 堆

2.1.1 堆的概念

  • 堆是一棵完全二叉树
  • 堆中每个节点的值都必须大于等于(大顶堆)或小于等于(小顶堆)其左右节点的值。

2.1.2 堆的实现

用数组存储完全二叉树,下标为 i i i的结点左子节点下标为 2 i 2i 2i,右子节点下标为 2 i + 1 2i+1 2i+1
在这里插入图片描述

2.1.3 堆的核心操作

在这里插入图片描述

  • 堆的插入和删除时间复杂度都为 O ( l o g n ) O(logn) O(logn)

(1) 往堆中插入一个元素

插入元素后可能导致堆不满足堆的特性,需要对其进行调整让其重新满足堆的特性(堆化),即顺着结点所在路径,向上或向下对比,然后变换。

例如:插入元素22,此时破坏了堆的特性,因此需要进行调整。
在这里插入图片描述
调整之后,得到的堆如下图所示:
在这里插入图片描述
(2) 从堆中删除一个元素:把最后一个结点放到堆顶,然后利用相同的父子结点对比方法,对于不满足父子结点大小关系的,互换两个结点,并且重复进行这个过程,直到满足堆的特性为止。

举例:删除结点33

首先将最后一个结点放到堆顶,替换掉该元素
在这里插入图片描述
然后根据堆的特性对结构进行调整:
在这里插入图片描述
最终得到的结果:
在这里插入图片描述

2.2 堆排序基本思想

首先将存放在 L [ 1... n ] L[1...n] L[1...n]中的 n n n个元素建成初始堆,由于堆本身的特点(以大根堆为例),堆顶元素就是最大值。输出堆顶元素之后,通常将堆底元素送入堆顶,此时根节点已不满足大根堆的性质,堆被破坏,将堆顶元素向下调整使其保持大顶堆的性质,再输出堆顶元素。如此重复,直到堆中仅剩下一个元素位置。

  1. 将待排序的序列构造成一个最大堆,此时序列的最大值为根节点
  2. 依次将根节点与待排序序列的最后一个元素交换
  3. 再维护从根节点到该元素的前一个节点为最大堆,如此往复,最终得到一个递增序列

2.3 堆排序的代码

void BuildMaxHeap(ElemType A[], int len) {
    for(int i = len / 2; i > 0; i --) {
        HeapAdjust(A, i, len);
    }
}

void HeapAdjust(ElemType A[], int k, int len) {
    A[0] = A[k]; 							//暂存根节点
    for(int i = 2 * k; i <= len; i *= 2) { 	//沿key较大的子节点向下筛选
        if(i < len && A[i] < A[i + 1]) { 	//i < len保证有右孩子,A[i] < A[i + 1]比较左右孩子的大小
            i++;
        }
        if(A[0] >= A[i]) break; 			//根节点比左右孩子更大,筛选结束
        else {
            A[k] = A[i]; 					//将A[i]调整到双亲结点上
            k = i; 							//修改k值,以便向下筛选
        }
    }
    A[k] = A[0]; 							//被筛选结点的值放入最终位置
}

void HeapSort(ElemType A[], int n) {
    BuildMaxHeap(A, n);
    for(int i = n; i > 1; i--) { 			//n-1趟的交换和建堆过程
        swap(A[i], A[1]); 					//输出堆顶元素
        HeapAdjust(A, 1, i - 1);
    }
}

2.4 性能分析

2.4.1 复杂度

  • 空间复杂度:仅使用常数个辅助单元, O ( 1 ) O(1) O(1)
  • 时间复杂度:建堆时间为 O ( n ) O(n) O(n),之后有 n − 1 n-1 n1次向下调整操作,每次调整的时间复杂度为为 O ( h ) O(h) O(h),所以在最好、最坏和平均情况下,堆排序的时间复杂度都为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

2.4.2 稳定性

  • 进行筛选时,有可能把后面相同关键字的元素调整到前面,因此堆排序是一种不稳定的排序算法。

2.4.3 适用场景

  • 适合关键字较多的情况。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值