交换排序(冒泡,快速排序)

柔情痞子 提交于 2019-12-16 03:23:26

交换排序

1.交换排序的思想

交换排序的思想为两两比较关键字的大小,若关键字次序相反,则这两个元素就进行交换,直到数组中没有出现反序的元素为止。

在这里主要介绍两种算法:

  • 冒泡排序
  • 快速排序

2.冒泡排序

冒泡排序的思想为在无序区中相邻两个元素比较,不断调整元素的位置,使关键字最小的元素像气泡一样冒出水面。

例:每经历一轮有效排序,都有一个元素加入有序区(冒泡到数组的一端)

数组下标 原始 第一轮 第二轮 第三轮 第四轮
0 4 0 0 0 0
1 2 4 1 1 1
2 3 2 4 2 2
3 1 3 2 4 3
4 0 1 3 3 4

冒泡排序是相邻元素的比较,是一个稳定的排序算法。

代码:

/*冒泡排序 o(n^2) 稳定*/
void BubbleSort(Elem elem[], int len)
{
	bool flag;
	for (int i = 0; i < len - 1; i++)
	{
		flag = false;
		for (int j = len - 1 ; j > i; j--)
		{
			if (elem[j].key < elem[j-1].key)
			{
				swap(elem[j - 1], elem[j]);
				flag = true;
			}
		}
		if (!flag)
			break;
	}
}

3.快速排序法

快速排序法是冒泡算法的改进,也属于交换排序。

区别:

  • 冒泡排序每经历一次排序,就有一个元素冒泡到数组的一端
  • 快速排序是每一次排序都按照一个基准值将数组分为两个部分,其中一部分的值都比基准值大,另外一部分都比基准值小

在这里插入图片描述

快速排序法的思想是分治(分而治之),按照以上的方法,每次都将数组划分为两部分的方法进行排序。


如何实现以上说的按照一个基准值划分成两部分?

有两种方法:双边循环法单边循环法

双边循环法

①假设以数组的第一个元素为基准值,使用两个指针left和right分别指向数组的左、右端:

5 3 2 6 2 7
pivot,start,left right,end

②然后right指针从右向左找到第一个比基准值小的元素,找到后停下来:

5 3 2 6 2 7
pivot,start,left right end

③left指针从左向右找到第一个比基准值大的元素:

5 3 2 6 2 7
pivot,start left right end

④交换left,right指针指向的值

5 3 2 2 6 7
pivot,start left right end

⑤重复步骤②、③、④直到左右指针相同

5 3 2 2 6 7
pivot,start left,right end

⑥交换基准值与left指向的值

2 3 2 5 6 7
pivot,start left,right end

可以看到此时基准值5的左边都比5小,右边都比5大

单边循环法这里暂时不介绍

参考代码:

/*以elem[start]为基准值,将数组划分为左右两部分,左边都比基准值小,右边都比基准值大,并返回基准值的位置*/
int partition(Elem elem[],int start,int end)
{
	int left = start;  //左指针,从左向右
	int right = end;   //右指针,从右向左

	Elem pivot = elem[start];

	while (left != right)
	{
		//指针从右向左扫描,找到第一个比基准值小的
		while (left < right && elem[right].key >= pivot.key)
		{
			right--;
		}
		//指针从左向右扫描,找到第一个比基准值大的
		while (left < right && elem[left].key <= pivot.key)
		{
			left++;
		}
		//交换左右指针
		if (left < right)
		{
			swap(elem[left], elem[right]);
		}
	}
    
	//基准值与循环截止位置的值交换
	elem[start] = elem[left];
	elem[left] = pivot;
    
	return left;
}

/*快速排序   o(nlog2(n)) 不稳定*/
void QuickSort(Elem elem[], int start, int end)
{
	if (start < end)
	{
		int pivot = partition(elem, start, end);   //确定基准值pivot
		QuickSort(elem, start, pivot - 1);         //从start,pivot-1再次进行快速排序
		QuickSort(elem, pivot + 1, end);           //从pivot+1,end再次进行快速排序
	}
}

优化交换次数版本:

/*以elem[start]为基准值,将数组划分为左右两部分,左边都比基准值小,右边都比基准值大,并返回基准值的位置*/
int partition(Elem elem[],int start,int end)
{
	int left = start;  //左指针,从左向右
	int right = end;   //右指针,从右向左

	Elem pivot = elem[start];

	while (left != right)
	{
		//指针从右向左扫描,找到第一个比基准值小的
		while (left < right && elem[right].key >= pivot.key)
		{
			right--;
		}
		elem[left] = elem[right];
		//指针从左向右扫描,找到第一个比基准值大的
		while (left < right && elem[left].key <= pivot.key)
		{
			left++;
		}
		elem[right] = elem[left];
	}

	//基准值与循环截止位置的值交换
	elem[left] = pivot;

	return left;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!