排序中比較複雜的有歸併排序,快速排序,堆排序三大演算法了,三個演算法的時間複雜度都是o(n * logn)
,三個演算法的思想我就簡單的展開詳述以下。
歸併排序的核心思想是鍊錶中的經典題目:合併兩個有序鍊錶。
劍指offer:合併兩個排序的鍊錶
leetcode p21: merge two sorted lists
採用分治的思想,將整個陣列分為兩個部分,先對左邊的陣列進行歸併排序,再對右邊的陣列進行歸併排序,最後兩者進行merge
。
下面的函式就是歸併排序的歸併部分,將兩個已經有序的陣列歸併。
public class mergesort implements sort
public void mergesort(int arr, int l, int r)
public void merge(int arr, int l, int mid, int r) else
}while (left <= mid)
while (right <= r)
for (int i = 0; i < newarr.length; i++)
}}
堆排序首先就要利用堆這個資料結構。首先先將整個陣列結構調整成堆。
heapinset
函式插入某個數字,並且將插入的部分進行調整。對於乙個陣列,從位置0開始插入,並且邊插入邊進行調整。
public class heapsort implements sort
int heapsize = arr.length;
swap(arr, 0, --heapsize);
while (heapsize > 0)
}public void heapinsert(int arr, int index)
}public void heapify(intarr, int index, int heapsize)
swap(arr, index, maxindex);
index = maxindex;
left = maxindex * 2 + 1;}}
public void swap(int arr, int i, int j)
}
這樣處理完之後,陣列就成為了乙個最大堆。要將陣列調整為有序的,要經歷以下幾步:
陣列的第乙個數是最大堆的頂,是最大的數,每次將第乙個數和最大堆的最後乙個數交換。這樣後面部分就排好序了
交換完之後,最大堆的結構就會被破壞了,所以需要調整最大堆。
//交換到最後乙個位置
swap(arr, 0, --heapsize);
while (heapsize > 0)
調整位置
public void heapify(intarr, int index, int heapsize)
swap(arr, index, maxindex);
index = maxindex;
left = maxindex * 2 + 1;
}}
快速排序的核心思想思想是選乙個數,不管是隨機的還是固定的,每一步將大於這個數的數字放在陣列的右邊,小於的所有數字放在左邊,聽起來是不是很熟悉,這就是經典題目荷蘭國旗的思想啦。
leetcode p75: sort colors
public class quicksort implements sort
public void quicksort(int arr, int l, int r)
public int partition(int arr, int l, int r) ;
int num = arr[r];
int left = l - 1;
int right = r;
int cur = l;
while (cur < right) else if (arr[cur] > num) else
}swap(arr, right++, r);
mids[0] = left;
mids[1] = right;
return mids;
}public void swap(int arr, int i, int j)
public static void main(string args) ;
quicksort q = new quicksort();
q.sort(arr);
}}
master
公式是為了評估遞迴函式的複雜度而誕生的。
t(n) = a* t(n / 2) + o(n^d)
log(b,a) > d
時,時間複雜度是o(n ^ log(b,a))
log(b,a) = d
時,時間複雜度是o(n ^ d + logn)
log(b,a) < d
時,時間複雜度是o(n^d)
使用master
公式計算三個排序的時間複雜度
merge-sort
的核心**如下:
public void mergesort(int arr, int l, int r)
時間複雜度估算:
part 1
部分將問題分為兩個部分,所以master
中a=2,b=2
。
part 2
部分是有序鍊錶的合併,時間複雜度是:o(n)
,所以d=1
。
所以套用master
公式中的第二條,時間複雜度是o(n*logn)
。
merge-sort
的核心**如下:
public void sort(int arr)
int heapsize = arr.length;
(2)part2
swap(arr, 0, --heapsize);
while (heapsize > 0)
}
時間複雜度估算:
最大堆的插入後調整的時間複雜度是o(logn)
,part 1
部分是插入n
個元素,時間複雜度是log1+log2+log3+log4+...+logn = n
,所以part 1
的時間複雜度是o(n)
。
part 2
部分中heapify
的時間複雜度是logn
,heapify
就是堆的調整,所以part 2
部分的時間複雜度是o(n * logn)
。
加起來就是o(n)+o(n*logn)
,取最大項即為o(n*logn)
。
quick-sort
的核心**如下:
public void quicksort(int arr, int l, int r)
時間複雜度估算:
part 1
中的partition
思想是荷蘭國旗問題,時間複雜度是o(n)
。
part 2
和歸併排序一樣,也是將問題分為了兩個部分,所以a=2,b=2
。
所以快速排序的時間複雜度是o(n*logn)
。
除了歸併排序中,需要提前分配o(n)
的空間,作為merge
過程中的快取。堆排序和快速排序全部都是in-place
修改的,所以不需要分配額外空間。
其實在在歸併排序和堆排序過程中,因為函式遞迴,函式占用的棧位址也算空間複雜度,但是為什麼沒有算進去呢?個人認為是因為在一些高手的眼中,遞迴函式是可以改成迴圈,所以這就不用考慮這些額外空間了。
快速排序 堆排序 歸併排序
快速排序 includeusing namespace std int a 8 int sz sizeof a sizeof int int partition int a,int p,int r int main const int sz sizeof a sizeof int 堆化 保持最大堆的...
快速排序,歸併排序,堆排序
l r 1在奇數長度陣列的中間,偶數長度陣列的左半邊最後乙個,極限情況下陣列只有兩個數,i遇到第乙個數就會停下,此時模擬i,j下標的數有無交換的情況,會發現j在停下來時都位於第乙個數的位置,因此用j,j 1才不會越界 l r 1 1在奇數長度陣列的中間往右第乙個,偶數長度陣列的右半邊第乙個,極限情況...
關於快速排序,歸併排序,堆排序
1 快速排序 quicksort 快速排序是乙個就地排序,分而治之,大規模遞迴的演算法。從本質上來說,它是歸併排序的就地版本。快速排序可以由下面四步組成。1 如果不多於1個資料,直接返回。2 一般選擇序列最左邊的值作為支點資料。3 將序列分成2部分,一部分都大於支點資料,另外一部分都小於支點資料。4...