排序算法——交换排序(快排*)和归并排序

上篇文章介绍了插入排序和选择排序,详见https://mp.csdn.net/postedit/97524495

3交换排序

     所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记

录向序列的尾部移动,键值较小的记录向序列的前部移动。交换排序分为冒泡排序和快速排序。

3.1冒泡排序

3.1.1基本方法

①比较相邻的元素。如果第一个比第二个大,就交换他们两个。 

②对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 

③针对所有的元素重复以上的步骤,除了最后一个。 

④持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 

3.1.2图解算法

3.1.3算法实现

void BubbleSort(int* array, int size)
{
	int flag = 1;
	for (int i = 0; i < size&&flag==1; i++)
	{
		int flag = 0;
		for (int j = 1; j < size - i; j++)
		{
			if (array[j - 1] > array[j])
			{
				flag = 1;
				Swap(&array[j - 1], &array[j]);//两两比较有逆序就交换 
			}
		}
	}
}

3.1.4特性总结

1. 冒泡排序是一种容易理解的排序

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定

3.2快速排序

3.2.1基本思想

      任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准

值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。快排之所以

快,是因为利用了二分的思想。

       快排有三种方法①左右指针法②挖坑法③前后指针法

3.2.2左右指针法

int Partion1(int* array, int left, int right)
{
	assert(array);
	int begin = left;
	int end = right-1;
	int key = array[end];//选基准值为最右元素
	while (begin < end)
	{
		while (begin < end && array[begin] <= key)//前面找大
			begin++;
		while (begin < end && array[end] >= key)//后面找小
			end--;
		if (begin < end)
			Swap(&array[begin], &array[end]);//交换
	}
	Swap(&array[right - 1], &array[begin]);//交换基准值和分界点
	return begin;
}

3.2.3挖坑法

int Partion2(int* array, int left, int right)
{
	assert(array);
	int begin = left;
	int end = right - 1;
	int key = array[end];//选取最右元素为坑
	while (begin < end)
	{
		while (begin < end && array[begin] <= key)//左边找大入坑
			begin++;
		if (begin < end)
			array[end] = array[begin];
		while (begin < end && array[end] >= key)//右边找小入坑
			end--;
		if (begin < end)
			array[begin] = array[end];
	}
	array[begin] = key;
	return begin;
}

3.2.4前后指针法

关于该方法很巧妙,这篇文章有详细的分析:https://blog.csdn.net/Hanani_Jia/article/details/80571665

int Partion3(int* array, int left, int right)
{
	int cur = left;
	int pre = cur-1;
	int end = right - 1;
	int key = array[end];
	while (cur<end)
	{
        //定义两个指针
        //如果前面的array[cur]小于key,prev走一步后交换array[pre]和array[cur],cur走
        //遇到array[cur]大于key时,pre停下来,cur继续走
        //当array[cur]等于key时,一趟结束,交换key和array[cur],平分数组,依次递归。

		while (array[cur] > key)
			cur++;
		if (array[cur] <= key)
		{
			pre++;
			Swap(&array[pre], &array[cur]);
			cur++;
		}
	}
	Swap(&array[pre], &array[end]);
	return pre;
}

3.2.5快速排序的优化

①三数取中法 
    当我们每次选取key时,如果key恰好是最大或者最小值,效率会很低,为了避免这种情况,对快排选取key值进行优化。 

    思路:依旧选取最右边的值作为key,但是在选取前,我们把数组中最左边,中间,最右边位置的三个数取出来。找到这三个

数中值排在中间的一个。把该值与最右边位置的值进行交换。此时key的值不可能是最大值或者最小值。 

int GetMidIndex(int* array, int left, int right)
{
	int mid = left + ((right - left)>>1);
	if(array[left] < array[right])
	{
		if(array[mid] < array[left])
			return left;
		else if(array[mid] > array[right])
			return right;
		else
			return mid;
	}
	else
	{
		if(array[mid] > array[left])
			return left;
		else if(array[mid] < array[right])
			return right;
		else
			return mid;
	}
}


②随机值法。 
      num = rand()%N 把num位置的数与最右边的值交换,key依旧去最右边的值。这种方法也可以,但是太随机了,特殊场景

会导致不可控的结果。 

③小区间优化 。

      快排是利用递归栈帧完成的,如果递归深度太深会影响效率。切割区间时,当区间内元素数量比较少时就不用切割区间了,

这时候就直接对这个区间采用直接插入法,可以进一步提高算法效率。 

// [left, right)
void QuickSort(int* array, int left, int right)
{
	if(right - left < 16)
		InsertSort(array+left, right - left);
	else
	{
		int div = Partion2(array, left, right);
		QuickSort(array, left, div);
		QuickSort(array, div+1, right);
	}
}

3.2.6快速排序特性总结

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4. 稳定性:不稳定

4归并排序

4.1归并排序

4.1.1基本思想

       归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,

得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

4.1.2图解

4.1.3算法实现

void MergeData(int* array, int left, int mid, int right, int*temp)
{
	int beginL = left, endL = mid;
	int beginR = mid, endR = right;
	int index = left;

	while(beginL < endL && beginR < endR)
	{
		if(array[beginL] <= array[beginR])
			temp[index++] = array[beginL++];
		else
			temp[index++] = array[beginR++];
	}

	while(beginL < endL)
	{
		temp[index++] = array[beginL++];
	}

	while(beginR < endR)
	{
		temp[index++] = array[beginR++];
	}
}

void _MergeSort(int* array, int left, int right, int* temp)
{
	if(right - left > 1)
	{
		int mid = left + ((right - left)>>1);
		_MergeSort(array, left, mid, temp);
		_MergeSort(array, mid, right, temp);
		MergeData(array, left, mid, right, temp);
		memcpy(array+left, temp+left, sizeof(array[0])*(right - left));
	}
}

void MergeSort(int* array, int size)
{
	int* temp = (int*)malloc(size * sizeof(array[0]));//需要开额外空间
	if(NULL == temp)
	{
		assert(0);
		return;
	}

	_MergeSort(array, 0, size, temp);
	free(temp);
}

4.1.4归并排序特性总结

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(N)

4. 稳定性:稳定

5排序算法性能比较

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值