排序算法(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);
			}
		}
	}
}

    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章