选择排序
- 基本思想:
选择排序将序列分为已经有序和暂时无序两部分,遍历暂时无序的部分,将其中最小的元素放到有序序列的末尾,直到全部待排序的元素排完为止。
- 排序分类:
选择排序分为直接选择排序和堆排序
一:直接选择排序
- 排序过程:
在无序部分中选取最小的元素,若它不是这组无序部分中的第一个元素,则将它与这组无序部分中的第一个元素交换,即此元素进入有序序列,在剩余的无序部分继续重复以上步骤,直到无序部分的元素数为1时排序结束。
- 代码实现:
在算法实现时,每一趟确定最小元素的时候会通过不断地比较交换来使得首位置为当前最小,交换是个比较耗时的操作。其实我们很容易发现,在还未完全确定当前最小元素之前,这些交换都是无意义的。我们可以通过设置一个变量min,每一次比较仅存储较小元素的数组下标,当轮循环结束之后,那这个变量存储的就是当前最小元素的下标,此时再执行交换操作即可。
C++实现:
#include<iostream>
#include<vector>
using namespace std;
void Selectsort(vector<int>& array){
int sz = array.size();
for (int disorder = 0; disorder < sz - 1; disorder++){
// 设置min存储最小元素的下标(无序区间的首元素)
int min = disorder;
// 遍历寻找无序区间中的最小元素的下标
for (int goal = disorder + 1; goal < sz; goal++){
if (array[goal] < array[min]){
min = goal;
}
}
// 如果最小元素不是无序区间中的第一个元素则与无序区间的首元素交换
if (min != disorder){
swap(array[min], array[disorder]);
}
}
}
int main(){
vector<int> array = { 7, 4, 5, 9, 8, 2, 1 };
Selectsort(array);
for (const auto& e : array){
cout << e << " ";
}
cout << endl;
}
输出结果:1 2 4 5 7 8 9
C实现:
#include<stdio.h>
void Print(int* array, int sz){
for (int cur = 0; cur < sz; cur++){
printf("%d ", array[cur]);
}
printf("\n");
}
void Swap(int* num1, int* num2){
int temp = *num1;
*num1 = *num2;
*num2 = temp;
}
void Selectsort(int* array, int sz){
for (int disorder = 0; disorder < sz - 1; disorder++){
int min = disorder;
for (int goal = disorder + 1; goal < sz; goal++){
if (array[goal] < array[min]){
min = goal;
}
}
if (min != disorder){
Swap(&array[disorder], &array[min]);
}
}
}
int main(){
int array[] = { 7, 4, 5, 9, 8, 2, 1 };
int sz = sizeof(array) / sizeof(array[0]);
Selectsort(array, sz);
Print(array, sz);
}
输出结果:1 2 4 5 7 8 9
- 特性总结:
1.时间复杂度:O(N2)
2.空间复杂度:O(1)
3.直接选择排序是一种不稳定的排序算法
二:堆排序
- 排序过程:
将无序序列进行建堆操作,排升序建大堆,排降序建小堆。将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端,重新调整堆结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
- 代码实现:
堆排序的基础是建堆,而建堆的基础是向下调整算法(前题:左右子树都是大堆):找出左右孩子中值较大的结点和父结点进行交换,并将下标更改parent = child,child = parent×2+1继续向下调整,直到左右孩子的值都比目标结点的值小则调整完毕。(默认排升序)
建堆是从最后一个非叶子结点开始进行向下调整,将一个无序序列调整为满足堆的性质。
C++实现:
#include<iostream>
#include<vector>
using namespace std;
void Adjustdown(vector<int>& array, int start, int end){
int parent = start;
int child = parent * 2 + 1;
while (child < end){
// 选出左右孩子中较大的
if (child + 1 < end && array[child + 1] > array[child]){
child++;
}
// 孩子和父亲比较
if (array[child] > array[parent]){
// 孩子大于父亲则交换
swap(array[child], array[parent]);
// 向下继续传递
parent = child;
child = parent * 2 + 1;
}
else{
break;
}
}
}
void Heapsort(vector<int>& array){
// 建堆:从最后一个非叶子结点(最后一个结点的父结点)开始进行向下调整
int sz = array.size();
for (int index = (sz - 1 - 1) / 2; index >= 0; index--){
Adjustdown(array, index, sz);
}
// 堆排序
while (sz > 0){
swap(array[0], array[sz - 1]);
sz--;
Adjustdown(array, 0, sz);
}
}
int main(){
vector<int> array = { 4, 6, 8, 5, 9 };
Heapsort(array);
for (const auto& e : array){
cout << e << " ";
}
cout << endl;
}
输出结果:4 5 6 8 9
C实现:
#include<stdio.h>
void Swap(int* num1, int* num2){
int temp = *num1;
*num1 = *num2;
*num2 = temp;
}
void Print(int* array, int sz){
for (int cur = 0; cur < sz; cur++){
printf("%d ", array[cur]);
}
printf("\n");
}
void Adjustdown(int* array, int start, int end){
int parent = start;
int child = parent * 2 + 1;
while (child < end){
// 选出左右孩子中较大的
if (child + 1 < end && array[child + 1] > array[child]){
child++;
}
// 孩子和父亲比较
if (array[child] > array[parent]){
// 孩子大于父亲则交换
Swap(&array[child], &array[parent]);
// 向下继续传递
parent = child;
child = parent * 2 + 1;
}
else{
break;
}
}
}
void Heapsort(int* array, int sz){
// 建堆:从最后一个非叶子结点(最后一个结点的父结点)开始进行向下调整
for (int index = (sz - 1 - 1) / 2; index >= 0; index--){
Adjustdown(array, index, sz);
}
// 堆排序
while (sz > 0){
Swap(&array[0], &array[sz - 1]);
sz--;
Adjustdown(array, 0, sz);
}
}
int main(){
int array[] = { 4, 6, 8, 5, 9 };
int sz = sizeof(array) / sizeof(array[0]);
Heapsort(array,sz);
Print(array, sz);
}
- 特性总结:
1.时间复杂度:O(N×logN)
2.空间复杂度:O(1)
3.堆排序是一种不稳定的排序算法