10种C++排序算法
生活随笔
收集整理的這篇文章主要介紹了
10种C++排序算法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 1.插入排序
- 2.冒泡排序
- 3.選擇排序
- 4.希爾排序
- 5.歸并排序
- 6.快速排序
- 6.1.快速排序(改進)
- 7.堆排序
- 8.計數排序
- 9.桶排序
- 9.1.桶排序(改進)
- 10.基數排序
題目:LeetCode 912. 排序數組(10種排序)
下面博文,為早期學習寫的,很不簡潔,請參考上面題目的版本。
1.插入排序
/** 1.插入排序* 每次在末尾插入一個數字,依次向前比較,類似與抓撲克牌(插入排序,每次左邊的子序列都是有序的)*/ void insertsort(size_t dsize, int *arr) //dsize是數組arr的長度 {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}for (size_t i = 0; i != dsize; ++i) {for (size_t j = i; j > 0 && arr[j-1] > arr[j]; --j)//每次的子列都是有序的,判斷條件可寫在for(內),否則不可(這么做減少運行次數)//每次和有序數組最后一個比較,向前搜索,直到找到位置停止{swap(arr[j-1], arr[j]);}} }/*時間復雜度分析最好情況:原數列有序,每次放在最后就好了,復雜度為n最壞情況:原數列倒序的,每次都要挪到最前面,1+2+...+n-1=n(n-1)/2*/2.冒泡排序
/**2.冒泡排序,數從前向后冒泡比較,冒泡過程中,數列無序狀態*/ void bsort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}bool arrisok = false;for(size_t i = 0; i != dsize; ++i){ arrisok = true;for(size_t j=1;j <= dsize-1-i;++j) //后面的數都排好了,所以j<=dsize-1-i,不減i,也可,但時間長{ if(arr[j-1]> arr[j]) //比較的過程中是無序的,判斷條件寫在for{}里//寫在for()里會出現局部條件不滿足就退出for循環了,以至于還未排序完{swap(arr[j-1],arr[j]);arrisok = false; //如果交換過,則數組未完成排序}}if(arrisok == true){return; //經過一輪冒泡后,數據沒有發生交換則數據為有序,可退出函數,可減少12%時間(用自己的程序)}} }/*時間復雜度分析最好情況:原數列有序,復雜度為n最壞情況:原數列倒序的,每次都要從前挪到后面,n-1+n-2+...+1=n(n-1)/2*/3.選擇排序
/**3.選擇排序,每次找出數值最小的下標,交換未排序區域第一個與最小的(與冒泡的區別,只交換一次)*/ void selecsort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}size_t mindex=0;for(size_t i =0; i!= dsize-1; ++i){ mindex= i ;for(size_t j=i+1;j!=dsize;++j){ if(arr[j]< arr[mindex]) //子列為無序的,判斷條件寫在for{}里{ mindex = j; //記錄下最小數的下標}}swap(arr[i],arr[mindex]);} }/*時間復雜度分析最好情況:(與最壞一樣)最壞情況:每次都要從前到后比較,n-1+n-2+...+1=n(n-1)/2*/4.希爾排序
/** 4.希爾排序,分組插入排序,相隔gap個數的都為一組,從第gap個數開始*/ void shellsort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}size_t gap = 1;size_t j=0;for(gap=dsize/2;gap> 0;gap /= 2){ for(size_t i = gap;i < dsize;++i){ for(j=i;int(j-gap)>=0 && arr[j-gap]> arr[j];j -= gap) //int()轉換類型,避免溢出,相當于分組的插入排序{swap(arr[j-gap],arr[j]);}}} }/*時間復雜度分析 [參考](https://blog.csdn.net/ginnosx/article/details/12263619)最好情況:最壞情況:*/5.歸并排序
/**5.歸并排序,自頂向下,遞歸*/ void merge(int *arr,size_t left,size_t mid,size_t right) {int len = right - left + 1;int *temp = new int [len]; //數組較長時請用new,不然棧空間容易溢出size_t index = 0;size_t i = left, j = mid + 1;while(i <= mid && j <= right){temp[index++] = arr[i]<= arr[j]? arr[i++]: arr[j++]; //對兩邊的數組從小到大放入臨時空間}while(i <= mid) //比較完后,左半邊有沒放進去的,直接寫入{temp[index++]= arr[i++];}while(j <= right) //比較完后,右半邊有沒有放進去的,直接寫入{temp[index++]= arr[j++];}for(int k = 0;k< len;++k){arr[left++ ]= temp[k]; //把有序的臨時數組寫入原來數組的起始位置}delete [] temp; //釋放空間temp = NULL; //指針置空 } void divide(int *arr,size_t left,size_t right) {if(left == right){ return;}size_t mid = (left+right)/2; //找出區間中部的數,將數組分段divide(arr,left,mid); //遞歸調用,對左邊繼續分段;divide(arr,mid+1,right); //遞歸調用,對右邊繼續分段;merge(arr,left,mid,right); //對左右兩半進行排序合并成一小段有序的數組 } void mergesort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}size_t left = 0, right = dsize-1;divide(arr,left,right); }6.快速排序
/*1. 6.快速排序2. 對數組找出一個中間大小的合適哨兵,把小于哨兵的放左邊,大于哨兵的放右邊,中間是等于哨兵的3. 分別對左右遞歸調用快排*/ size_t parr [2]; //全局變量,全局變量不好,長期占用內存,每個函數都可訪問,容易被修改,函數間相互干擾 void selectmedianofthree(int *arr, size_t left, size_t right) //找出中間大小的數做哨兵 {size_t mid = left + (right - left)/2; //中部數據的下標if(arr[mid]>arr[right]) {swap(arr[mid],arr[right]);}if(arr[left]>arr[right]){swap(arr[left],arr[right]);}if(arr[mid]>arr[left]){swap(arr[mid],arr[left]); //把中間大小的數值放到首位} } void partion(int *arr, size_t left, size_t right) //數據分段 {selectmedianofthree(arr,left,right); //找出中間大小的哨兵,讓分段盡量均勻,提高效率size_t lessPnum = 0, largePnum=0;int pval = arr[left]; //中間大小的數賦值給哨兵int *temp = new int [right-left+1]; //開辟堆空間存放臨時數組int tempLindex=0, tempRindex = right-left; //臨時數組的首末位下標for(int i = left+1; i <= right; ++i){if(pval > arr[i]) //比哨兵小的放在左邊,從左邊首位往中間寫入,記錄下比哨兵小的有多少個{temp[tempLindex++] = arr[i];++lessPnum;}if(pval < arr[i]) 比哨兵大的放在右邊,從右邊末位中間寫入,記錄下比哨兵大的有多少個{temp[tempRindex--] = arr[i];largePnum++;}}for( ; tempLindex <= tempRindex; ++tempLindex)//中間還未被寫入的位置,寫入哨兵(哨兵可能是多個相同的值){temp[tempLindex] = pval;}for(int i = left, j=0; i <= right; ++i){arr[i] = temp[j++]; //把分好段的數組寫回原數組{[小于哨兵的],[等于哨兵的],[大于哨兵的]}}delete [] temp; //釋放臨時數組temp = NULL; //指針置空parr[0]=lessPnum;parr[1]=largePnum; //可以采用被調用函數的參數引用回傳給主函數 } void qsort(int *arr, size_t left, size_t right, int deep) {if(left >= right){return;}else if(right-left == 1) //只有兩個數直接比較交換(也可以設置長度小于X(比如10),調用其他排序,如歸并,減少不必要的調用快排){if(arr[left]>arr[right]){ swap(arr[left], arr[right]);}}else{partion(arr,left,right); //數據分段,{[小于哨兵的],[等于哨兵的],[大于哨兵的]}size_t pl_index = left + parr[0]; //首位哨兵的下標size_t pr_index = right - parr[1]; //末位哨兵的下標if(pr_index == right && pl_index != left) //哨兵群位于數組最右邊,且左邊還有數據{qsort(arr,left,pl_index-1,deep); //只對左邊非哨兵數據快排}else if(pl_index == left && pr_index != right) //哨兵群位于數組最左邊,且右邊還有數據{ qsort(arr,pr_index+1,right,deep); //只對右邊非哨兵數據快排}else if(pl_index == left && pr_index == right) //全部是哨兵,兩側無數據,退出{return;}else //兩側都有非哨兵數據,對兩側調用快排{qsort(arr,left,pl_index-1,deep);qsort(arr,pr_index+1,right,deep);}} } void quicksort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}size_t left = 0, right = dsize-1;int deep = 0; //可以打印顯示出調用的層數qsort(arr,left,right,deep); }6.1.快速排序(改進)
/** 6-1.快速排序(改進:不使用全局變量傳遞參數)* 對數組找出一個中間大小的合適哨兵,把小于哨兵的放左邊,大于哨兵的放右邊,中間是等于哨兵的* 分別對左右遞歸調用快排*/ void selectmedianofthree(int *arr, size_t left, size_t right) //找出中間大小的數做哨兵 {size_t mid = left + (right - left)/2; //中部數據的下標if(arr[mid]>arr[right]) {swap(arr[mid],arr[right]);}if(arr[left]>arr[right]){swap(arr[left],arr[right]);}if(arr[mid]>arr[left]){swap(arr[mid],arr[left]); //把中間大小的數值放到首位} } void partion(int *arr, size_t left, size_t right, size_t &lessPnum, size_t &largePnum)//數據分段 {selectmedianofthree(arr,left,right); //找出中間大小的哨兵,讓分段盡量均勻,提高效率int pval = arr[left]; //中間大小的數賦值給哨兵int *temp = new int [right-left+1]; //開辟堆空間存放臨時數組int tempLindex=0, tempRindex = right-left; //臨時數組的首末位下標for(int i = left+1; i <= right; ++i){if(pval > arr[i]) //比哨兵小的放在左邊,從左邊首位往中間寫入,記錄下比哨兵小的有多少個{temp[tempLindex++] = arr[i];++lessPnum;}if(pval < arr[i]) 比哨兵大的放在右邊,從右邊末位中間寫入,記錄下比哨兵大的有多少個{temp[tempRindex--] = arr[i];largePnum++;}}for( ; tempLindex <= tempRindex; ++tempLindex)//中間還未被寫入的位置,寫入哨兵(哨兵可能是多個相同的值){temp[tempLindex] = pval;}for(int i = left, j=0; i <= right; ++i){arr[i] = temp[j++]; //把分好段的數組寫回原數組{[小于哨兵的],[等于哨兵的],[大于哨兵的]}}delete [] temp; //釋放臨時數組temp = NULL; //指針置空 } void qsort(int *arr, size_t left, size_t right, int deep) {if(left >= right){return;}else if(right-left == 1)//只有兩個數直接比較交換(也可以設置長度小于X(比如10),調用其他排序,如歸并,減少不必要的調用快排){if(arr[left]>arr[right]){swap(arr[left], arr[right]);}}else if(right-left > 1 && right-left < 20) //數組長度較小時,調用希爾排序,減少調用快排{size_t len = right - left + 1;shellsort(len, &arr[left]); //數組首地址為&arr[left]}else{size_t lessPnum = 0, largePnum=0;partion(arr,left,right,lessPnum,largePnum); //數據分段,{[小于哨兵],[等于哨兵],[大于哨兵]}size_t pl_index = left + lessPnum; //首位哨兵的下標size_t pr_index = right - largePnum; //末位哨兵的下標if(pr_index == right && pl_index != left) //哨兵群位于數組最右邊,且左邊還有數據{qsort(arr,left,pl_index-1,deep); //只對左邊非哨兵數據快排}else if(pl_index == left && pr_index != right) //哨兵群位于數組最左邊,且右邊還有數據{ qsort(arr,pr_index+1,right,deep); //只對右邊非哨兵數據快排}else if(pl_index == left && pr_index == right) //全部是哨兵,兩側無數據,退出{return;}else //兩側都有非哨兵數據,對兩側調用快排{qsort(arr,left,pl_index-1,deep);qsort(arr,pr_index+1,right,deep);}} } void quicksort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}size_t left = 0, right = dsize-1;int deep = 0; //可以打印顯示出調用的層數qsort(arr,left,right,deep); }7.堆排序
/** 7.堆排序,建堆(升序建大堆,降序建小堆)* 交換堆頂與最后一位無序數據* 調整堆,遞歸,交換調整*/ void adjust(int *arr, size_t i, size_t dsize) {size_t LowerLeftNode = i*2+1; //下一層左邊的節點while(LowerLeftNode < dsize){if(LowerLeftNode+1< dsize && arr[LowerLeftNode]< arr[LowerLeftNode+1] ){++LowerLeftNode;}if(arr[i]> arr[LowerLeftNode]) //如果上層節點大于小面兩個子節點,結束{break;}swap(arr[i], arr[LowerLeftNode]);i = LowerLeftNode; //往下循環調整LowerLeftNode = i*2+1;} } void makeheap(size_t dsize, int *arr) {for(size_t i = dsize/2 -1; i >=0;--i) //從后往前,底下第二層(第一個有子節點的元素的下標){adjust(arr,i,dsize); //有子節點,調整堆(從i節點往下,末位固定dsize-1)if(i == 0)break;} } void heapsort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}makeheap(dsize,arr); //建立堆,大堆,上面父節點比子節點大size_t i = 0;for(i=dsize-1;i>=0;--i) //從最后一位開始,與堆頂交換,調整堆,尾部數據減1{swap(arr[i],arr[0]); //把最大的arr[0]與隊尾交換adjust(arr,0,i); //從第0位往下開始調整,末位不固定,數組長度i,每次減一if(i == 0) //i = 0,退出,防止--i,size_t溢出break;} }8.計數排序
/**8.計數排序,找出數列中最大最小的數,并記錄下每一個元素的個數,然后放回*/ void countsort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}int index = 0;int min, max;min = max = arr[0];for(int i = 1; i<dsize;++i){min=(arr[i] < min)? arr[i] : min;max=(arr[i] > max)? arr[i] : max;}//創建新的數組存放int k = max -min +1;int *temp = new int [k](); //()初始化為0for(int i = 0;i< dsize;++i){++temp[arr[i]-min]; //記錄每個數的個數,存入數組}for(int i = min; i <= max;++i){for(int j = 0; j < temp[i-min];++j) //存放元素個數不為0的,才進入循環{arr[index++] = i; //把元素值寫回數組}}delete [] temp;temp = NULL; }9.桶排序
/**9.桶排序,將數據按規則分組,對各小組再分別排序*/ void bucketsort(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}int maxval = arr[0];int minval = arr[0];for(int i = 0; i != dsize; ++i) //遍歷數組,找出最大最小元素{maxval = maxval > arr[i] ? maxval : arr[i];minval = minval < arr[i] ? minval : arr[i];}if(maxval == minval) //如果最大==最小,數組不需要排序(排除下面div=0,進不了位,div總是為0){return;}else{int space = 10000; //每個桶數元素值的最大差值(區間大小)int div = ceil((double)(maxval-minval)/space); //桶的個數,ceil取進位數(先double強轉(float的精度不夠高),避免丟失小數點)//space 太小,桶個數太多,會造成棧空間溢出int numsofeachbucket[div]; //開辟數組,存放每個桶內的元素個數//知識點://1.桶的個數跟數據相關,space是固定的,但是桶的個數會根據環境變化,不能確保程序在其他環境下正確運行//2.div很大時,int numsofeachbucket[div],直接撐爆棧空間,需要采用new 開辟堆空間//3.當(maxval-minval)是space的整數倍的時候,段錯誤,訪問越界//第3個問題改成int div = floor((double)(maxval-minval)/space)+1;即可for(size_t i =0; i != div; ++i){numsofeachbucket[i] = 0; //每個桶的元素個數初始化為0}for(size_t i = 0; i != dsize; ++i){++numsofeachbucket[(arr[i]-minval)/space]; //把元素按大小分到不同的桶,并增加該桶元素個數}int **p = new int* [div]; //開辟堆空間,指針數組,每個元素(指針)指向每個桶的0位int **temp = new int* [div]; //臨時數組,保存某些指針的初始值,方便delete(delete時,指針必須位于初始位置)int **temp_1 = new int* [div]; //同上(改進啟發:數組長度是一定的,申請一次內存,知道每個桶始末位置即可)for(size_t i = 0; i != div; ++i){if(numsofeachbucket[i] != 0) //桶內有元素(沒有元素就不要申請空間了,如申請了,指針的地址是不為NULL的,會出問題){p[i] = new int [numsofeachbucket[i]]; //指針數組,每個元素(指針)指向每個桶的0位temp[i] = p[i]; //記錄每個桶申請的空間的初始地址,后面delete temp_1[i]即可刪除開辟的p[i] new出的空間temp_1[i] = p[i]; //記錄初始地址,后面p[i],temp[i](指針)也要挪動}else{p[i] = NULL; //沒有元素的桶,不申請空間,指針初始化為NULLtemp[i] = NULL;temp_1[i] = NULL;}}for(size_t i = 0; i != dsize; ++i){size_t bucketidx = (arr[i]-minval)/space; //遍歷數組,每個元素的桶號*p[bucketidx] = arr[i]; //把每個元素寫入對應的桶中++p[bucketidx]; //該桶指針后移一位}size_t idx = 0; //之前用了static,下次調用的時候idx不會被賦值 =0 操作//cout << "static idx " << idx << endl;for(size_t i = 0; i != div; ++i){if(numsofeachbucket[i] != 0) //桶非空{if(numsofeachbucket[i]>1) //桶元素個數2個或更多{quicksort(numsofeachbucket[i], temp[i]); //對動態數組進行快速排序(p[i]挪動過了,temp[i]指向數組首位)}for(size_t j = 0; j != numsofeachbucket[i]; ++j){arr[idx++] = *temp[i]; //對排序后的數組(1個元素不需排序),寫入原數組++temp[i];//cout << "static idx " << idx << endl;}}}for(size_t i = 0; i != div; ++i){if(numsofeachbucket[i] != 0) //對申請出來的空間,釋放掉{delete [] temp_1[i]; //上面每個桶的數組初始位置指針p[i],temp[i]都動過了,所以用此副本初始地址temp_1[i] = NULL; //被釋放的空間的相關的指針置為空temp[i] = NULL;p[i] = NULL;}}delete [] temp_1; //delete 與 new 配對出現,釋放數組,指針置NULLdelete [] temp; //內存檢測工具valgrind http://valgrind.org/delete [] p;temp_1 = NULL;temp = NULL;p = NULL;} }9.1.桶排序(改進)
/**9-1.桶排序,將數據按規則分組,對各小組再分別排序*(改進)*1.數組長度一定的,只申請一次內存,避免內存碎片化,提高效率*2.給定桶的個數,程序運行狀況在不同環境下可控*/ void bucketsort1(size_t dsize, int *arr) {if(dsize <= 1) //預防特殊情況下后面代碼失效{return;}int maxval = arr[0];int minval = arr[0];for(int i = 0; i != dsize; ++i) //遍歷數組,找出最大最小元素{maxval = maxval > arr[i] ? maxval : arr[i];minval = minval < arr[i] ? minval : arr[i];}if(maxval == minval) //如果最大==最小,數組不需要排序{return;}else{int div = 1000; //桶的個數int space = (maxval-minval)/div+1; //每個桶的數值跨度,+1放大一點包住int *numsofeachbucket = new int [div](); //開辟數組,存放每個桶內的元素個數,()初始化為0int *endpositionofeachbucket = new int [div](); for(size_t i = 0; i != dsize; ++i){++numsofeachbucket[(arr[i]-minval)/space]; //把元素按大小分到不同的桶,并增加該桶元素個數++endpositionofeachbucket[(arr[i]-minval)/space];}for(int i = 1; i != div; ++i){endpositionofeachbucket[i] += endpositionofeachbucket[i-1]; //每個桶區間的最大下標+1的值}int *temparr = new int [dsize]; //開辟堆空間,存放臨時數組for(size_t i = 0; i != dsize; ++i){temparr[--endpositionofeachbucket[(arr[i]-minval)/space]] = arr[i]; //遍歷數組,把每個元素寫入對應的桶中,從每個桶的后部往前寫//--運行完成后endpositionofeachbucket[i]就是該桶的首位}for(size_t i = 0; i != div; ++i){if(numsofeachbucket[i] > 1) //桶元素2個或以上才排序{quicksort(numsofeachbucket[i], &temparr[endpositionofeachbucket[i]]); //對每個桶的數組進行快速排序(元素個數,每個桶數組首位的地址)} }for(size_t i = 0; i != dsize; ++i){arr[i] = temparr[i]; //對排序后的數組,寫入原數組}delete [] numsofeachbucket; //delete 與 new 配對出現,釋放數組,指針置NULLdelete [] endpositionofeachbucket; //內存檢測工具valgrind http://valgrind.org/delete [] temparr;numsofeachbucket = NULL;endpositionofeachbucket = NULL;temparr = NULL;} }比較優化前后的桶排序計算效率(同樣的環境下)
優化前 運行時間:149s
優化后 運行時間:96s (提升35%)堆的申請和釋放次數也降低了
10.基數排序
/**10.基數排序*/ void radix_countsort(size_t dsize, int *arr, int exp) {int numofeachbucket[10] = {0}; //十個數位,每個桶上有0個元素for(int i = 0; i != dsize; ++i){++numofeachbucket[(arr[i]/exp)%10]; //記錄該數位上相同的元素個數}for(int i = 1; i < 10; ++i){numofeachbucket[i] += numofeachbucket[i-1]; //每個位數區間的最大下標+1的值(現在存儲的是下標區間的上限+1)}int *output = new int [dsize];for(int i = dsize-1; i >= 0; --i){output[--numofeachbucket[(arr[i]/exp)%10]] = arr[i]; //把數組放在不同的區間位置上}for(int i = 0; i != dsize; ++i){arr[i] = output[i]; //一個數位排好后,寫回原數組}delete [] output;output = NULL; } void radixsort(size_t dsize, int *arr) {if(dsize <= 1){return;}int maxval = arr[0];for(size_t i = 0; i != dsize; ++i){maxval = arr[i] > maxval ? arr[i] : maxval; //找出最大的數}for(int exp = 1; maxval/exp > 0; exp *= 10) //從最低位開始對每個數位進行排序{radix_countsort(dsize, arr, exp);} }總結
以上是生活随笔為你收集整理的10种C++排序算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode 322. 零钱兑换(D
- 下一篇: s3c2440移植MQTT