1. 程式人生 > >交換排序(氣泡排序/快速排序)及其優化

交換排序(氣泡排序/快速排序)及其優化

交換排序基本思想

兩兩比較待排記錄的關鍵字,一旦發現兩個記錄的次序與排序的要求相逆,則交換這兩個記錄的位置,直到表中沒有逆序的記錄存在為止。

分類

  • 氣泡排序
  • 快速排序(對冒泡的改進)

<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;   
	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;
		if(high-j>1)
		{
			s.push(j+1);
			s.push(high);
		}
		if(i-low>1)
		{
			s.push(low);
			s.push(i-1);
		}
	}
}
void Show(int *arr,int len)
{
	for(int i=0;i<len;i++)
	{
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}
int main()
{
	int arr[]={47,47,55,1,47,47};
	int len=sizeof(arr)/sizeof(arr[0]);
	QuickSort(arr,0,len-1);
	Show(arr,len);
}