排序演算法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