C語言排序算法之選擇排序(直接選擇排序,堆排序)
前言
本期為大家?guī)淼氖浅R娕判蛩惴ㄖ械倪x擇排序,主要有直接選擇排序以及——堆排序(有點(diǎn)難理解),包您一看就會,快來試試吧~
一、直接選擇排序
1.1 算法思想
每一次從待排序的數(shù)據(jù)元素中選出最?。ɑ蜃畲蟮模┑囊粋€元素,存放在序列的起始位置,直到全部待排序的數(shù)據(jù)元素排完。
在元素集合a[i]--a[n-1]中選擇關(guān)鍵碼最大(小)的數(shù)據(jù)元素 若它不是這組元素中的最后一個(第一個)元素,則將它與這組元素中的最后一個(第一個) 元素交換 在剩余的 [a[i] , a[n-2] …… [a[i+1],a[n-1] ]集合中,重復(fù)上述步驟,直到集合剩 余1個元素。
我們拿一組實(shí)例來感受一下,直接選擇排序是怎么運(yùn)算的:
1.2 代碼實(shí)現(xiàn)
給大家?guī)硪粋€優(yōu)化版本的直接選擇排序,一次遍歷,選出最大數(shù)和最小數(shù),然后交換,相較于傳統(tǒng)的,效率高了許多。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> //交換 void Swap(int* mini, int* maxi) { int tmp = *mini; *mini = *maxi; *maxi = tmp; } //打印 void Print(int* a, int n) { for (int i = 0; i < n; i++) { printf("%d ", a[i]); } } //直接選擇排序 void SelectSort(int *a,int n) { //下標(biāo) int begin = 0; int end = n - 1; while (begin < end) { int mini = begin, maxi = end; //選出最大的給maxi,選出最小的給mini for (int i=begin;i<=end;++i) { if (a[i]>a[mini])//升序 { mini = i; //改兩個if的符號即可實(shí)現(xiàn)升序、降序轉(zhuǎn)換。 } if (a[i] <a[maxi]) { maxi = i; } } //交換 Swap(&a[begin],&a[mini]); //因?yàn)檫€有一種特殊情況,就是begin跟maxi重疊,然后執(zhí)行第一次交換之后,maxi記錄的是最小值 if (begin == maxi) { maxi = mini; } Swap(&a[end], &a[maxi]); ++begin; --end; } } 直接選擇排序 //void SelectSort(int* a, int n)//(升序) //{ // for (int j=0;j<n-1;j++)//整體遍歷 // { // for (int i=j+1;i<n;i++)//遍歷比較 // { // if (a[j] > a[i])//比較交換 // { // int tmp = a[j]; // a[j] = a[i]; // a[i] = tmp; // } // } // } //} int main() { int a[10] = { 3,5,9,7,4,2,1,6,0,8 }; SelectSort(a, sizeof(a) / sizeof(a[0])); //打印 Print(a, sizeof(a) / sizeof(a[0])); return 0; }
1.3 直接選擇排序的特征總結(jié)
- 1.直接選擇排序的算法非常好理解,但是效率不高,實(shí)際中也很少使用
- 2.時間復(fù)雜度:O(N^2) ,直接選擇排序不管數(shù)據(jù)的順序如何,都要遍歷至結(jié)束
- 3.空間復(fù)雜度:O(1)
- 4.穩(wěn)定性:不穩(wěn)定
二、堆排序
2.1 什么是堆?
2.2 判斷是否是堆
我們在給到一個數(shù)組的時候,里面的數(shù)據(jù)往往不是“堆”,我們在使用堆排序的時候,就需要建堆,
堆排序(Heapsort)是指利用堆積樹(堆)這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計的一種的排序算法,它是選擇排序的一種,利用堆來進(jìn)行選擇數(shù)據(jù)。跟著我一起看看具體是怎么操作的。
建小堆排降序,建大堆排升序。
怎樣建堆呢?這里我們的前輩就設(shè)計了一種算法
2.3 向下調(diào)整算法
堆排序的本質(zhì)是選擇排序
向下調(diào)整算法,如果是建小堆(排降序),前提:左右子樹都是小堆。大堆就是反著來。
從根節(jié)點(diǎn)開始,選出左右孩子中小的那一個跟父親比較,如果比父親小就交換,然后繼續(xù)往下調(diào)整,調(diào)整到葉子節(jié)點(diǎn)就停止。
2.4 自底向上的建堆方式
這種建堆方式是從倒數(shù)第二層的節(jié)點(diǎn)(葉子節(jié)點(diǎn)的上一層)開始,從右往左,從下到上的向下進(jìn)行調(diào)整。
2.5 代碼實(shí)現(xiàn)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> //打印數(shù)據(jù) void Printf(int* a, int n) { for (int i = 0; i < n; ++i) { printf("%d ", a[i]); } } //交換,傳地址 void Swap(int* child, int* parent) { int tmp = *child; *child = *parent; *parent = tmp; } //向下調(diào)整算法 //從根節(jié)點(diǎn)開始,如果是建立小堆選出左右孩子中小的那一個,跟父親比較,如果比父親小就交換 void AdjustDwon(int* a, int n, int root)//建小堆 { int parent = root;//父親節(jié)點(diǎn) int child = parent * 2 + 1;//默認(rèn)是左孩子 while (child < n)//葉子節(jié)點(diǎn)下標(biāo)不會超過數(shù)組總下標(biāo)數(shù)n { //選出左右孩子中最小的那一個 if (child+1 < n&& a[child + 1] < a[child]) { child += 1;//用a[child]與父親節(jié)點(diǎn)a[parent]比較 } if (a[child] < a[parent]) { //交換,傳地址 Swap(&a[child], &a[parent]); //交換后,將child,作為根節(jié)點(diǎn)繼續(xù)向下調(diào)整,持續(xù)建堆 parent = child; //新的左孩子 child = parent * 2 + 1; } else { break;//如果不用交換,直接結(jié)束循環(huán) } } } //堆的建立 //大堆要求:樹中所有的父親都>=孩子,根是最大的 //小堆要求:書中所有的父親都<=孩子,根是最小的 //建大堆排升序,建小堆排降序 //建堆的時間復(fù)雜度是O(N); void HeapSort(int *a,int n) { //找父親節(jié)點(diǎn) for (int i=(n-1-1)/2;i>=0;--i) { //向下調(diào)整算法 AdjustDwon(a,n,i); } //大堆或小堆建立完畢,排序 //用主根節(jié)點(diǎn)與最后一個節(jié)點(diǎn)交換位置 int end = n - 1; while (end>0) { //交換,傳地址 Swap(&a[0],&a[end]); //繼續(xù)向下調(diào)整 AdjustDwon(a,end,0); --end; } } //選擇排序—堆排序 int main() { int a[10] = {9,2,5,4,3,1,6,7,8,0}; //堆的建立 HeapSort(a,sizeof(a) / sizeof(a[0])); //打印數(shù)據(jù) Printf(a,sizeof(a) / sizeof(a[0])); return 0; }
2.6 堆排序的特性總結(jié)
- 1.堆排序使用堆來選數(shù),效率高很多
- 2.時間復(fù)雜度:O(N*logN)
- 3.空間復(fù)雜度:O(1)
- 4.穩(wěn)定性:不穩(wěn)定
2.7 堆排序的特性總結(jié)
- 1.堆排序使用堆來選數(shù),效率高很多
- 2.時間復(fù)雜度:O(N*logN)
- 3.空間復(fù)雜度:O(1)
- 4.穩(wěn)定性:不穩(wěn)定
到此這篇關(guān)于C語言排序算法之選擇排序(直接選擇排序,堆排序)的文章就介紹到這了,更多相關(guān)C語言選擇排序 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言數(shù)據(jù)結(jié)構(gòu)詳細(xì)解析二叉樹的操作
二叉樹可以簡單理解為對于一個節(jié)點(diǎn)來說,最多擁有一個上級節(jié)點(diǎn),同時最多具備左右兩個下級節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。本文將詳細(xì)介紹一下C++中二叉樹的實(shí)現(xiàn)和遍歷,需要的可以參考一下2022-04-04C/C++實(shí)現(xiàn)動態(tài)庫動態(tài)加載
在很多項目中,我們多少會用到第三方動態(tài)庫,這些動態(tài)庫一般都是相對固定,使用也很簡單,下面我們就來看看c/c++中如何實(shí)現(xiàn)動態(tài)庫動態(tài)加載吧2024-01-01C++用一棵紅黑樹同時封裝出set與map的實(shí)現(xiàn)代碼
set中存儲的一般為鍵K即可,而map存儲的一般都是鍵值對KV,也就是說他們結(jié)構(gòu)是不同的,那么我們?nèi)绾尾拍苡靡活w紅黑樹同時封裝出set與map兩種容器呢,那么接下來我們具體地來研究下STL庫中是怎樣實(shí)現(xiàn)的,并且進(jìn)行模擬實(shí)現(xiàn),需要的朋友可以參考下2024-03-03C++ 字符串string和整數(shù)int的互相轉(zhuǎn)化操作
這篇文章主要介紹了C++ 字符串string和整數(shù)int的互相轉(zhuǎn)化操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12C語言實(shí)現(xiàn)靜態(tài)存儲通訊錄的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C語言實(shí)現(xiàn)一個靜態(tài)存儲的通訊錄,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下2022-09-09Clion下載安裝使用的詳細(xì)教程(Win+MinGW)
這篇文章主要介紹了Clion下載安裝使用教程(Win+MinGW),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08C++基礎(chǔ)入門教程(七):一些比較特別的基礎(chǔ)語法總結(jié)
這篇文章主要介紹了C++基礎(chǔ)入門教程(七):一些比較特別的基礎(chǔ)語法總結(jié),本文總結(jié)的都是一些特殊的語法,需要的朋友可以參考下2014-11-11