1. 程式人生 > >排序演算法02——插入排序(直接、折半)、快速排序

排序演算法02——插入排序(直接、折半)、快速排序

插入排序:

插入排序的思路就是,前面的陣列已經有序(從第二個數看來,第一個數已經有序了,它只要找到自己的插入點插入就行了;然後第三個數看前兩個數都已經有序了....以此類推),下標為i的這個值依次與前面的值比較,找到合適的地方就可以直接插入了。但是,陣列的插入是需要插入位以後的資料全部後移,所以我們邊比較邊移位,這樣才能在找到合適位置的情況下直接插入。

程式碼如下:

void insert_sort(int arr[],int len)
{
	for (int i = 0; i < len-1; i++) //把下標為i的元素插入到前面有序的陣列中 
	{
		int num = arr[i+1];   //i=0時只有一個數肯定是有序的 因此從第二個數記錄要插入的值  
		int j = 0;
		for (j = i;j>=0;j--)  
		{
			if (arr[j] > num)  //如果前面的數大於記錄的值
			{
				arr[j+1] = arr[j]; //那麼數往後移動 這裡不用擔心覆蓋 因為i+1的值已經被記錄在num裡了
			}
			else
			{
				break; // arr[j]小於num  說明找到了可以插入的位置arr[j+1]
			}
		}
		arr[j+1] = num; //插入
	}
}

插入排序分直接插入和折半插入,時間複雜度都為O(n^2)

上面的就是直接插入 ,對於直接插入我們發現效率不是很高,每次要找到插入點都需要遍歷前面的部分陣列,運氣好第一個就是,運氣不好要遍歷全部。因此,折半插入做到了優化,每次都與有序陣列(這裡與上述一樣)的中間值比較,如果值小於中間值,說明要插入的地方在中間值左側,反之則在右側。

程式碼如下:

void binary_insertsort(int arr[],int len)
{
	for (int i = 0; i < len-1; i++)
	{
		int num= arr[i+1]; //插入數的下標從1開始
		int left = 0; //左區間
		int right = i; //右區間  [0,i]
		while(left <= right)
		{
			int mid = (left+right)/2; //中間值下標
			if (num < arr[mid])
			{
				right = mid-1; //小 說明插入位置在左側 [0,mid-1]
			}
			else
			{
				left = mid+1; //大  說明在右側 [mid+1,i]
			}
		}
		for (int j = i; j >= left ; j--)
		{
			arr[j+1] = arr[j]; //資料後移
		}
		arr[left] = num; //插入
	}
}

快速排序:

     快速排序的思想光是用文字描述有點吃力,所以我畫了張圖來便於理解。


這裡需要幾個引數 一個是pos(position,基準位置) 一個是 key (基準值) ,快速排序中有個基準值的概念,在當前迴圈下,基準值左邊的數要小於基準值,右邊的數要大於基準值。下面我用圖來解釋下這是如何實現的

首先,第一個基準值為3,下標為0。記key = 3; pos = 0; left ,right為當前需要排序的區間

第一次迴圈時,我們先從右邊往左看,發現,右邊第一個數(1)就小於key值(3),按照基準值的概念,右邊的數都必須大於基準值,因此我們把1放到下標為pos的位置上,同時把pos賦值為1的下標7,即pos = 7 


然後我們從左邊開始看,第一個數1小於key(3),符合基準值的概念,第二個數5大於key(3),不符合,因此我們就把5放到當前下標為pos的地方,同時pos也置為5的下標,即1。


此時發現右邊的數都大於key值(3),左邊的數都小於key值。那麼我們就把key值放回下標為pos的位置上即可。


第一次迴圈結束 發現左邊已經有順序了 而右邊還是比較亂 因此這裡,我們用到了遞迴,遞迴呼叫本身來處理區間為[pos+1,right]的陣列 ,(如果是左邊無序,則是處理區間[left,pos-1],都亂的話那就都處理了)  

注意:快速排序的迴圈中僅僅只是更換pos的值,而key值在此次迴圈中一直保持不變,直到找到自己合適的地方放入後。通過遞迴呼叫來更換下一個key值。

程式碼如下:

void quickArr(int arr[],int left,int right)
{
	int pos = left; //left作為基準下標 使得基準左邊皆小於基準,右邊皆大於
	int key = arr[pos];
	int i=left;
	int j=right;
	while(j>i)
	{
		while(j>i && arr[j]>key)
		{
			j--;
		} //迴圈找到右邊小於key的值
		if (j>i)
		{
			arr[pos] = arr[j];
			pos = j;
		}
		while(j>i && arr[i]<=key)  //=號放上面或下面都可以 
		{
			i++;
		} //迴圈找到左邊大於key的值
		if (j>i)
		{
			arr[pos] = arr[i];
			pos = i;
		}
	}
	arr[pos] = key;
	if (pos-1 > left)
	{
		quickArr(arr,left,pos-1);
	}
	if (right>pos+1)
	{
		quickArr(arr,pos+1,right);
	}
}

最後在封裝一下就ok了

void quickSort(int arr[],int len)
{
	quickArr(arr,0,len-1);
}

快速排序的時間複雜度為O(nlogn)   

計算過程: T(n)=2T(n/2)+n   n為第一次迴圈遍歷所需   2T(n/2)表示遞迴所需的時間 

T(n/2)=2T(n/4)+n/2 .....  一直到T(2)= 2T(1)+2    

可以得到T(n) = 2T(n/2)+n=4T(n/4)+2n=8T(n/8)+3n=.....=nT(1)+n*logn  (T(1)為單位時間) 冪級數n小於nlogn 略去 

得到時間複雜度為O(nlogn)  log底數為2