1. 程式人生 > >能仔練習1----一次失敗的二分排序

能仔練習1----一次失敗的二分排序

因為最近要準備頭條和騰訊的面試,開始刷演算法,可是自己還是練得太少,刷演算法效率太低.不過無所謂了,每刷一道,就比以前的自己厲害一點.

今天的主題是插入排序,.這麼簡單的排序,卻困擾了我很久.首先說一下理解

插入排序的原理就是,首先認定第一個元素是有序的,然後從第二個元素開始,往第一個元素合適的位置插(大於=放在後面,小於交換位置,放在前面)–>所以它是穩定的(遇到相等的不調整位置).
然後第一次插入就導致了一個後果—>前面兩個元素都有序了,接著我們從第三個元素開始插入,從第二個元素開始,依次與第三個元素比較,插入到最合適的位置(即插入到剛好小於哪個元素的位置)
這樣,迴圈N-1次(因為是從第二個元素開始的)

,就完成了N-1個元素的插入,完成了有序

接下來看程式碼,普通的實現

void swap(int &a,int &b)
{
	int temp=a;
	a=b;
	b=temp;
}
void insert(int *arr,int n)
{
	if(arr==NULL||n<2) 
		return;
	for(int j=1;j<n;j++)//一共插n-1個數
		for(int i=j-1;i>=0;i--)//總是和將要插入的資料前面一個相比較
		{
			if(arr[i]>arr[i+1])
				swap(arr[i+1],arr[i]);
//若插入資料小,則交換資料,最多交換j次 } }

這段程式碼比較簡單,還是有地方寫錯了,比較的應該是i+1和i處的值,而不是i和j處的值,因為j只有第一次是新值,第一次若j<i;則swap(a[i],a[j])此時a[j]已經換了值,原來的a[j]到了a[i];所以應該比較a[i]和a[i+1]

從上面的程式碼可以瞭解到,每插入一個數,前面的有序部分就多一個值,而我們每次插入的時候都要通過反向遍歷的方式和前面的數一一比較,來找到最佳插入位置,因為前面的數已經有序,我們可以想到一種更高效的遍歷方法–>二分查詢
接著看下面的程式碼

void binarysearch(int *arr,
int left,int right,int key) { if(left>right||arr==NULL) return; while(left<=right) { int mid=left+((right-left)>>1); if(arr[mid]>key) right=mid-1; else if(arr[mid]<=key) left=mid+1; } return left;//返回最左邊下標 } void insertsort(int *arr,int n) { if(arr==NULL||n<2) return; for(int j=1;j<n;j++) { int temp=arr[j];//儲存要插入的數,因為一會要進行覆蓋 int index=binarysearch(arr,0,j-1,arr[j]);//找到插入下標 for(int i=j;i>index;i--) arr[i]=arr[i-1];//將數字覆蓋到它的下一個座標 arr[index]=temp;//將插入的值放到正確的位置 } }

這個排序方法,我寫的時候犯了個錯誤,就是在找到插入下標以後,要將插入位置後的資料全部向前移動一個位置,然後再向插入位置插數,我寫的時候,移動的方向是從index到j,這就導致了假如說index位置是4,則後面的數全被覆蓋成了4,當時是這樣寫的

for(int i=index;i<j;i++)
			arr[i+1]=arr[i];//將數字覆蓋到它的下一個座標

看來寫移動的時候,最好是從後往前覆蓋,這樣才不會有問題