交换排序(冒泡排序/快速排序)及其优化

交换排序基本思想

两两比较待排记录的关键字,一旦发现两个记录的次序与排序的要求相逆,则交换这两个记录的位置,直到表中没有逆序的记录存在为止。

分类

  • 冒泡排序
  • 快速排序(对冒泡的改进)

<1>冒泡排序

基本思想:序列中相邻的两个元素进行比较,如果前一个元素大于后一个元素,则交换二者位置,接着继续向后循环比较直到最后一个元素,这样一趟下来就可以将最大的那个元素放到最后的位置。完成之后,进行第二趟排序,循环比较直到倒数第二个元素,就可以将第二大元素放于倒数第二个位置......循环以上步骤,直到循环比较到原数组的最后两个元素,排序即可完成。

代码体现:

void bubble_sort(int arr[],int len)
{
	for(int i=0;i<len-1;i++)   //排序的趟数,n个数就需要排序n-1趟
	{
		bool flag=true;   //一个小小的优化,在某一趟如果没有改变就不再进行最外层循环了
		for(int j=0;j<len-i-1;j++)  //每一趟中的交换排序
		{
			if(arr[j]>arr[j+1])
			{
				swap(arr[j],arr[j+1]);
				flag=false;    //进入这个判断就证明排序没有完成
			}
		}
		if(flag)
			break;

	}
}

复杂度分析:

1:空间复杂度:可以看出并没有什么大的额外空间,所以为O(1);

2:时间复杂度:

  • 最好情况下,待排序列已经是有序的,这样冒泡排序在第一趟排序过程就没有交换发生,所以一趟之后即排序结束。也即,只在第一趟中进行了n-1次比较。最坏情况是待排序列记录按逆序排序,所以需要进行n-1趟排序,且第i趟需要进行n-i次比较。所以总的比较次数为1/2*n*(n-1);所以冒泡排序的时间复杂度为O(n²)。

 3:算法稳定性:

冒泡排序并未改变相同元素的先后次序,所以是稳定的排序算法

<2>快速排序

基本思想:快速排序就是冒泡排序的一种改进,冒泡排序是通过每一趟冒泡将最大值(最小值)放到恰当位置,而快速排序則是每趟排序从待排序区间选一个基准值(也称作枢纽值),将比它小的数据全放在其左边,将比它大的值放在其右边然后递归其左右子区间对其排序,一层层递归下去,某区直到间只剩一个数据时,停止递归,此子区间已经算是有序,继而向其上层区间返回,一层层向上返回,当首次枢纽值的左右区间均已有序时,整个排序就算完成。

递归代码:

#include<iostream>
using namespace std;

int Partition(int *arr,int low,int high) //划分算法,找到元素正确位置
{

	int tmp=arr[low];  //从第一个元素作为基准进行划分
	while(low<high)   //从序列两端交替向中间扫描
	{
		while(arr[high]>=tmp&&low<high) //从右向左扫描查找第一个小于基准的
			high--;
		if(low<high)    //找到小于基准的数,将其交换到表的左边
		{
			arr[low]=arr[high];
			low++;     //因为本位low已存从右边拿来的数据,所以low往后移
		}

		while(arr[low]<=tmp&&low<high) //从左往右找第一个大于基准的元素
			low++;
		if(high>low) //找到大于基准的数,将其交换到表的右边
		{
			arr[high]=arr[low];
			high--;
		}
	}
	arr[low]=tmp;  //最终的low/high位置即是基准在序列中的最终位置,放入即可
	return low;  //返回基准的最终位置下标
}
void QuickSort(int *arr,int low,int high)  //递归形式的快排
{
	int i;
	if(low<high)
	{
		i=Partition(arr,low,high); //以i为基准将序列分为两部分
		if(i-low>1)   //避免一次递归调用,左边没有元素或者只有一个就不用排序
			QuickSort(arr,low,i-1);  //左半边进行排序
		if(high-i>1)
			QuickSort(arr,i+1,high);  //右半边进行排序
	}
}
void Show(int *arr,int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	int arr[]={61,33,48,82,72,11,25,48};
	int len=sizeof(arr)/sizeof(arr[0]);
	QuickSort(arr,0,len-1);
	Show(arr,len);

}

非递归代码(我们都知道栈与递归很相似,那么我们就可以用栈来替换递归)


#include<iostream>
#include<stack>
#include<assert.h>
using namespace std;

void QuickSort(int *arr,int low,int high)
{
	assert(arr);
	if(low>=high)
		throw "error";
	int i=low;   //赋值给i只是为了用于操作,不想打字low(懒)。。。
	int j=high;
	stack<int> s;
	s.push(i);   //将序列最左边下标入栈
	s.push(j);  //将最右边下标入栈
	while(!s.empty())  //当栈不空证明还有序列需要处理
	{
		j=s.top();   //因为先入左边,所以先出右边
		high=j;      //将当前的左右端记录,下边要进行判断,重要啊,否则就死了
		s.pop();
		i=s.top();
		low=i;
		s.pop();
		int tmp=arr[i];
		while(i<j)
		{
			
			while(i<j&&arr[j]>=tmp)
				j--;
			if(i<j)
			{
				arr[i]=arr[j];
				i++;
			}
			while(i<j&&arr[i]<=tmp)
				i++;
			if(i<j)
			{
				arr[j]=arr[i];
				j--;
			}
		}
		arr[i]=tmp;   //此时的i和j都是分段的下标
		if(high-j>1)  //判断分割点右边是否还有数据需要入栈进行排序
		{
			s.push(j+1);  //入分割点右边序列的最左边的下标
			s.push(high); //入分割点右边序列的最右边的下标
		}
		if(i-low>1)
		{
			s.push(low);
			s.push(i-1);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Eunice_fan1207/article/details/82924372