几种排序算法的简介

假设输入为一组随机整数如下形式,且最终结果均为升序排列。

vector<int> vec{1,9,22,10,8,33,54,32,7};

选择排序(insert sort)

原理:分为两步,令i = 0,首先,在vec[i,end)之间搜索得到最小值,然后将该值与vec[i]交换,++i并跳转到第一步,直到i == end,完成排序。

复杂度分析:平均时间复杂度为O(n^2),空间复杂度为常数。

代码:

void selection_sort(vector<int> &vec)
{
	for(auto i = vec.begin(); i != vec.end(); ++i)	
	{
		auto min_idex = i;
		for(auto j = i+1; j != vec.end(); ++j)
		{
			if(*min_idex > *j)
				min_idex = j;
		}
		if(min_idex != i){
			swap(*i, *min_idex);
		}

	}
}

插入排序(insert sort)

原理:i=0, vec[i]依次与其左侧的数作比较,如果小于则进行互换,直到该数大于左侧的数或它位于最左侧,++i并重复上一步,直到i > end。简而言之先对左侧子序列排序,而后子序列长度慢慢增加直到整个序列有序。

复杂度分析:平均时间复杂度为O(1/4 n^2),空间复杂度为常数,stability

代码:

void insert_sort(vector<int> &vec)
{
	for(auto i = vec.begin(); i != vec.end(); ++i)
	{
		for(auto j = i; j != vec.begin(); --j)
		{
			if(*(j-1) > *j)	
				swap(*(j-1), *j);

		}
	}
}

希尔排序(shell sort)

原理:希尔排序其实是一种改进的插入排序算法,如果待排序列局部有序的话会大大加快插入排序算法的速度,正是如此,通过改变插入排序的步长,多次调用插入排序就可以先局部排序而后整体排序。常见的步长递增公式为step = 3*n + 1。

复杂度分析:平均算法复杂度未知,空间复杂度为常数。

代码:

void shell_sort(vector<int> &vec)
{
	int N = vec.size(); 
	int h = 1;
	// Find the suitable h
	while(h < N/3)	h = 3 * h + 1 ;

	while(h >= 1)
	{
		// Avoid compare the first element
		for(auto i = vec.begin() + h; i != vec.end() ; ++i)	
		{	// The descend sequence to compare
			for(auto j = i ; (j - vec.begin()) >= h ; j = j - h)
				if(*(j-h) > *j)
					swap(*(j-h), *j);
		}
		h = h/3;	
	}
}

归并排序(merge sort)

原理:首先将待排序列分为两个子序列,递归的对两个子序列进行归并排序,最后合并两个子序列。

复杂度分析:平均时间复杂度 O(N log N), 空间复杂度 O(N) 需要分配额外的空间,stablity.

代码:

bool is_sort(vector<int> &vec, const int &lo, const int &hi)
{
	for(int i = lo; i <= hi -1; ++i)
		if(vec[i] > vec[i+1]){
			return false;
		}
	return true;
}
void merge(vector<int> &vec, vector<int> &temp_vec, const int &lo, const int &mid, const int &hi)
{
	if(!is_sort(vec, lo, mid) || !is_sort(vec, mid+1, hi)){
		cerr << "the merge subsequence is't sorted." << endl;
		exit ;
	}
	// acceleration the sort
	if(vec[mid] < vec[mid+1])
		return;
	// copy	
	for(int i = lo; i <= hi; ++i)
		temp_vec[i] = vec[i];
	
	int i = lo, j = mid + 1;
	for(int k = lo; k <=hi; ++k)
	{
		if(i > mid) 
			vec[k] = temp_vec[j++];
		else if(j > hi)
			vec[k] = temp_vec[i++];
		else if(temp_vec[i] < temp_vec[j])
			vec[k] = temp_vec[i++];
		else
			vec[k] = temp_vec[j++];
	}
}
void sorting(vector<int> &vec, vector<int> &temp_vec, const int &lo, const int &hi)
{
	if(lo >= hi)
		return;
	sorting(vec, temp_vec, lo, (hi+lo)/2);
	sorting(vec, temp_vec, (hi+lo)/2 + 1, hi);
	merge(vec, temp_vec, lo, (hi+lo)/2, hi);
	show(vec);
}
void merge_sort(vector<int> &vec)
{
	vector<int> temp_vec(vec.begin(), vec.end());
	sorting(vec, temp_vec, 0, vec.size()-1);
}

自底而上(button to up)的非递归方法

