交换排序---冒泡、快速、归并排序

一、交换排序

     利用交换元素的位置进行排序的方法称作交换排序。常用的交换排序的方法有冒泡排序和快速排 序。快速排序是一种分区交换排序方法。

二、冒泡排序

之前已经介绍过冒泡排序,点击打开链接

冒泡排序最好情况时间复杂度O(n),冒泡排序最坏情况下时间复杂度O(n^2). 冒泡排序空间复杂度O(1),冒泡排序是一种稳定的排序算法。

三、快速排序

快速排序,实际上是找一个基准值,将这个数组分成两个部分,左边部分的数据都比右边部分的数据要小,再按照此方法对子区间进行划分进行排序。 
算法思想是:
1、开始时设置两个变量left,right,给定一个基准值key。
2、left向后移,找到第一个比key值大的数,否则继续向后走,。
3、right向前移,找到第一个比key小的数,否则继续向前走。
4、判断是否满足条件left小于right,不满足则交换,否则重复步骤2和步骤3,直到left和right相遇,这样所有的数就有序。

递归:

void QuickSort(int *array, int left, int right)
{
	if (left < right)
	{
		size_t div = Pation1(array, left, right);
		//size_t div = Pation2(array, left, right);
		//size_t div = Pation3(array, left, right);

		QuickSort(array, left, div);
		QuickSort(array, div + 1, right);
	}
}

方法一:左右指针法


代码如下:

int Pation1(int *array, int left, int right)
{
	size_t begin = left;
	size_t 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]);
	}
	if (begin!=right)
		swap(array[begin], array[right-1]);

	return begin;
}

方法二:挖坑法

先将最左边或者最右边取为基准值,保留这个值,这个位置就是起始坑,然后从左边开始遍历,找到第一个比坑里面的值大的数就交换,相当于用大的数填坑,以前的位置就会形成新的坑。然后我们可在右边找比坑的值小的数入坑, 又会形成新的坑,这样不断遍历走子问题直到两个坑相遇.
第一种类似只是保留了key的值,换成坑,然后不断找新的值去填坑,直到相遇。

//挖坑法
int Pation2(int *array, int left, int right)
{
	size_t begin = left;
	size_t 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;
}

方法三:

前两种都是从两边遍历,第三种是从一边取两个指针。如下:

两个指针prev和cur,如下图:

代码如下:

int Pation3(int *array, int left, int right)
{
	int key = array[right - 1];
	//int key = GetMidDataIdx(array, left, right);
	int cur = 0;
	int pre = cur - 1;

	while (cur < right)
	{
		//不相邻
		if (array[cur] < key&&pre++ != cur)   //前者不成立后者不执行
			swap(array[pre], array[cur]);
		cur++;
	}
	if (++pre != right)
		swap(array[pre], array[right - 1]);
	return pre;
}

以上三种方法的基准值都是取最左或最右,若取的值是最小或者最大的值,效率就很低。我们可以用三数取中法。

它并不是选择待排数组的第一个数作为中轴,而是选用待排数组最左边、最右边和最中间的三个元素的中间值作为中轴。 

//三数取中
int GetMidDataIdx(int *array, int left, int right)
{
	int mid = left + ((right - left) >> 1);
	if (array[left] < array[right])
	{
		if (array[left]>array[mid])
			return left;
		else if (array[right] < array[mid])
			return right;
		else
			return mid;
	}
	else
	{
		if (array[right]>array[mid])
			return right;
		else if (array[left] < array[mid])
			return left;
		else
			return mid;
	}
}


非递归

void QuickSortNor(int *array, int size)
{
	int left = 0;
	int right = size;     //左闭右开
	stack<int> s;
	s.push(right);
	s.push(left);

	while (!s.empty())
	{
		left = s.top();
		s.pop();
		right = s.top();
		s.pop();

		if (left < right)
		{
			int div = Pation2(array, left, right);
			//保存右半部分区间
			s.push(right);
			s.push(div + 1);

			//保存左半部分区间
			s.push(div);
			s.push(left);
		}
	}
}


四、归并排序

基本思想:将待排序的元素序列分成两个长度相等的子序列,为每一个子序列排序,然后将他们合并成一个序列。合并两个子序列的过程称为两路归并。

第一步:划分,为每一个子序列排序

第二步:归并

void Merge(int *array, int left,int mid, int right, int *temp)
{
	int index1 = left;
	int index2 = mid;
	int index = left;

	while (index1 < mid&&index2 < right)
	{
		if (array[index1] < array[index2])
			temp[index++] = array[index1++];
		else
			temp[index++] = array[index2++];
	}
	//如果有剩余元素
	while (index1 < mid)
		temp[index++] = array[index1++];
	while (index2 < right)
		temp[index++] = array[index2++];

	memcpy(array + left, temp + left, (right - left)*sizeof(int));
}
void _MergeSort(int *array, int left,int right,int *temp)
{
	if (left + 1 < right)
	{
		int mid = left + ((right - left) >> 1);
		_MergeSort(array, left, mid,temp);
		_MergeSort(array, mid, right, temp);
		Merge(array, left, mid, right,temp);
	}
}
void MergeSort(int *array, int size)
{
	int *temp = new int[size];
	_MergeSort(array, 0, size, temp);
	delete[] temp;
}


非递归:

//非递归
void MergeSortNor(int *array, int size)
{
	int gap = 1;
	int *temp = new int[size];

	while (gap < size)
	{
		for (int i = 0; i < size; i += 2 * gap)
		{
			int left = i;
			int mid = i + gap;
			int right = mid + gap;

			if (mid>size)
				mid = size;
			if (right>size)
				right = size;

			Merge(array, left, mid, right, temp);
		}

		gap *= 2;
	}

	delete[] temp;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值