Open Notes: C++模板式实现的10个常用排序算法
by Max Z. C. Li (maximusl@g.ucla.edu)
I’m not writing dedicated blog articles but trying to keep notes of my practices. This one is inspired by Top_Spirit’s 超详细十大经典排序算法总结(java代码)c或者cpp的也可以明白; many thanks.
Feel free to use any codes, but be warned: they work but are not well tested!!!
All corrections and criticisms are welcomed and you have my thanks in advance : )
I tried my best to use STL array whenever possible, but they are SUCH a bother.
There are some finer points mentioned in comments, and I’ll try to keep further notes of them when I have time.
And FYI, the bubble bursted, that’s why it’s not here.
#include <iostream>
#include <array>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <cmath>
using namespace std;
#define DEBUG 0
template <typename T>
void swap_pos(T &a, T &b){
/* xor method cannot handle alias
#if DEBUG
cout << a << " " << b << endl;
#endif
a ^= b;
#if DEBUG
cout << a << " " << b << endl;
#endif
b ^= a;
#if DEBUG
cout << a << " " << b << endl;
#endif
a ^= b;
#if DEBUG
cout << a << " " << b << endl;
#endif
*/
int temp = a;
a = b;
b = temp;
}
template <typename T>
void printArr(T const& arr){
for(auto it = arr.begin(); it != arr.end(); it++){
cout << *it << " ";
}
cout << endl;
}
template <typename T>
void selectSort(T& arr){
int nSize = arr.size();
//add one elem as min_holder
int min_pos = 0;
for(int i=0; i<nSize; i++){
min_pos = i;
for(int j=i+1; j<nSize; j++){
if(arr[min_pos] > arr[j]){
min_pos = j;
}
}
if(i^min_pos) swap_pos(arr[i],arr[min_pos]); //bug at i=3, min_pos=3 for some reason
#if DEBUG
printArr(arr); cout << "\n";
#endif
}
}
template <typename T>
void insertSort(T& arr){
int nSize = arr.size();
int next;
//ordered=>i, ordering=>j
for(int i=0; i<nSize-1; i++){//there is no bext to [nSize]
next = i+1;
for(int j=i; j>=0; j--){
#if DEBUG
if(arr[next]>arr[j]){//insert arr[next] after arr[j]
//either manual shift all elem, or use iterator insert(j+1, arr[next]), erase(next) ==> more costly than swap each time
}
#endif
//swap method
if(arr[next]<arr[j]){ //cannot only use swap_pos(arr[next], arr[j]), which give new val to arr[next] and stop further ordering
swap_pos(arr[next], arr[j]);
//swap_pos(next,j); //wrong!!! the bookmark j cannot be changed !!!!!
next = j;
#if DEBUG
printArr(arr); cout << "\n";
#endif
}
else break; //for O(n)
}
}
}
template <typename T>
void shellSort(T& arr){
int nSize = arr.size();
int gap = nSize / 2;
while(gap>0){ //not gap >1 or when gap = 1, the last round will be sikpped
for(int i=0; i<nSize-gap; i++){ //same insert sort, using gap instead 1
int next = i+gap;
for(int j=i; j>=0; j-=gap){
if(arr[next]<arr[j]){
swap_pos(arr[next],arr[j]);
next = j;
}
else break;
}
}
gap /= 2;
}
}
template <typename L, typename R, typename T>
T& merge(L& l_arr, R& r_arr, T& arr){
//merge sorted
int N = arr.size();
int l_len = l_arr.size();
int r_len = r_arr.size();
for(int i=0,l=0,r=0; i<N; i++){ //int i,l,r=0 does not initialize the i,l properly!!!!
#if DEBUG
cout << i << l << r << endl;;
printArr(arr);
#endif
if(l>=l_len) arr[i] = r_arr[r++];
else if(r>=r_len) arr[i] = l_arr[l++];
else if(l_arr[l] < r_arr[r]) arr[i] = l_arr[l++];
else arr[i] = r_arr[r++];
}
#if DEBUG
//printArr(arr);
#endif
return arr;
}
template <typename T>
vector<T>& mergeSort(vector<T>& arr){
int N = arr.size();
//base
if(N <= 1) return arr;
//recurse
const int l_len = N>>1;
const int r_len = N - l_len;
vector<T> l_arr(l_len,0);
for(int i =0; i<l_len; i++){
l_arr[i] = arr[i];
}
vector<T> r_arr(r_len, 0);
for(int i=l_len; i<N; i++){
r_arr[i-l_len] = arr[i];
}
return merge(mergeSort(l_arr), mergeSort(r_arr), arr);
}
template <typename T>
int partition(T& arr, int head, int tail){//tail is last elem pos
int nSize = tail - head + 1;
if(nSize == 0){perror("unchecked single element.\n"); exit(-1);}
srand(int(time(NULL)));
int pivot_pos = head + rand()%nSize;
//select the middle index to benefit from presorted array
//int pivot_pos = nSize>>1;
int small_end_pos = head - 1;
//move pivot to last to simplify scan
swap_pos(arr[pivot_pos],arr[tail]);
for(int i=head; i<=tail; i++){
if(arr[i] <= arr[tail]){//==arr[tail] is crucial for putting pivot in place at the end
small_end_pos++;
if(i > small_end_pos)
swap_pos(arr[i],arr[small_end_pos]);
}
}
return small_end_pos;
}
template <typename T>
T& quickSort(T& arr, int head, int tail){
if(tail - head < 1) return arr; //arr.size() is not changed!!!
int small_end_pos = partition(arr, head, tail);
quickSort(arr,head,small_end_pos-1);
quickSort(arr,small_end_pos+1,tail);
return arr;
}
template <typename T>
void makeHeap(T& arr, int parent, int nSize){
int child;
int array_index_offset = 1;
/* singular method ==> unnecessary burden for the heapSort function.
int child = 2*parent;//left child first
if(child <= nSize && arr[child - array_index_offset] > arr[parent - array_index_offset]){//left child exist and large than parent, swap
swap_pos(arr[child - array_index_offset], arr[parent - array_index_offset]);
}
//now parent is the larger of parent, l_child
int r_child = child + 1;
if(r_child <= nSize && arr[child] > arr[parent- array_index_offset]){
swap_pos(arr[child],arr[parent - array_index_offset]);
}
*/
//iteration method
int prev = parent;
while(true){
child = 2*parent;
if(child <= nSize && arr[child - array_index_offset] > arr[parent - array_index_offset]) parent = child;
child++;
if(child <= nSize && arr[child - array_index_offset] > arr[parent - array_index_offset]) parent = child;
if(prev == parent) break; //none to be swapped
swap_pos(arr[prev - array_index_offset], arr[parent - array_index_offset]);
prev = parent;
}
}
template <typename T>
T& heapSort(T& arr){
int nSize = arr.size();
//base case: return when only 1 elem
if(nSize <= 1) return arr;
//make heap
for(int i = nSize/2; i>=1; i--){//here i is the node no. of the heap, starting from 1 !!!!!!!
makeHeap(arr,i,nSize); //build the heap bottom up ==> O(n) in total
}
for(int i=1; i<=nSize; i++){
swap_pos(arr[0], arr[nSize - i]);
makeHeap(arr, 1, nSize - i); //adjust the heap top--->down ==> O(logN)
printArr(arr);
}
return arr;
}
template <typename T>
vector<T>& countSort(vector<T>& arr){//cannot sort float/double precisely
//find min, max
int nSize = arr.size();
#if DEBUG
cout << "in counting, arr size is: "<< nSize << endl;
#endif
if(nSize<2) return arr; //always check your base case before accessing element outside bound check;
T mini = arr[0], maxi = arr[0];
for(int i=1; i < nSize; i++){
if(arr[i] > maxi) maxi = arr[i];
else if(arr[i] < mini) mini = arr[i];
}
if(maxi == mini) return arr;
int bias = round(mini);
int record_len = ceil(maxi - mini) + 1; //constexpr int still can't be used in an array declaration
#if DEBUG
cout << "in counting, min, max, record lenth are: "<< mini << " " << maxi << " " << record_len << endl;
#endif
//build record
vector<T> record(record_len,0); //auto init to 0
for(int i=0; i<nSize; i++){
record[round(arr[i])-bias]++; //record is shfted to base at 0 !!!
}
#if DEBUG
cout << "in counting, the record is: " << endl;
printArr(record);
#endif
//print
int arr_index = 0;
for(int i=0; i<record_len; i++){
while(record[i]>0){
record[i]--;
if(arr_index < nSize){
arr[arr_index] = i+bias;
arr_index++;}
else {cout << "mismatched record\n"; exit(-1);}
}
}
return arr;
}
template <typename T>
int assignBucket(T& elem, vector<double> const& bucketBorder){
if(elem < bucketBorder[0]){cout << "invalid input into buckets"; exit(-1);}
for(int i=0; i<bucketBorder.size()-1; i++){
if(elem>=bucketBorder[i] && elem <=bucketBorder[i+1])
return i;
}
return -1;
}
template <typename T>
vector<T>& bucketSort(vector<T>& arr, int num_bucket){
int nSize = arr.size();
//process input, find variance, mean, stdd
T mini = arr[0], maxi = arr[0];
T sum = 0;
for(int i=0; i<nSize; i++){
if(mini > arr[i]) mini = arr[i];
else if(maxi < arr[i]) maxi = arr[i];
sum += arr[i];
}
double mean = sum / nSize;//assign bucket as if the data is continous for conve.
sum = 0;
for(int i=0; i<nSize; i++){
sum += pow(arr[i]-mean,2);
}
double stdd = pow(sum / nSize, 0.5);
#if DEBUG
cout << mean << " " << stdd << endl;
#endif
//now use the stat to div bucket
int num_1stdd = 0.68 * num_bucket;//num bucket under +-stdd
int num_l1 = (num_bucket - num_1stdd)/2;//num bucket to the lest of -1 stdd
int num_1r = num_bucket - num_1stdd - num_l1;//num to the right of 1 stdd
//div up what's under +-1 stdd and both sides
double bin_width = 2*stdd / num_1stdd;
double l_stdd = mean - stdd;
double r_stdd = mean + stdd;
if(mini > l_stdd || maxi < r_stdd){cout << "something is seriously wrong *_*"; exit(-1);}
double l_bin_width = (l_stdd - mini) / num_l1;
double r_bin_width = (maxi - r_stdd) / num_1r;
//now get limits for each bucket
vector<double> bucketBorder(num_bucket+1,0);
bucketBorder[0] = mini;
int bias = 1;
//bucketBorder[num_bucket] = maxi;
for(int i=bias; i<bias+num_l1; i++){bucketBorder[i] = bucketBorder[i-1] + l_bin_width;}
bias += num_l1;
for(int i=bias; i<bias+num_1stdd; i++){bucketBorder[i] = bucketBorder[i-1] + bin_width;}
bias += num_1stdd;
for(int i=bias; i<bias+num_1r; i++){bucketBorder[i] = bucketBorder[i-1] + r_bin_width;}
#if DEBUG
cout << (maxi == bucketBorder[num_bucket]) << endl;
printArr(bucketBorder);
#endif
//now fill the bucket and sort by mergeSort()
vector<vector<T>*> buckets;
for(int i=0; i<num_bucket; i++){
buckets.push_back(new vector<T>);
}
for(int i=0; i<nSize; i++){
buckets[assignBucket(arr[i], bucketBorder)]->push_back(arr[i]);
}
#if DEBUG
cout << "numbers per bucket: ";
for(int i=0; i<num_bucket; i++){
cout << buckets[i]->size() << " ";
}
cout << endl;
#endif
for(int i=0; i<num_bucket; i++){
countSort(*buckets[i]);
}
//print
for(int i=0; i<nSize;){
for(int j=0; j<num_bucket; j++){
for(int k=0; k < buckets[j]->size(); k++){
arr[i] = buckets[j]->at(k);
i++;
}
}
}
for(int i=0; i<num_bucket; i++){delete buckets[i];} //delete [] buckets is not legal since 'buckets' is not regarded as a pointer
//delete [] bucket[i] is not legel since 'buckets[i]' does not hold pointers
//delete [] type**; delete type* for each 'new' called
return arr;
}
template <typename T>
vector<T>& radixSort(vector<T>& arr){
int nSize = arr.size();
T maxi = arr[0];
for(int i=0; i<nSize; i++){
if(maxi < arr[i]) maxi = arr[i];
}
int max_num_digit = 0;
while(maxi>=1){
maxi /= 10;
max_num_digit++;
}
//now sort by digits
//vector<vector<T>*> digit_count_arr(10,new vector<T>); does not work properly, only gives 1 new vec
vector<vector<T>*> digit_count_arr(10);
for(int i=0; i<10; i++){digit_count_arr[i] = new vector<T>;}
int mod = 10, musk = 1, digit;
for(int i=1; i<=max_num_digit; i++){ //do counting sort for each digit
//get all digits
for(int j=0; j<nSize; j++){
digit = (arr[j]%mod)/musk;
digit_count_arr[digit]->push_back(arr[j]);
}
//sort-print result
for(int k=0; k<nSize;){
for(int m=0; m<10; m++){
int size = digit_count_arr[m]->size();
for(int n =0; n<size; n++){
if(k>=nSize){cerr << "mismatched array when count-sorting digits.\n"; exit(-1);}
arr[k] = digit_count_arr[m]->at(n);
k++;
}
}
}
mod *= 10; musk *= 10;
for(int i=0; i<10; i++){//clear out the buckets for next digit
//delete digit_count_arr[i];//in conflict with digit_count_arr[i]->clear and delete the pointer itself, clear return ptr
//digit_count_arr[i]->clear(); //A reallocation is not guaranteed to happen, and the vector capacity is not guaranteed to change due to calling this function.
//A typical alternative that forces a reallocation is to use swap:
vector<T>().swap(*digit_count_arr[i]);
}
}
return arr;
}
int main(){
array<int, 10> og_arr ={2,3,4,1,5,6,9,8,7,0};
//array<int, 10> arr(og_arr);
vector<int> arr = {22,43,14,51,45,76,49,38,57,0,80,91,2,9,4,7,3,9,1,33,67,89,63};
printArr(radixSort(arr));
}