/** 快速排序前端
最壞O(N^2) 說明:當待排序的序列在排序前是有序(順序或逆序)的,此時的時間複雜度最差(這種狀況下,快排退化成了冒泡排序)。
平均O(NlogN)
最好O(NlogN)
*/ public class QuickSort {node
// ------- 1.數組的快速排序 ------- public int[] quickSort(int[] array, int left, int right) { if (left < right) { int pivotPosition = partition(array, left, right); // 獲取基準元素的下標 quickSort(array, left, pivotPosition - 1); // 對基準元素的左邊進行排序 quickSort(array, pivotPosition + 1, right); // 對基準元素的右邊進行排序 } return array; } public int partition(int[] array, int left, int right) { int pivot = array[left]; // 選取數組的頭元素做爲基準元素 int pivotPosition = left; // 基準元素的索引 /** * 循環結束後: * 1)pivotPosition後面的元素都大於等於基準元素pivot * 2)pivotPosition前面的元素(包括pivotPosition指向的元素):除了array[left]外,其它的元素都小於基準元素pivot * 注:循環結束後,咱們還要將array[left]與array[pivotPosition]進行交換,以實現pivotposition前面的元素(不包括pivotPosition指向的元素)都小於基準元素 */ for (int i = pivotPosition + 1; i <= right; i++) { // i表示當前元素的索引 if (array[i] < pivot) { pivotPosition++; if (pivotPosition != i) { swap(array, pivotPosition, i); } } } /** * 目的: * 將pivotPosition指向基準元素pivot,而且返回pivotPosition,以肯定遞歸函數quickSort()中left參數與right參數的值 * 實現: * 交換基準元素與pivotPosition當前指向元素的位置。 */ if (pivotPosition != left) { array[left] = array[pivotPosition]; array[pivotPosition] = pivot; } return pivotPosition; } // ------- // ------- 2.鏈表的快速排序 ------- public void quickSort(Node left, Node right) { if (left == null || right == null || left == right) return; Node pivotPosition = partition(left, right); // 獲取基準元素的下標 quickSort(left, pivotPosition); // 對基準元素的左邊進行排序 quickSort(pivotPosition.next, right); // 對基準元素的右邊進行排序 } public Node partition(Node left, Node right) { int pivot = left.value; // 選取鏈表的頭元素做爲基準元素 Node pivotPosition = left; // 基準元素的引用 for (Node i = pivotPosition.next; i != right.next; i = i.next) { // i表示當前元素的引用 if (i.value < pivot) { pivotPosition = pivotPosition.next; if (pivotPosition != i) { swap(pivotPosition, i); } } } if (pivotPosition != left) { left.value = pivotPosition.value; pivotPosition.value = pivot; } return pivotPosition; } // ------- /** * 數組中,交換兩個元素的位置 */ private void swap(int[] array, int pivotPosition, int i) { int temp = array[pivotPosition]; array[pivotPosition] = array[i]; array[i] = temp; } /** * 鏈表中,交換兩個元素的值 */ private void swap(Node pivotPosition, Node i) { int temp = pivotPosition.value; pivotPosition.value = i.value; i.value = temp; } public static void print(int[] array) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + ","); } System.out.println(); } public static void print(Node head) { ArrayList<Object> list = new ArrayList<>(); while (head.next != null) { list.add(head.value); head = head.next; } list.add(head.value); System.out.println(list.toString()); } public static void main(String[] args) { QuickSort qs = new QuickSort(); // test array quicksort int[] array = {2, 5, 9, 3, 17, 4, 18, 7, 6}; int[] sortArray = qs.quickSort(array, 0, array.length - 1); print(sortArray); // test linkedlist quicksort Node head = new Node(5); Node node1 = new Node(3); Node node2 = new Node(2); Node node3 = new Node(6); Node node4 = new Node(7); Node node5 = new Node(17); Node node6 = new Node(9); head.next = node1; node1.next = node2; node2.next = node3; node3.next = node4; node4.next = node5; node5.next = node6; qs.quickSort(head, node6); print(head); }
}算法
============================================================================數組
/**函數
最壞O(N^2) 說明:當待排序的序列在排序前是逆序的,此時的時間複雜度最差
平均O(N^2)
最好O(N) 說明:當待排序的序列在排序前是順序的,此時的時間複雜度最好
*/ public class BubbleSort {測試
public static void sort(int[] array) { // 總共冒泡array.length-1次,每次冒泡都會將未排序序列中的最小元素放到未排序序列的最前端。 for (int i = 0; i < array.length-1; i++) { // 一趟冒泡(排序):從後往前比較相鄰的元素,較小的元素往前冒泡,最終將最小的元素放到未排序序列的最前端。 for (int j = array.length - 1; j > 0; j--) { if (array[j] < array[j - 1]) { swap(array, j, j - 1); } } } } /** * 數組中,交換兩個元素的位置 */ public static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void main(String[] args) { int[] data = {2, 5, 9, 3, 17, 4, 18, 7, 6}; sort(data); System.out.println(Arrays.toString(data)); }
}優化
============================================================================ui
/**this
最壞O(N^2)
平均O(N^2)
最好O(N^2)
*/ public class SelectSort {.net
public static void sort(int[] array) { // 將array[i]和array[i+1 ~ a.length]中的最小元素進行交換 for (int i = 0; i < array.length - 1; i++) { // 記錄本次循環中最小元素的下標,初始值爲第一個元素的下標 int minIndex = i; // 將本次循環中最小值的下標賦值給minIndex for (int j = i + 1; j < array.length; j++) { if (array[j] < array[minIndex]) { minIndex = j; } } if (minIndex != i) { // 將本次(第i次)循環中的最小值a[minIndex]與a[i]進行交換 swap(array, minIndex, i); } } } /** * 數組中,交換兩個元素的位置 */ public static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void main(String[] args) { int[] data = {2, 5, 9, 3, 17, 4, 18, 7, 6}; sort(data); System.out.println(Arrays.toString(data)); }
}
============================================================================
/**
最壞O(N^2)
平均O(N^2)
最好O(N)
*/ public class InsertSort {
public static void sort(int[] array) { int insertElement; // 要插入的元素的值 int j; // 有序序列的下標 /** * 經過遍歷前面的有序序列,將下標爲i的元素插入到不大於本身的元素的後面。 */ for (int i = 1; i < array.length; i++) { // 將array[i]做爲要插入的元素,從數組的第二個元素開始遍歷。 insertElement = array[i]; // 要插入的元素 j = i - 1; // 有序序列最後一個元素的下標 (注:要插入的元素 前面的序列是有序序列,從後向前遍歷前面的有序序列)。 while (j >= 0 && array[j] > insertElement) { // 1.若是下標爲j的元素大於要插入的元素,則將下標爲j的元素向後移動一位。 array[j + 1] = array[j]; j--; } array[j + 1] = insertElement; // 2.直到下標爲j的元素不大於要插入的元素,則將要插入的元素插入到下標爲j+1的位置。 } } public static void main(String[] args) { int[] data = {2, 5, 9, 3, 17, 4, 18, 7, 6}; sort(data); System.out.println(Arrays.toString(data)); }
}
============================================================================
/**
平均O(N^1.3)
*/ public class ShellSort {
public static void sort(int[] array) { if (array == null || array.length <= 1) { return; } int increment = array.length / 2; // 增量 while (increment > 0) { for (int i = 0; i < array.length; i++) { // 遍歷待排序序列 for (int j = i; j+increment < array.length; j += increment) { // 對子序列進行插入排序 if (array[j] > array[j+increment]) { swap(array,j,j+increment); } } } increment = increment / 2; // 設置新的增量,進行下一輪的遍歷。 } } /** * 數組中,交換兩個元素的位置 */ public static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void main(String[] args) { int[] data = {2, 5, 9, 3, 17, 4, 18, 7, 6}; sort(data); System.out.println(Arrays.toString(data)); }
}
============================================================================
/**
<p>
最壞O(NlogN)
平均O(NlogN)
最好O(NlogN)
*/ public class MergeSort {
public static int[] sort(int[] array, int low, int high) { if (low < high) { // 當low=high時,說明已經分解到單個元素了 int mid = (low + high) / 2; sort(array, low, mid); // 遞歸地對左半部分進行分解 sort(array, mid + 1, high); // 遞歸地對右半部分進行分解 merge(array, low, mid, high); // 分解已完成,將左子序列和右子序列進行合併 } return array; } /** * 將左子序列和右子序列合併爲一個有序序列。 * * [@param](https://my.oschina.net/u/2303379) array * [@param](https://my.oschina.net/u/2303379) low * [@param](https://my.oschina.net/u/2303379) mid * [@param](https://my.oschina.net/u/2303379) high */ public static void merge(int[] array, int low, int mid, int high) { int[] temp = new int[high - low + 1]; // 臨時數組,長度與原數組長度一致 int i = low; // 左邊序列的第一個元素的下標 int j = mid + 1; // 右邊序列的第一個元素的下標 int k = 0; // 臨時數組temp的下標 // eg: 左邊的序列爲:6 9 13 右邊的序列爲:4 7 /** * 把較小的數先copy到臨時數組temp中: ==> 依次將 四、六、7 copy到臨時數組temp中。 */ while (i <= mid && j <= high) { if (array[i] < array[j]) { temp[k++] = array[i++]; } else { temp[k++] = array[j++]; } } // 若是左邊的序列中還有未copy過的元素,則把左邊剩餘的元素copy臨時數組temp中。 ==> 依次將 九、13 copy到臨時數組temp中。 while (i <= mid) { temp[k++] = array[i++]; } // 若是右邊的序列中還有未copy過的元素,則把右邊剩餘的元素copy臨時數組temp中。 while (j <= high) { temp[k++] = array[j++]; } // 用排好序的臨時數組temp覆蓋原數組 for (int t = 0; t < temp.length; t++) { array[t + low] = temp[t]; } } public static void main(String[] args) { int[] data = {2, 5, 9, 3, 17, 4, 18, 7, 6}; sort(data,0,data.length-1); System.out.println(Arrays.toString(data)); }
}
============================================================================
/**
堆的概念:堆是一棵順序存儲的徹底二叉樹。
堆分爲大根堆和小根堆:
大根堆:每一個節點的值不小於等於其左、右孩子的值
小根堆:每一個節點的值不大於等於其左、右孩子的值
1)將一個待排序的序列構形成一個堆:從最後一個非葉子結點向上遍歷,直到全部的非葉子節點都遍歷完畢(篩選法)。
2)移走堆頂元素後,將剩餘的元素再次構形成一個新的堆。
1)將待排序的n個元素構形成一個大根堆。
2)移走堆頂元素(即:將堆頂元素與堆數組的末尾元素進行交換,此時末尾元素爲最大值)。
3)將剩餘的n-1個元素從新構形成一個大根堆,重複上面的步驟,直到剩餘的元素只有一個時,排序完成。
將一個徹底二叉樹按層序排號依次存入數組:
8
/ \
3 9 ---按層序排號存入數組---> [8,3,9,5,7,4,2]
/ \ / \
5 7 4 2
1)下標爲i的節點,其父節點的下標爲(i-1)/2
2)下標爲i的節點,其左子節點的下標爲2*i+1,右子節點的下標爲2*i+2
問題:top K
方案:
第1步)分治:先將全部的數據按照hash方法分解成多個較小數據集。
第2步)使用堆排序分別找出這幾個較小數據集中的topK。
第3步)將第2步中各數據集的topk放在一個集合裏,而後求出最終的topK。
1)時間複雜度:最好、最壞、平均 的複雜度都是 O(nlogn),故堆排序堆輸入的數據不敏感。
建堆:O(n)
調整:O(log n)
2)空間複雜度:堆排序是就地排序,故其空間複雜度爲O(1)。
*/ public class HeapSort {
public static int[] array; public static int adjustTime = 0; public static int whileTime = 0; public HeapSort(int[] array) { this.array = array; } /** * 根據子節點的索引來獲取父節點的索引 * * [@param](https://my.oschina.net/u/2303379) child * @return */ public static int parentIndex(int child) { return (child - 1) / 2; } /** * 根據父節點的索引來獲取左子節點的索引 * * @param parent * @return */ public static int leftChildIndex(int parent) { return parent * 2 + 1; } /** * 第一步:將待排序的n個元素構形成一個大根堆。 * * 將數組初始化爲大根堆:從下(最後一個非葉子節點)往上(堆的根節點)循環遍歷。 * * 要點:遍歷存在左子節點的父節點,從最後一個非葉子結點開始遍歷。 * 說明: * 1)徹底二叉樹是按層序排號存入數組的,故二叉樹的最後一個節點(即:數組中索引值最大的元素)必定是葉子節點,故最後一個節點必定有父節點,且最後一個節點的父節點就是 堆最後一個非葉子節點。 * 2)二叉樹的最後一個節點的索引爲array.length-1,則其父節點(即:最後一個非葉子節點)的索引爲(array.length-1-1)/2,故咱們從array.length/2-1開始遍歷! */ public static void initHeap() { // 從下往上的循環 for (int parentIndex = parentIndex(array.length-1); parentIndex >= 0; parentIndex--) { adjustHeap(array, parentIndex, array.length - 1); } } /** * 對堆進行排序 */ public static void sortHeap() { // array.length-1次 調整完成排序 for (int i = array.length - 1; i > 0; i--) { // 第二步:將堆頂元素(數組中第一個元素)和當前未排序子序列中的最後一個元素交換 swap(array, 0, i); // 第三步:交換後,將剩餘的n-1個元素從新構形成一個大根堆 adjustHeap(array, 0, i-1); } } /** * 調整堆:從上往下循環遍歷,即 沿父節點的較大子節點向下調整 * * @param array * @param parentIndex * @param maxIndex */ public static void adjustHeap(int[] array, int parentIndex, int maxIndex) { adjustTime++; int temp = array[parentIndex]; // 父節點的值 int child = leftChildIndex(parentIndex); // 左子節點的索引 // 從上往下循環:沿父節點的較大子節點向下調整 while (child <= maxIndex) { // 左子節點必須在未排序子序列中 whileTime++; // 記錄循環的次數,測試用。 // 若當前節點(即:父節點)存在右子節點(且右子節點在未排序的子序列中),而且右子節點的值大於左子節點時,將右子節點的索引賦值給child if (child + 1 <= maxIndex && array[child + 1] > array[child]) { child++; // 將左子節點轉換爲右子節點 } // 此時,child表示 子節點中值最大的那個節點 的索引 // 若當前節點(即:父節點)的值大於子節點的值時,直接退出。 if (temp > array[child]) { break; } else { array[parentIndex] = array[child]; // 將子節點的值賦值給父節點 // 選取子節點的左子節點繼續向下調整(執行wile循環) parentIndex = child; child = leftChildIndex(parentIndex); } } // 若發生了交換,則parentIndex表示子節點的索引;若沒有發生交換,則parentIndex仍舊錶示父節點的索引。 array[parentIndex] = temp; } public static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void main(String[] args) { int[] array = {2, 13, 5, 7, 14, 6, 10, 8, 11, 4, 3, 9, 1, 12, 0}; HeapSort heapSort = new HeapSort(array); heapSort.initHeap(); heapSort.sortHeap(); System.out.println("排序後數組" + Arrays.toString(heapSort.array) + " 調整次數" + adjustTime + " while循環次數" + whileTime); }
}