1. 程式人生 > >常見排序總結

常見排序總結

/*
選擇排序
每一趟選取第i個關鍵字,與剩下的n-i關鍵字進行比較,找出最大的值與剛開始選擇的i下標對應的值交換
第一趟: 0 , 1~n-1
第二趟:1, 2~n-1
.....
應用場景
  最好最壞情況時間複雜度都是O(n^2) 空間複雜度為O(1)
  最壞情況:對相對有序的一組資料排序
  不穩定 由其交換引起的
  適用於數量不大並且交換次數少的情況下
*/

void Select_Sort(int *arr,int len)
{

	if(arr == NULL || len < 0)
		return ;
	int i,j;
	int minIndex;
	for(i = 0;i < len -1 ;++i)
	{
		minIndex = i;
		for( j = i+1 ;j < len - 1;++j )
		{
			if(arr[j] < arr[minIndex])
			{
				minIndex = j;
			}
		}

		if(i != minIndex)
		{
			int tmp = arr[minIndex];
			arr[minIndex] = arr[i];
			arr[i] = tmp;
		}
	}
}

/*
氣泡排序
每趟兩個相鄰的資料比較,如果前者大於後者就交換位置;每趟最後一個元素都是本趟元素裡最大的那個
所以在每趟的比較中,上一趟的最後一個元素不再參與排序

本程式碼是升序排列 
  給出的數字序列也是相對於升序 那麼交換次數少 最好情況下時間複雜度O(n)
  給出的數字序列是完全降序  那麼交換次數duo 最壞情況下時間複雜度O(n^2)
*/

void Bulle_Sort(int *arr,int len)
{
	if(arr == NULL || len < 0)
		return ;

	int i ,j;
	for( i = 0 ; i < len-1 ;++i)
	{
		for(j = 0;j < len -i-1 ;++j)
		{
			if(arr[j] > arr[j+1])
			{
				int tmp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = tmp;
			}
		}
	}
}

/*
直接插入排序
每趟選定一個標兵位,標兵位開始往前遍歷 找出第一個比標兵位小的位置
那麼這個位置的後面就是該標兵存放的位置

應用場景
給出的數字序列基本有序 最好情況下 時間複雜度O(n)
給出的數字基本無序 最壞情況下 時間複雜度O(n^2)
空間複雜度為O(1)
如果有少量或者大量資料都可以採用
*/

void Insert_Sort(int *arr,int len)
{
	if(arr == NULL || len < 0)
		return ;
	int tmp = 0;
	int j;
	for(int i = 1;i < len - 1; i++)
	{
		tmp = arr[i];
		for(j = i-1;j >= 0;j--)
		{
			if(arr[j] > tmp)
			{
				arr[j+1] = arr[j];
			}
			else
			{
				break;
			}
		}
		arr[j+1] = tmp;
	}
}

/*
歸併排序 採用二路歸併法
開闢另一個與舊陣列空間大小的陣列
兩個有序的歸併段 依次從頭開始比較 小的先放入到新開闢的陣列中 在繼續往後比較歸併 直至歸併為一個歸併段
每一次歸併歸併段的增量為上一次的二倍
時間複雜度 一次歸併O(logn),整體歸併時間複雜度O(nlogn)
空間複雜度 O(n)
*/

void Merge(int *arr,int len ,int gap)
{
	int low1 = 0;
	int high1 = low1+gap-1;

	int low2 = high1+1;
	int high2 = low2+gap-1 < len ? low2+gap-1:len-1;

	int *brr = new int[len];
	if(brr == NULL) return ;
	int i = 0;
	while(low2 < len)//保證第二個歸併段至少有一個數字
	{
		while(low1 <= high1 && low2 <= high2)
		{
			if(arr[low1]<=arr[low2])
			{
				brr[i++] = arr[low1++];
			}
			else
			{
				brr[i++] = arr[low2++];
			}
		}

		//兩個歸併段比完之後還有可能某個歸併段剩餘資料
		while(low1 <= high1)
		{
			brr[i++] = arr[low1++];
		}
		while(low2 <= high2)
		{
			brr[i++] = arr[low2++];
		}

		low1 = high2+1;
		high1 = low1+gap-1;

		low2 = high1+1;
		high2 = low2+gap-1<len?low2+gap-1:len-1;
	}

	while(low1 < len)//不足兩個歸併段
	{
		brr[i++] = arr[low1++];
	}

	for(i = 0 ;i < len ;++i)
	{
		arr[i] = brr[i];
	}
	delete []brr;
}
void Merge_Sort(int *arr,int len)
{
	if(arr == NULL ||len < 0)
		return ;
	for(int i = 1;i < len ;i = i*2)
	{
		Merge(arr,len,i);
	}
}

/*
堆排序
將陣列中的值虛擬的建立成一個根堆
一次調整堆從上往下調整 時間複雜度為O(logn)
第一次建堆從下往上 建堆時間複雜度為O(n*lon)
堆排序的時間複雜度為 O(n*lon)
空間複雜度為O(1)
應用場景  
  在不要求完全有序的情況下得到資料中最大或者最小的部分資料 可以選擇堆排序
*/

void Adjust(int *arr,int start,int end)
{
	//將開始的值儲存在臨時量中
	int tmp = arr[start];
	

	for(int i = 2*start+1;i < end ;i = 2*start+1)
	{
		//i+1 < end保證了有右孩子的存在  每次還需將i的左右孩子進行比較 i每次的落腳點都是左孩子處
		if((i+1<=end) && arr[i] < arr[i+1])
		{
			i++;//i下標始終對應左右孩子中的最大值
		}
		//判斷父節點的值與左右孩子中最大值的大小關係 如果小於最大值 則兩個值進行交換
		//交換後需要比較i所在節點它的左右孩子與其當前值的大小關係
		if( tmp < arr[i])
		{
			arr[start] = arr[i];
			start = i;
		}
		else
		{
		//左右孩子值都小於父節點的值
			break;
		}
	}
	arr[start] = tmp;
}
void Heap_Sort(int *arr,int len)
{
	if(arr == NULL || len < 0)
		return ;
	int i;
	//len-1是最後一個元素 根據最後一個元素下標推其父親節點下標  i-1/2 所以本題中即為len-1-1/2
	for( i = (len-1-1)/2;i>=0;--i)
	{
		Adjust(arr,i,len-1);
	}

	int tmp = 0;
	for( i = 0;i < len -1 ;++i)
	{
		tmp = arr[0];
		arr[0] = arr[len-1-i];
		arr[len-1-i] = tmp;
		Adjust(arr,0,len-1-i-1);
		//由於最後一個已經是有序了的 再一次調整時他就不參與了
	//即就是每一次的交換後最後一個元素都不參與調整過程
	}
	
}