交换排序:冒泡排序和快速排序

交换排序分为冒泡排序和快速排序。

冒泡排序:

    它是一种比较简单的排序方法,相邻两个元素两两比较,如果后一个比前一个大,就交换,每趟排序都可以将大的元素“浮”到顶端。外层循环循环n-1次,相当于排好了最大的n-1个元素的位置,则最后一个元素的位置就确定了。内层循环循环n-i次,两两交换,第一次排出最大元素在a[n-1]处;第二次循环n-1次,最大元素在a[n-2]处。

时间复杂度:

最好的情况:有序的,需要进行n-1次比较,O(n)

最坏的情况:逆序的,第一趟进行n-1次比较,第二趟进行n-2次比较,......进行1次比较。总共需要n*(n-1)/2次比较,则时间复杂度为O(n^2)

算法稳定性:

冒泡排序是将小的元素调到前面,大的元素调到后面。相邻两元素比较,交换,相等就不交换,因此算法的是稳定的。

 

void BubbleSort(int a[],size_t n)
{
	int i,j,temp;
	for(i = 0;i < n-1;++i) //进行n-1次外层循环
	{
		for(j = 0;j < n-1-i;++j)
		{
			if(a[j] > a[j+1])
			{
				temp = a[j];
				a[j] = a[j+1];
				a[j+1] = temp;
			}
		}
	}	
}

void TestBubbleSort()
{
	int a[] = {12,23,6,9,18,22,35,120,89,67};
	BubbleSort(a,sizeof(a)/sizeof(a[0]));
	PrintArray(a,sizeof(a)/sizeof(a[0]));
}

运行结果:

 

快速排序:

      设要排序的数组为a[0],a[1],a[2],a[3],......,a[n-1],任取一个数据作为关键字(通常是数组的第一个元素),一趟快速排序下来,比它大的数放在它的前面,比它小的数放在它的后面,对左右区间重复快排,直到各区间只有一个数。

时间复杂度:

最好的情况:若数组有n个元素,算出支点key的位置,再递归调用左半部分和右半部分,第一层递归 n/2 ,n/2 ,第二层递归 4/n ,4/n ,4/n ,4/n ,则n个元素递归共需x层 :2^x = n , x=logn , 每层都是n的复杂度,则时间复杂度为O(nlogn)。

最坏的情况:有序数组,则比较次数为n-1+n-2+n-3+……+1=n(n-1)/2=O(n^2)

快速排序是不稳定的一种算法,两个相同的值在排序过程中的先后顺序有可能改变。

代码实现:

快速排序(左右指针法)

begin为左指针,end为右指针

//[begin,end]
int Partition(int* a,int begin,int end)
{
	int& key = a[end]; //key是a[end]的别名,使用引用更高效
	while(begin < end)
	{
		while(begin < end && a[begin] <= key)
			++begin; //begin找大

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

		if(begin < end)
			swap(a[begin],a[end]);
	}
        //当出了循环,begin=end时,此时a[begin]一定大于key,a[begin]和key交换,key就在“中间”的位置
        swap(a[begin],key);
	return begin;
}

void QuickSort(int* a,int left,int right)
{
    if(left >= right)
        return;
    int div = Partition(a,left,right); //div是选出来的关键数字,其左边的数比它小,右边的数字比他大
    QuickSort(a,left,div-1); 对div左右两边的区间再进行快排
    QuickSort(a,div+1,right);
}

void TestQuickSort()
{
    int a[] = {12,17,18,3,9,7,4,29,53,6};
    //int a[] = {7,4,21,8,16,19,20,9,10};
    QuickSort(a,0,sizeof(a)/sizeof(a[0])-1);
    PrintArray(a,sizeof(a)/sizeof(a[0]));
}

运行结果:

改进:快速排序(左右指针法)

对于支点key的选取----采用三数取中法,使key两边的数更加接近五五分。

int GetMidIndex(int* a,int left,int right)
{
    //三数取中法
    int mid = left + (right-left)/2;
    if(a[left] < a[mid])
    {
        if(a[left] > a[right])
            return left;
        else if(a[left] < a[right])
            return right;
        else
            return mid;
    }
    else
    {
        if(a[mid] > a[right])
            return mid;
        else if(a[mid] < a[right])
            return right;
        else
            return left;
    }
}

//左右指针法[left,right]
int Partition(int* a,int begin,int end)
{
	int mid = GetMidIndex(a,begin,end);
	swap(a[mid],a[end]);

	int& key = a[end];
	while(begin < end)
	{
		//begin找大
		while(begin < end && a[begin] <= key)
			++begin;

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

		if(begin < end)
			swap(a[begin],a[end]);
	}
	swap(a[begin],key);
	return begin;
}

快速排序(挖坑法)

begin找比key大的数,把a[begin]赋给a[end],begin处为新坑,end找比key小的数,a[end]赋给a[begin],end处为新坑。

//挖坑法[left,right]
int Partition(int* a,int begin,int end)
{
	int& key = a[end];
	while(begin < end)
	{
		while(begin < end && a[begin] >= key)
			++begin;

		if(begin < end)
			a[end] = a[begin];

		while(begin < end && a[end] <= key)
			--end;
		
		if(begin < end)
			a[begin] = a[end];
	}
	a[begin] = key;
	return begin;
}

快速排序(前后指针法)

int Partition(int* a,int left,int right)
{
	int prev = left-1;
	int cur = left;
	while(cur < right)
	{
		if(a[cur] < a[right] && ++prev !=cur)
		{
			swap(a[cur],a[prev]);
		}
		++cur;
	}
	swap(a[right],a[++prev]);
	return prev;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值