void button_up_merge_sort(vector<int> &vec)
{
	vector<int> temp_vec(vec.begin(), vec.end());
	for(int sz = 1; sz < vec.size(); sz = sz+sz)
		for(int lo = 0; lo < vec.size() - sz; lo = lo + sz + sz)
		{
			int hi = (lo + sz + sz -1 < vec.size() - 1) ? (lo + sz + sz - 1) : (vec.size() - 1);
			merge(vec, temp_vec, lo, lo + sz -1, hi);	
		}
}

快速排序(quick sort)

原理及步骤:先对序列进行随机化(为了避免遇到较差的情况),接着采用递归的手段,依次进行划分,排序。划分时,首先选择一个值比如v,使得对于给定序列 v 左侧的值都小于v,右侧的值都大于v。

算法复杂度:平均时间复杂度O(1.39N log N), 空间复杂度O(1)(原地算法)unstability

代码:

void shuffle(vector<int> &vec, const int &lo, const int &hi)
{
	srand((unsigned)time(NULL));
	for(int i = lo + 1; i <= hi; ++i)
	{
		int j = rand() % i;
		if( i !=  j)	
			swap(*(vec.begin() + i), *(vec.begin() + j));		
	}
}
int partition(vector<int> &vec, const int &lo, const int &hi)
{
	int i = lo;
	int j = hi + 1;
	while(1)	
	{
		while(vec[lo] < vec[--j])
			if(j == lo)
				break;
		while(vec[lo] > vec[++i])
			if(i == hi)
				break;
		if(i >= j)
			break;
		swap(*(vec.begin() + i), *(vec.begin() + j));
	}
	swap(*(vec.begin() + lo), *(vec.begin() + j));
	return j;
}
void sorting_quick(vector<int> &vec, const int &lo, const int &hi)
{
	if(hi <= lo )
		return;
	int j = partition(vec, lo, hi);
	sorting_quick(vec, lo, j - 1);
	sorting_quick(vec, j + 1, hi);
}
void quick_sort(vector<int> &vec)
{
	shuffle(vec, 0, vec.size() - 1);
	sorting_quick(vec, 0, vec.size() - 1);
}
一种改进的利用快排查找第k个小的数的算法
int find_kth_lowest(vector<int> &vec, const int &k)
{
	shuffle(vec, 0, vec.size() - 1);
	int lo = 0;
	int hi = vec.size() - 1;
	while(hi >= lo)
	{
		int j = partition(vec, lo, hi);	
		if( j > k) 
			hi = j - 1;
		else if(j < k)
			lo = j + 1;
		else 
			return vec[k];
	}
}
Dijkstra 3-way quick sort,对于排序存在多个相同数值的改进快排算法。
void dijkstra_quick_sort(vector<int> &vec, const int &lo, const int &hi)
{
	if(hi <= lo)
		return;
	int lt = lo;
	int gt = hi;
	int i = lo;	
	int v = vec[lo];
	while( i <= gt)
	{
		if(vec[i] < v){	
			swap(*(vec.begin() + i), *(vec.begin() + lt));
			lt++;
			i++;
		}else if(vec[i] > v){
			swap(*(vec.begin() + i), *(vec.begin() + gt));
			gt--;
		}else{
			++i;
		}
	}
	dijkstra_quick_sort(vec, lo, lt-1);	
	dijkstra_quick_sort(vec, gt + 1, hi);
}


堆排序(heap sort)

原理及步骤:二叉堆是一个特殊的二叉树,要求每个节点的值都大于其两个儿子节点的值,因此树的根节点就是当前堆最大值。堆排序就是先建堆,接着每次取根节点并删除,直到堆大小为1,结束。删除根节点的策略:首先将根节点与尾后节点的值互换,接着删除尾后节点,并对根节点处理(满足儿子节点小于父节点的约束)。

算法复杂度:平均时间复杂度为O(2N lg N), 空间复杂度O(1),原地算法,unstability.

代码:

void sink(vector<int> &vec, int k, const int &max_length)
{
	while(2 * k <= max_length)
	{
		int j = 2 * k;	
		if(j < max_length  && vec[j] < vec[j+1])
			j++;
		if(vec[k] < vec[j])
			swap(*(vec.begin() + k), *(vec.begin() + j));
		k = j;
	}
}

void heap_sort(vector<int> &vec)
{
	// Insert an unused number to make the index begin at 1.
	vec.insert(vec.begin(), -1);
	int N = vec.size() - 1;
	for(int i = N / 2; i >= 1; --i)
	{
		sink(vec, i, N);
	}
	
	while(N > 1)
	{
		swap(*(vec.begin() + 1), *(vec.begin() + N--));	
		sink(vec, 1, N);
	}
	// Erase the unused number.
	vec.erase(vec.begin());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值