排序算法(3) ---交换排序

  应用交换排序基本思想的主要排序方法有:冒泡排序和快速排序。这里将主要介绍快速排序,以及快速排序的递归与非递归的实现和对快速排序的优化

冒泡排序

 

     冒泡排序是将大数或小数不断后移的一种思想,比较和交换都发生在两个相邻元素之间。

    思想:

        1.单趟排序:比较相邻的元素,如果第一个比第二个大(小),交换这两个元素,直到最后元素,则最后元素的值应该为最大或最小值,前面的元素可能有序也可能无序。

        2.对前面元素在进行单趟排序,改变每趟排序的下标,进行单趟排序,直到没有一对数据进行比较时,则排序完成。

    优化:

       对于冒泡排序,经过单趟排序后,前面的元素可能有序,若前面元素已经有序,则可终止排序,提高效率。(设置标志flag=false,如果在每次单趟排序有数据交换,则将flag=true,, 每次单趟排序完可对flag进行判断,若flag==false,说明前面元素已经有序,直接跳出循环,否则,继续进行冒泡。)

    图解:

   实现:

void BubbleSort(int* a, size_t n)
{
	assert(n);
	bool finish = false; //堆排序进行优化
	size_t end = n;
	while(end > 0) //end表示每次冒泡的终止位置
	{
		//单趟冒泡
		for(size_t i=1; i<end; i++)
		{
			if(a[i-1]>a[i])
			{
				swap(a[i-1],a[i]);
				finish = true;  //若单趟排序有数据交换,则将finish置为true
			}
		}

		if(finish == false)  //判断单趟排序中是否有交换
		{
			return;
		}

		--end;
	}
}

 

    算法分析:

         算法时间复杂为O(n^2),属于稳定性算法

快速排序

 

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

   图解: 

                              

 

 

        分析:快速排序应用分治法的思想,将大问题分解为小问题,先求解子问题,以得到对原问题的求解。

        递归实现:            

//左右指针法	找一个key,将大数右移,小数左移,最后将数组区间划分为两部分 [] key []
int PartSort1(int* a, int begin, int end)
{
	int left = begin, right = end; //right赋值为 end 还是 end-1
	int key = a[right]; //选择最右边为key值
	//单趟排序
	while(begin < end)
	{
		//begin找大于
		while(begin < end && a[begin] <= key) //注意条件a[begin] <= key 
		{
			++begin;
		}

		//end找小
		while(begin < end && a[end] >= key)
		{
			--end;
		}

		swap(a[begin], a[end]);
	}
	swap(a[begin],a[right]); //a[right]相当于中间划分的位置

	return begin; 
}

void QuickSort(int* a, int left, int right)
{
	assert(a);

	if(left >= right) //注意:结束条件的判断
	{
		return;
	}

	int div = PartSort1(a, left, right);

	QuickSort(a, left,div-1);
	QuickSort(a, div+1, right); //right为什么不-1;
}

算法分析

       

 最坏情况:

        如果我们在选取基准p的时候,每次选取的都是当前数组中最小的一个元素,那么每次划分都只是让数组中的元素少1(被筛选出来的那个元素当然有序),这样一来就需要反复遍历数组导致复杂度变成了O(n2)。

   最好情况:

     如果我们在选取基准p的时候,每次选取的都是当前数组中最中间的一个元素(是中位数,而不是元素位置上的中间),那么每次划分都把当前数组划分成了长度相等的两个子数组,这样一来复杂度变成了O(nlog2n)。

基准的选择

1.对于基准元素的选取,也可以采用随机数选取的方式

2.如果要按照元素在位置上的中间来选取基准元素,还可以将中间位置上的元素与第一个元素进行对换

优化:

三平均分区法

这一改进与其它的快速排序方法不同,它并不是选择待排数组的第一个元素作为中轴,而是选用待排数组最左边、最右边和最中间的三个元素的中间值作为中轴。这一改进的优势:

(1)首先,它使得最坏情况发生的几率减小了。

(2)其次,未改进的快速排序算法为了防止比较时数组越界,在最后要设置一个哨点。

 

非递归实现快排:

借助栈,栈中保存左右下标,先压右、在压左,栈为空时循环结束

注:压栈应注意左右下标有意义时才压栈及(left<right).

void QuickSortFD(int* a, int left, int right)
{
	stack<int> s; //栈中存放下标
	if(left < right) //首先要保证下标有意义再压栈
	{
		s.push(right);
		s.push(left);
	}

	while(s.size()>0)
	{
		
		int left = s.top();
		s.pop();
		int right = s.top();
		s.pop();
		if(right - left <= 20)
		{
			InsertSort(a+left,right-left+1);
			return;
		}
		else
		{
			int div = PartSort1(a,left,right);

			if(div - 1 >left) //区间不合法时,不入栈
			{
				s.push(div-1);
				s.push(left);
			}

			if(right > div+1)
			{
				s.push(right);
				s.push(left);
			}
		}
	}
}

 

    

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值