选择排序(简单选择排序堆排序的算法思想及代码实现)
Posted 薛定谔的猫ovo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了选择排序(简单选择排序堆排序的算法思想及代码实现)相关的知识,希望对你有一定的参考价值。
选择排序
选择排序的基本思想是:
在由
n
n
n个元素组成的序列中,选择一个具有最小(或最大)关键字的元素,把它加入到有序序列中,然后在剩余的元素序列中再选一个具有最小(或最大)关键码的元素,把它加入到有序序列中。如此继续,直到元素序列中只剩下一个元素为止,排序结束。
简单选择排序
简单选择排序的基本思想
根据选择排序的思想,可以得出简单选择排序算法的思想:
假设待排序表为
L
[
1...
n
]
L[1...n]
L[1...n],第
i
i
i趟排序从
L
[
i
.
.
.
n
]
L[i...n]
L[i...n]中选择关键字最小的元素与
L
[
i
]
L[i]
L[i]交换,每一趟排序可以确定一个元素的最终位置,这样经过
n
−
1
n-1
n−1趟排序就可以使得整个排序表有序。
简单选择排序的实现代码
//简单选择排序
void SelectSort(SeqList &L)
int n = L.n;
for(int i=0; i<n-1; i++) //一共进行n-1趟排序
int min=i; //记录最小元素的位置
for(int j=i+1; j<n; j++) //从L.data[i...n-1]中选择最小的元素
if(L.data[j] < L.data[min])
min = j; //更新最小元素的位置
if(min!=i)
swap(L.data[i], L.data[min]); //与第i个位置交换
简单选择排序的性能分析
空间复杂度
仅使用了常数个辅助单元,只需要一个用于数据交换的工作单元,故空间复杂度为 O ( 1 ) O(1) O(1)。
时间复杂度
简单选择排序的关键字的比较次数和元素的初始排列状态无关,而元素的移动次数与元素的初始排列状态有关。
总的关键字的比较次数为 n − 1 + n − 2 + . . . + 1 = n ( n − 1 ) 2 n-1+n-2+...+1 = \\fracn(n-1)2 n−1+n−2+...+1=2n(n−1)。
元素的移动次数:
- 最好情况:元素已经有序(正序),移动次数为 0 0 0
- 最坏情况:每一次都要进行交换,移动次数为 3 ( n − 1 ) 3(n-1) 3(n−1)
故简单选择排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2)
稳定性
在第 i i i 趟找到最小元素后,和第 i i i 个元素交换,可能会导致第 i i i 个元素与其含有相同关键字元素的相对位置发生改变。故简单选择排序是一种不稳定的排序方法。
堆排序
堆排序的基本思想
堆在逻辑上是一个完全二叉树组织的非线性结构,在物理上是用一个一维数组存储的。
堆的定义如下:
n
n
n个关键字序列
L
[
1...
n
]
L[1...n]
L[1...n]称为堆,当且仅当该序列满足:
L
[
i
]
≤
L
[
2
i
]
L[i]≤L[2i]
L[i]≤L[2i] 且
L
[
i
]
≤
L
[
2
i
+
1
]
L[i]≤L[2i+1]
L[i]≤L[2i+1] (小根堆)
或者
L
[
i
]
≥
L
[
2
i
]
L[i]≥L[2i]
L[i]≥L[2i] 且
L
[
i
]
≥
L
[
2
i
]
L[i]≥L[2i]
L[i]≥L[2i] (大根堆) (
1
≤
i
≤
⌊
n
/
2
⌋
1≤i≤\\lfloor n/2 \\rfloor
1≤i≤⌊n/2⌋)
显然,在大根堆中,最大元素存放在根结点中,且对其任一非根结点,它的值小于或等于其双亲结点值;
在小根堆中,根结点是最小元素,且对其任一非根结点,它的值大于或等于其双亲结点值。
堆排序是一种树形选择排序方法,其特点是:在排序过程中,将 L [ 1... n ] L[1...n] L[1...n]视为一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。
堆排序的排序算法的步骤如下:
1、 把数组heap中的元素序列用筛选法
A
d
j
u
s
t
D
o
w
n
AdjustDown
AdjustDown调整为大根堆(即初始堆)。
2、 令 i i i 从 n − 1 n-1 n−1 循环到 1 1 1,重复执行。
- 处于堆顶的元素 h e a p [ 0 ] heap[0] heap[0] 与 h e a p [ i ] heap[i] heap[i] 对调,把有最大关键字的元素交换到最后。
- 对前面的 i − 1 i-1 i−1 个元素,使用堆的筛选算法 A d j u s t D o w n AdjustDown AdjustDown重新调整为大根堆。
3、 循环结束,最后得到全部排序好的元素序列。
堆排序的实现代码
//大根堆的向下调整算法
void AdjustDown(Heap &H, int k, int len)
//将元素k向下调整
H.heap[0] = H.heap[k]; //暂存H.heap[k]的值
for(int i=2*k; i<=len; i*=2) //延关键字较大的子结点向下筛选
if(i<len && H.heap[i]<H.heap[i+1])
i++; //选取关键字较大的子结点的下标
if(H.heap[0] >= H.heap[i])
break; //筛选结束
else
H.heap[k] = H.heap[i]; //将H.heap[i]调整到双亲结点上
k = i; //修改k值,以便继续向下筛选
H.heap[k] = H.heap[0]; //被筛选结点的值放入最终位置
//建立大根堆
void CreateMaxHeap(Heap &H, int arr[], int len)
for(int i=0; i<len; i++)
H.heap[i+1] = arr[i]; //从下标1开始存储
H.n = len+1; //长度需加1
for(int i=len/2; i>0; i--)
AdjustDown(H,i,len); //从i=n/2 ~ 1,反复调整堆
//堆排序
void HeapSort(Heap &H, int arr[], int len)
CreateMaxHeap(H, arr, len); //初始建堆
for(int i=len; i>1; i--) //n-1趟交换和建堆过程
swap(H.heap[1], H.heap[i]); //输出堆顶元素(和堆底元素交换)
AdjustDown(H, 1, i-1); //把剩余的i-1个元素整理称堆
对于大根堆的建立过程请参照文章堆的定义及其基本操作(存储、建立、插入、删除),由于文章篇幅原因不再赘述。
堆排序的性能分析
空间复杂度
只用了常数个辅助单元(对调元素),故空间复杂度为 O ( 1 ) O(1) O(1)。
时间复杂度
建堆时间为 O ( n ) O(n) O(n),之后有 n − 1 n-1 n−1 次向下调整操作,每次调整的时间复杂度为 O ( h ) O(h) O(h),故在最好、最坏和平均情况下,堆排序的时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)。
稳定性
由于在进行筛选时,有可能把后面相同关键字的元素调整到前面,所以堆排序算法是一种不稳定的排序算法。
完整代码
简单选择排序
#include<bits/stdc++.h>
using namespace std;
//设待排序序列存储在静态分配的顺序表中
#define maxSize 20
typedef struct
int data[maxSize];
int n;
SeqList;
//输入待排序列并存入顺序表中
void CreateList(SeqList &L, int n)
L.n = n;
for(int i=0; i<n; i++)
int x;
cin>>x;
L.data[i] = x;
//输出序列
void PrintList(SeqList L)
for(int i=0; i<L.n; i++)
cout<<L.data[i]<<" ";
cout<<endl<<endl;
//简单选择排序
void SelectSort(SeqList &L)
int n = L.n;
for(int i=0; i<n-1; i++) //一共进行n-1趟排序
int min=i; //记录最小元素的位置
for(int j=i+1; j<n; j++) //从L.data[i...n-1]中选择最小的元素
if(L.data[j] < L.data[min])
min = j; //更新最小元素的位置
if(min!=i)
swap(L.data[i], L.data[min]); //与第i个位置交换
int main()
SeqList L;
int n;
cin>>n; //元素个数
CreateList(L, n);
cout<<endl<<"L: ";
PrintList(L);
//简单选择排序
SelectSort(L);
PrintList(L);
return 0;
运行结果:
堆排序
#include<bits/stdc++.h>
using namespace std;
//堆的存储结构
#define maxSize 20
typedef struct
int heap[maxSize]; //存放堆中元素的数组,一般从下标1开始存储(为了对应完全二叉树)
int n; //当前元素个数
Heap;
//输出序列
void PrintList(Heap &H)
for(int i=1; i<H.n; i++)
cout<<H.heap[i]<<" ";
cout<<endl<<endl;
//大根堆的向下调整算法
void AdjustDown(Heap &H, int k, int len)
//将元素k向下调整
H.heap[0] = H.heap[k]; //暂存H.heap[k]的值
for(int i=2*k; i<=len; i*=2) //延关键字较大的子结点向下筛选
if(i<len && H.heap[i]<H.heap[i+1])
i++; //选取关键字较大的子结点的下标
if(H.heap[0] >= H.heap[i])
break; //筛选结束
else
H.heap[k] = H.heap[i]; //将H.heap[i]调整到双亲结点上
k = i; //修改k值,以便继续向下筛选
H.heap[k] = H.heap[0]; //被筛选结点的值放入最终位置
//建立大根堆
void CreateMaxHeap(Heap &H, int arr[], int len)
for(int i=0; i<len; i++)
H.heap[i+1] = arr[i]; //从下标1开始存储
以上是关于选择排序(简单选择排序堆排序的算法思想及代码实现)的主要内容,如果未能解决你的问题,请参考以下文章