C語(yǔ)言中如何實(shí)現(xiàn)桶排序
C語(yǔ)言實(shí)現(xiàn)桶排序
1.原理
由映射函數(shù)分配初始元素的鍵值,然后將這些元素放入對(duì)應(yīng)鍵值的桶中,并對(duì)桶中的數(shù)據(jù)進(jìn)行排序。然后依次將每個(gè)桶中的元素分出得到排好序的序列。
2.桶排序不是基于比較的排序
將N個(gè)待排序的元素放入桶中只需要O(n)時(shí)間。后續(xù)則是對(duì)桶中元素的排序,所以當(dāng)桶越多的時(shí)候,桶中的元素會(huì)越少,所采取的基于比較的排序算法的時(shí)間則會(huì)大大減少。
所以,這里我們就可確定了一個(gè)重點(diǎn),即是桶的數(shù)量必須是有限個(gè)的,可以經(jīng)過一系列運(yùn)算得到具體數(shù)目的。
3.桶的實(shí)現(xiàn)形式
我們以結(jié)構(gòu)體數(shù)組存儲(chǔ)單鏈表實(shí)現(xiàn)。以結(jié)構(gòu)體數(shù)組的數(shù)組單元來(lái)春初鏈表的頭節(jié)點(diǎn),頭節(jié)點(diǎn)含有兩個(gè)變量,為指針變量(指向下一個(gè)鏈表節(jié)點(diǎn)),和整形變量key(就是如下圖里面頭節(jié)點(diǎn)的值),key表示鏈表的節(jié)點(diǎn)個(gè)數(shù)。
4.桶中元素的排序
因?yàn)橥笆遣扇捂湵韥?lái)實(shí)現(xiàn)的,所以桶中元素的插入就是鏈表中的元素插入。這里要注意分桶為空和非空兩種情況來(lái)插入。
if(p->key == 0){ bucket_table[index]->next = node_branch; (bucket_table[index]->key)++; } //鏈表的插入形式,按照大小從后到大。 else{ while(p->next!=NULL && p->next->key <= node_branch->key){ p=p->next; } node_branch->next = p->next; p->next = node_branch; (bucket_table[j]->key)++; }
4.最后就是將桶中的元素依次輸出
或存放到數(shù)組原始序列的數(shù)組中。
5完整代碼如下
#include<stdio.h> #include<stdlib.h> //整體思想大致為用數(shù)組單元內(nèi)存放的為結(jié)構(gòu)體式的鏈表,每個(gè)鏈表稱為一個(gè)桶。通里面容納的都是鍵值相同的元素。 // 之后便是查看對(duì)應(yīng)元素的鍵值,然后放進(jìn)與之對(duì)應(yīng)的桶,還需注意桶為空和不空的時(shí)的放入方式 //桶元素的插入就是看桶以什么方式的實(shí)現(xiàn)。這里桶以鏈表的形式表現(xiàn),所以桶中元素的插入即為鏈表中數(shù)組的插入。 /*只要桶的數(shù)量夠多,那么之前的放入操作只需花費(fèi)O(n)的時(shí)間,而后面的對(duì)每個(gè)桶里面的元素進(jìn)行排序則需要基于比較的排序算法。因此后面算法的選擇也是 關(guān)乎桶排序速度的重要因素。 */ //桶排序的特點(diǎn)是要有界限分明的桶,而不能是無(wú)限個(gè)桶,也就是說桶排序的個(gè)數(shù)應(yīng)該是可以確定的,有限個(gè)的。 //這里鏈表實(shí)現(xiàn)桶排序的還有要注意的點(diǎn),就是數(shù)組的首地址其實(shí)鏈表的頭節(jié)點(diǎn),有這里的值確定該桶的元素個(gè)數(shù),并由這里出發(fā)尋找其他元素。 typedef struct node *Snode; typedef struct node{ int key; Snode next; }BBc; void sort(int keys[],int keys_size,int bucket_size) { Snode *bucket_table = (Snode *)malloc(bucket_size*sizeof(Snode));//為結(jié)構(gòu)體數(shù)組分配空間。 for(int i=0;i<bucket_size;i++)//對(duì)每個(gè)數(shù)組單元賦予內(nèi)存空間時(shí),初始化每個(gè)結(jié)構(gòu)體數(shù)組單元。 { bucket_table[i] = (Snode)malloc(sizeof(Snode));//這一步是必要的,因?yàn)橹爸皇墙o數(shù)組分配了一連串的存儲(chǔ)空間,但是每個(gè)單元的存儲(chǔ)地址都是 //不確定,也不確定該方式是否會(huì)自動(dòng)地分配內(nèi)存空間給每個(gè)數(shù)組單元。 //那么這樣準(zhǔn)確的給數(shù)組單眼分配的空間是占用之前的分配給數(shù)組的空間,還是重新分撥其他的空間給數(shù)組單元。 //其實(shí)應(yīng)該是分配之前給整個(gè)數(shù)組單元分配的一段存儲(chǔ)空間。 bucket_table[i]->key = 0; bucket_table[i]->next = NULL; }//其實(shí)創(chuàng)建數(shù)組這部分應(yīng)該放在主函數(shù)那里,否則某些功能只能在這個(gè)函數(shù)中使用。 for(int j=0;j<keys_size;j++) { Snode node_branch = (Snode)malloc(sizeof(Snode));//定義一個(gè)節(jié)點(diǎn),滿足條件時(shí)鏈入以鏈表為表現(xiàn)形式的桶。 node_branch->key = keys[j]; node_branch->next = NULL; int index = keys[j]/10; Snode p = bucket_table[index];//p用來(lái)充當(dāng)指向循環(huán)的變量。 //桶為空和非空時(shí)的兩種插入形式 if(p->key == 0){ bucket_table[index]->next = node_branch; (bucket_table[index]->key)++; } //鏈表的插入形式,按照大小從后到大。 else{ while(p->next!=NULL && p->next->key <= node_branch->key){ p=p->next; } node_branch->next = p->next; p->next = node_branch; (bucket_table[j]->key)++; } } //以此輸出每個(gè)桶中的所有元素。 for(int i=0;i<bucket_size;i++){ for(Snode k = bucket_table[i]->next;k!=NULL;k = k->next){ printf(" %d ",k->key); } } } int main() { int keys[] = {49,26,53,47,89,31,72,11,33}; int keys_size = sizeof(keys)/sizeof(int); int bucket_size = keys_size+2; sort(keys,keys_size,bucket_size); }
7.桶排序的時(shí)間復(fù)雜度和空間復(fù)雜度
前面的將n個(gè)待排序元素分到對(duì)應(yīng)鍵值的桶中只需要O(n)時(shí)間,后面則是基于比較的排序算法,基于比較的排序算法最快可以達(dá)到:O(nlogn)時(shí)間。
所以桶里面的排序算法的選擇也會(huì)影響到桶排序的速度。至于空間復(fù)雜度,一般都是占用空間比較大,以便每個(gè)桶中盡可能的達(dá)到一個(gè)元素,這樣桶里面的排序也是O(n)時(shí)間,可以說是非??焖俚?。所以桶排序也是一種空間換時(shí)間的排序。
另外桶排序的元素鍵值應(yīng)該相差不大,以免照成空間的浪費(fèi)。另外,劃分的桶也應(yīng)該是有限個(gè)的。
【排序】圖解桶排序
思想
一句話總結(jié):劃分多個(gè)范圍相同的區(qū)間,每個(gè)子區(qū)間自排序,最后合并。
桶排序是計(jì)數(shù)排序的擴(kuò)展版本,計(jì)數(shù)排序可以看成每個(gè)桶只存儲(chǔ)相同元素,而桶排序每個(gè)桶存儲(chǔ)一定范圍的元素,通過映射函數(shù),將待排序數(shù)組中的元素映射到各個(gè)對(duì)應(yīng)的桶中,對(duì)每個(gè)桶中的元素進(jìn)行排序,最后將非空桶中的元素逐個(gè)放入原序列中。
桶排序需要盡量保證元素分散均勻,否則當(dāng)所有數(shù)據(jù)集中在同一個(gè)桶中時(shí),桶排序失效。
圖解過程
核心代碼
public static void bucketSort(int[] arr){ // 計(jì)算最大值與最小值 int max = Integer.MIN_VALUE; int min = Integer.MAX_VALUE; for(int i = 0; i < arr.length; i++){ max = Math.max(max, arr[i]); min = Math.min(min, arr[i]); } // 計(jì)算桶的數(shù)量 int bucketNum = (max - min) / arr.length + 1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); for(int i = 0; i < bucketNum; i++){ bucketArr.add(new ArrayList<Integer>()); } // 將每個(gè)元素放入桶 for(int i = 0; i < arr.length; i++){ int num = (arr[i] - min) / (arr.length); bucketArr.get(num).add(arr[i]); } // 對(duì)每個(gè)桶進(jìn)行排序 for(int i = 0; i < bucketArr.size(); i++){ Collections.sort(bucketArr.get(i)); } // 將桶中的元素賦值到原序列 int index = 0; for(int i = 0; i < bucketArr.size(); i++){ for(int j = 0; j < bucketArr.get(i).size(); j++){ arr[index++] = bucketArr.get(i).get(j); } } }
復(fù)雜度分析
1. 時(shí)間復(fù)雜度:O(N + C)
對(duì)于待排序序列大小為 N,共分為 M 個(gè)桶,主要步驟有:
- N 次循環(huán),將每個(gè)元素裝入對(duì)應(yīng)的桶中
- M 次循環(huán),對(duì)每個(gè)桶中的數(shù)據(jù)進(jìn)行排序(平均每個(gè)桶有 N/M 個(gè)元素)
一般使用較為快速的排序算法,時(shí)間復(fù)雜度為O(NlogN),實(shí)際的桶排序過程是以鏈表形式插入的。
整個(gè)桶排序的時(shí)間復(fù)雜度為:
O(N)+O(M*(N/M*log(N/M)))=O(N*(log(N/M)+1))
當(dāng) N = M 時(shí),復(fù)雜度為 O(N)
2. 額外空間復(fù)雜度:O(N + M)
穩(wěn)定性分析
桶排序的穩(wěn)定性取決于桶內(nèi)排序使用的算法。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Java算法之桶排序Bucket?Sort詳解
- Java排序算法之桶排序詳解
- 基于Java實(shí)現(xiàn)計(jì)數(shù)排序,桶排序和基數(shù)排序
- Java桶排序之基數(shù)排序詳解
- C/C++語(yǔ)言八大排序算法之桶排序全過程示例詳解
- C++ 實(shí)現(xiàn)桶排序的示例代碼
- 10個(gè)python3常用排序算法詳細(xì)說明與實(shí)例(快速排序,冒泡排序,桶排序,基數(shù)排序,堆排序,希爾排序,歸并排序,計(jì)數(shù)排序)
- 詳解C++ 桶排序(BucketSort)
- python實(shí)現(xiàn)計(jì)數(shù)排序與桶排序?qū)嵗a
- C#實(shí)現(xiàn)桶排序算法的示例代碼
相關(guān)文章
C++中運(yùn)算符 &和&&、|和|| 的詳解及區(qū)別
這篇文章主要介紹了C++中運(yùn)算符 &和&&、|和|| 的詳解及區(qū)別的相關(guān)資料,這里舉例說明該如何區(qū)別他們的不同,需要的朋友可以參考下2016-11-11visual?studio?2022一個(gè)不易發(fā)現(xiàn)的問題
本文主要介紹了visual?studio?2022一個(gè)不易發(fā)現(xiàn)的問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能
所謂高斯噪聲是指它的概率密度函數(shù)服從高斯分布(即正態(tài)分布)的一類噪聲,這篇文章主要給大家介紹了關(guān)于C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能的相關(guān)資料,需要的朋友可以參考下2021-07-07vs2022?qt環(huán)境搭建調(diào)試的方法步驟
最近net6和vs2022發(fā)布,本文就詳細(xì)的介紹一下vs2022?qt環(huán)境搭建調(diào)試的方法步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Qt串口通信開發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法
這篇文章主要介紹了Qt串口通信開發(fā)之QSerialPort模塊Qt串口通信接收數(shù)據(jù)不完整的解決方法,需要的朋友可以參考下2020-03-03數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)-用棧實(shí)現(xiàn)表達(dá)式求值的方法詳解
本篇文章是對(duì)在c語(yǔ)言中用棧實(shí)現(xiàn)表達(dá)式求值的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05