選擇排序
- 基本思想:
選擇排序將序列分爲已經有序和暫時無序兩部分,遍歷暫時無序的部分,將其中最小的元素放到有序序列的末尾,直到全部待排序的元素排完爲止。
- 排序分類:
選擇排序分爲直接選擇排序和堆排序
一:直接選擇排序
- 排序過程:
在無序部分中選取最小的元素,若它不是這組無序部分中的第一個元素,則將它與這組無序部分中的第一個元素交換,即此元素進入有序序列,在剩餘的無序部分繼續重複以上步驟,直到無序部分的元素數爲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.堆排序是一種不穩定的排序算法