常見排序演算法及其演算法思路解讀
幾種常見的排序演算法及其演算法思路:
1.選擇排序 O(n方)
所謂選擇就是每次都選擇一個最小的元素放在最前面。思路是去尋找最小元素的座標,然後將最小座標的元素與當前座標的元素交換。具體的做法是假設當前迴圈的第一個座標是最小元素的座標,然後從第二個元素開始遍歷(內層迴圈),如果該元素比最小座標的的元素小,就把最小元素的座標修改為該元素的座標,最後交換選出的最小元素座標所在的元素和當前外層迴圈正在處理的元素兩個元素,實現程式碼如下:
//交換陣列中的兩個元素 private static void swap(int[] arr, int i, int j) { int t = arr[i];arr[i] = arr[j]; arr[j] = t; } //選擇排序 public static void selectSort(int[] arr) { int n = arr.length; for (int i = 0; i < n; i++) { int minIndex = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) minIndex = j; swap(arr, minIndex,i); } } }
2.插入排序 O(n方)
插入排序的操作和排序撲克牌的操作及其相似,目標就是每個元素都選擇正確的位置進行插入,使得該序列始終處於有序狀態。想象以下你打撲克牌的摸牌的時候,摸第一張牌,他本身就是有序的,無需任何操作,摸第二張牌就要考慮下,較小就放前面,較大就放後面,剩下的牌插入時,從後往前看,比前面的小就交換一次,直到比前面大就說明找到了合適的位置。最後完成插入排序,可以看出插入排序有提前結束內層迴圈的機會,所以它在排序較為有序的序列時速度很快,以下是程式碼實現:
//插入排序 public static void insertSort(int[] arr) { intn = arr.length; for (int i = 1; i < n; i++) { for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) { swap(arr, j, j - 1); } } }
3.快速排序O(n*logn)
快速排序本身是一種分治演算法,即把一個大問題拆成多個相同的結構的小問題,然後遞迴解決小問題,大問題也就得到了解決。快速排序把問題拆分成一個個小的partition操作,partition操作是選擇一個參考元素,並使得參考元素左邊的元素都比他小,右邊的都比他大.操作的具體做法為:選擇要排序序列左端點的元素v為參考元素,把序列分為左partA,和右partB兩個部分,使得partA都比v小,partB都v大,用 j 這個元素記錄partA的最後一個元素的座標(初始值為左端點l),從v後面的元素向右端點遍歷,如果比v小,partA容量+1(j++),並把它作為partA的最後一個元素 ,比v大就不作任何操作,最後將參考元素 v(座標為l),與partA最後一個元素(座標為j)兩個元素交換位置完成partition操作,並返回參考元素最後所在位置(j),之後在遞迴對j左右兩邊進行快速排序,排序完成。
// 快速排序 public static void quickSort(int[] arr) { int n = arr.length; qSort(arr, 0, n - 1); } private static void qSort(int[] arr, int l, int r) { if (l >= r) return; int p = partition(arr, l, r); qSort(arr, l, p); qSort(arr, p + 1, r); } private static int partition(int[] arr, int l, int r) { int v = arr[l]; int j = l; for (int i = l + 1; i <= r; i++) { if (arr[i] < v) {j++; swap(arr, j, i); } } swap(arr, l, j); return j; }
4. 歸併排序(O(n*logn))
歸併排序也是一種分治演算法,這裡只說自下而上的歸併排序的實現,歸併排序的基本思想是採用二分的方式對序列進行分割,當分到每一小塊都只有一個元素時,每個片段就都是有序的,然後自下而上進行merge(歸併操作),所以,歸併排序的核心也是這個merge操作,歸併操作需要藉助一個輔助的空間來協助我們排序,首先歸併時左右兩個半邊都須是有序的,具體思路如下:開闢一個和原要歸併序列相同的輔助序列,然後對原序列的每個座標(用k表示)進行選舉操作,即從輔助序列中選舉出合適的元素放在該座標,直至遍歷完原序列的每一個座標。用 i 這個元素標記輔助序列左半邊正在處理元素的座標,用 j 這個元素標記輔助序列右半邊正在操作的座標,然後將i所在的元素與j所在的元素進行比較,誰更小就作為被選中的目標放在原序列正在處理的座標上,並且該半邊正在操作的元素的座標後移一位,即(i++或j++),當然如果某半邊操作完成,原序列選舉時沒的可選,就把剩餘半邊未操作的元素依次放在原序列正在選舉的座標上,完成該序列的歸併操作,最終通過遞迴的方式完成歸併排序,以下是程式碼實現:
//歸併排序 public static void mergeSort(int[] arr) { int n = arr.length; mSort(arr, 0, n - 1); } private static void mSort(int[] arr, int l, int r) { if (l >= r) return; int mid = (l + r) / 2; mSort(arr, l, mid); mSort(arr, mid + 1, r); merge(arr, l, mid, r); } private static void merge(int[] arr, int l, int mid, int r) { int temp[] = Arrays.copyOfRange(arr, l, r + 1); int i = l, j = mid + 1; for (int k = l; k <= r; k++) { if (i > mid) { arr[k] = temp[j - l]; j++; } else if (j > r) { arr[k] = temp[i - l]; i++; } else if (temp[i - l] < temp[j - l]) { arr[k] = temp[i - l]; i++; } else { arr[k] = temp[j - l]; j++; } } }
5.堆排序
堆排序就是藉助堆(heap)這種特殊的資料結構進行排序。堆是一顆完全的二叉樹(即除了最後一層,每一層節點的個數都是飽和的,且最後一層即使不飽和,節點也都集中在左側),可以通過實現一個最大堆來實現堆排序,最大堆(所有父節點都大於它的子節點),下面是使用陣列的方式實現一個最大堆,從陣列座標為的1的位置開始存放元素,這樣,每個元素左孩子座標為父節點座標的兩倍,右孩子是父節點座標的兩倍+1, 入堆和出堆操作時使陣列始終保持最大堆的特性,核心是兩個shift操作。下面是最大堆程式碼實現 。
public class MaxHeap<Item extends Comparable> { protected Item[] data; protected int count;//當前元素的個數 protected int capcity;//最大堆的容量 public MaxHeap(int capcity){ data= (Item[]) new Comparable[capcity+1]; count=0; this.capcity=capcity; } public MaxHeap(Item[] items){ int n=items.length; capcity=n; data= (Item[]) new Comparable[n+1]; count=n; for(int i=1;i<=n;i++) data[i]=items[i-1]; for(int i=n/2;i>=1;i--) shifdown(i); } // 向最大堆中插入一個新的元素 item public void insert(Item item){ assert count+1<=capcity; data[count+1]=item; count++; shiftup(count); } // 向最大堆中取出入一個新的元素 item public Item extactTop(){ assert count>0; Item item=data[1]; swap( 1 , count ); count--; shifdown(1); return item; } private void swap(int i,int j){ Item t=data[i]; data[i]=data[j]; data[j]=t; } private void shiftup(int k) { while(k>1 && data[k/2].compareTo(data[k])<0){ swap(k/2,k); k =k/2; } } private void shifdown(int k) { while (k*2<=count){ int j=k*2; if(j+1<=count && data[j].compareTo(data[j+1])<0) j++; if(data[k].compareTo(data[j])>0) break; swap(k,j); k=j; } } public static void main(String[] args) { // MaxHeap maxHeap=new MaxHeap<Integer>(100); // for(int i=1;i<100;i=i+2){ // maxHeap.insert(i); // } // // for(int i=1;i<100;i=i+2){ // System.out.print(maxHeap.extactTop()+ " "); // } Integer[] arr=new Integer[]{1,4,9,7,5,3,1}; MaxHeap maxHeap=new MaxHeap<Integer>(arr); for (int i=0;i<arr.length;i++){ System.out.print(maxHeap.extactTop()+" "); } } }