Open Notes: c++ template implementation of 10 common sorting algorithms; c++ 模板写的10个常用排序算法

51 篇文章 2 订阅
43 篇文章 0 订阅

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));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值