赞
踩
笔者在撰写的时候为了方便最先写的是英文版,因此个人认为英文版本相较于中文版本更为完整通畅,有时间的同学可以移步英文版去看看。不过中文版也是很用心重写了的!
名称 | 最佳时间复杂度 | 最差时间复杂度 | 平均时间复杂度 | 空间复杂度 | 是否稳定 |
---|---|---|---|---|---|
归并排序 Merge Sort | O(n*log(n)) | O(n*log(n)) | O(n*log(n)) | O(n) | 是 |
冒泡排序 Bubble Sort | O(n) | O(n**2) | O(n**2) | O(1) | 是 |
插入排序 Insertion Sort | O(n) | O(n**2) | O(n**2) | O(1) | 是 |
选择排序 Selection Sort | O(n**2) | O(n**2) | O(n**2) | O(1) | 否 |
希尔排序 Shell Sort | O(n) | O(n**1.3) | O(n**2) | O(1) | 否 |
堆排序 Heap Sort | O(n*log(n)) | O(n*log(n)) | O(n*log(n)) | O(1) | 否 |
快速排序 Quick Sort | O(n*log(n)) | O(n*log(n)) | O(n**2) | O(log(n))~O(n) | 否 |
一种分而治之的算法。它递归地将数组分成两半,对每一小半进行排序,然后合并排序后的两半。
假设你有一堆编号的扑克牌,你想把它们按从小到大的顺序排列。但是,你一次只能比较两张牌。所以,你要做的是:
1.分而治之: 将大牌堆划分为两个小牌堆,再分别将每个小牌堆分为两个更小的牌堆,以此类推,直到你划出来的所有小牌堆只包含一张牌。
比如你手上有卡片: 5, 3, 7, 2, 8, 4 .
分割 到这样停下: (5, 3), (7, 2), (8, 4).
2.对每一堆进行排序:上一步已经拆整为一,接下来我们要合一为整。
先将最小的牌堆排好序。
选出小牌堆: (3, 5), (2, 7), (4, 8).
3.将它们组合起来:从有序的2×1数列合成有序的4×1数列,再将有序的4×1数列组合合成有序的8×1数列…以此类推,直至数列所有的数都被囊括进最后合成的大数列中。
单位组合:(2, 3, 5, 7), (4, 8).
重复直到完全组合:(2, 3, 4, 5, 7, 8).
public class MergeSort { public static void main(String[] args) { int[] array = {12, 11, 13, 5, 6, 7}; System.out.println("Original array:"); printArray(array); mergeSort(array); System.out.println("\nSorted array:"); printArray(array); } // 归并排序 public static void mergeSort(int[] array) { int n = array.length; if (n > 1) { int mid = n / 2; int[] leftArray = new int[mid]; int[] rightArray = new int[n - mid]; // Copy data to temporary arrays leftArray[] and rightArray[] System.arraycopy(array, 0, leftArray, 0, mid); System.arraycopy(array, mid, rightArray, 0, n - mid); // Recursively sort the two halves mergeSort(leftArray); mergeSort(rightArray); // Merge the sorted halves merge(array, leftArray, rightArray); } } // 合并两个子数列 public static void merge(int[] array, int[] leftArray, int[] rightArray) { int i = 0, j = 0, k = 0; // Merge elements back into the original array in sorted order while (i < leftArray.length && j < rightArray.length) { if (leftArray[i] <= rightArray[j]) { array[k] = leftArray[i]; i++; } else { array[k] = rightArray[j]; j++; } k++; } // 复制leftArray[]剩下的元素 while (i < leftArray.length) { array[k] = leftArray[i]; i++; k++; } // 复制rightArray[]剩下的元素 while (j < rightArray.length) { array[k] = rightArray[j]; j++; k++; } } // 打印数列 public static void printArray(int[] array) { for (int value : array) { System.out.print(value + " "); } System.out.println(); } }
ps : gif转载自https://www.runoob.com/w3cnote/merge-sort.html.
如侵权,请联系我删除。
(总测试次数=5)
通过观察图,我们可以粗略地得知:当数据量很小时,归并排序大体上呈线性增加的趋势。但当数据规模超过某个阈值时,时间反而会减少。因此,有庞大的数据集时,归并排序是一个不错的选择。
优势
缺点
代码实现
由于归并排序算法的递归性质,实现它的代码往往较长,通常在50-70行左右。
重复遍历列表,比较相邻的元素,如果他们的顺序错误就把他们交换过来。该算法之所以得名,是因为较小的元素会“冒泡”到列表的顶部。
想象一下,你想把一队孩子按身高从矮到高排成一行。
于是,从排头开始,你将比较每个孩子和他旁边的孩子的身高。如果一个较矮的孩子站在较高的孩子右侧,交换他们的位置。一直这样做,直到排尾。
在第一次遍历后,最高的孩子一定在排尾。
同理,第二次遍历后,第二高的孩子一定在排尾左侧。
继续这个过程,在数次循环后个子最矮的孩子也“冒泡”到了正确的位置,排序终止。
public class BubbleSort { public static void main(String[] args) { int[] array = {64, 34, 25, 12, 22, 11, 90}; System.out.println("Original array:"); printArray(array); bubbleSort(array); System.out.println("\nSorted array:"); printArray(array); } public static void bubbleSort(int[] array) { int n = array.length; for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (array[j] > array[j + 1]) { // Swap if the element found is greater than the next element int temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; } } } } public static void printArray(int[] array) { for (int value : array) { System.out.print(value + " "); } System.out.println(); } }
优势
缺点
代码实现 20-30行就可以,短小精悍。
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
public class InsertionSort { public static void main(String[] args) { int[] array = {12, 11, 13, 5, 6}; System.out.println("Original array:"); printArray(array); insertionSort(array); System.out.println("\nSorted array:"); printArray(array); } // Function to perform insertion sort static void insertionSort(int[] array) { int n = array.length; for (int i = 1; i < n; ++i) { int key = array[i]; int j = i - 1; // Move elements of array[0..i-1] that are greater than key to one position ahead of their current position while (j >= 0 && array[j] > key) { array[j + 1] = array[j]; j = j - 1; } array[j + 1] = key; } } // Utility function to print an array static void printArray(int[] array) { int n = array.length; for (int i = 0; i < n; ++i) { System.out.print(array[i] + " "); } System.out.println(); } }
优势
实现简单 插入排序很容易实现,对于小数据集或部分排序的数据集来说是一个很好的选择。
适用小数据集 对于小数据集或几乎排好序的数据集,插入排序可能比一些更复杂的算法更有效。
就地排序 它只需要固定数量的额外内存空间。
在线算法 它可以一边接收数据一边排序列表,即数据是实时的。
缺点
大数据集的低效率 插入排序的时间复杂度为O(n^2),其中n是数组中元素的数量。与合并排序或快速排序等更高级的算法相比,这使得它在处理大型数据集时效率低下,这些算法具有更好的平均和最坏情况时间复杂度。
对初始顺序敏感 插入排序的效率高度依赖于元素的初始顺序。如果数组大部分是有序的,则插入排序执行得很好,但对于完全未排序的数据,它可能效率很低。
不具有适应性 插入排序不能很好地适应输入数据的变化。如果输入数据发生变化,算法可能无法利用现有的顺序,而必须从头开始。
不稳定
代码实现 相对简短和简洁。它由大约30行代码组成,包括注释和空格。该算法的简单性有助于实现的简洁性。
一个简单的排序算法,通过将输入数组划分为已排序和未排序的区域来工作。该算法反复从未排序区域中找到最小(或最大,取决于排序顺序)元素,并将其与未排序区域的第一个元素交换。这个过程一直重复,直到整个数组排序完毕。
1.划分数组 : 将数组划分为已排序和未排序两个区域。最初,排序区域为空,整个数组位于未排序区域。
2.查找最小元素 :遍历未排序区域以查找最小元素。
3.与未排序区域的第一个元素交换 :将找到的最小元素与未排序区域的第一个元素交换。这有效地将最小元素添加到排序区域的末尾。
4.扩展已排序区域 :扩展已排序区域以包含新添加的最小元素。
5.重复 : 重复步骤2-4,直到整个数组被排序。未排序区域缩小,排序区域扩大,直到包含整个数组。
public class SelectionSort { public static void main(String[] args) { int[] array = {64, 25, 12, 22, 11}; System.out.println("Original array:"); printArray(array); selectionSort(array); System.out.println("\nSorted array:"); printArray(array); } // Function to perform selection sort static void selectionSort(int[] array) { int n = array.length; // One by one move the boundary of the unsorted subarray for (int i = 0; i < n - 1; i++) { // Find the minimum element in the unsorted array int minIndex = i; for (int j = i + 1; j < n; j++) { if (array[j] < array[minIndex]) { minIndex = j; } } // Swap the found minimum element with the first element int temp = array[minIndex]; array[minIndex] = array[i]; array[i] = temp; } } // Utility function to print an array static void printArray(int[] array) { int n = array.length; for (int i = 0; i < n; ++i) { System.out.print(array[i] + " "); } System.out.println(); } }
优势
缺点
代码实现 大约20-30行。
插入排序的一种优化方法。它首先对彼此相距很远的元素对进行排序,然后逐步缩小要比较的元素之间的间隔。最后一次迭代使用1的间隔,本质上是执行插入排序。
1.从大步长开始
譬如,你有:5,2,9,1,5,6
选定步长 = 2 :5,2,9,1,5,6
经过排序后数组为:5,2,5,1,9,6
2.缩小步长
将步长缩小为1:5,2,5,1,9,6
经过排序后数组为:2,1,5,6,9,5
3.更小的步长
将步长缩小到1:2,1,5,6,9,5
经过排序后数组为: 1,2,5,5,6,9 。排序完毕。
public class ShellSort { public static void main(String[] args) { int[] array = {64, 34, 25, 12, 22, 11, 90}; System.out.println("Original array:"); printArray(array); shellSort(array); System.out.println("\nSorted array:"); printArray(array); } public static void shellSort(int[] array) { int n = array.length; // Start with a big gap, then reduce the gap for (int gap = n / 2; gap > 0; gap /= 2) { // Do a gapped insertion sort for this gap size for (int i = gap; i < n; i++) { int temp = array[i]; int j; for (j = i; j >= gap && array[j - gap] > temp; j -= gap) { array[j] = array[j - gap]; } array[j] = temp; } } } public static void printArray(int[] array) { for (int value : array) { System.out.print(value + " "); } System.out.println(); } }
优势
缺点
代码实现 通常在40-50行左右。
利用堆这种数据结构所设计的一种排序算法-使用二进制堆数据结构来构建堆,然后反复从中提取最大元素。
若你现在有数组:[4, 10, 3, 5, 1].则它的堆排序将是:
4
/ \
10 3
/ \
5 1
1.初始化循环
(for (int i = n / 2 - 1; i >= 0; i–)):
n 是数组中元素的个数,在本例中是5。
i 从n / 2 - 1开始, 在本例中是2. 所以循环从2开始(数组的中间).
循环将向后遍历数组,一直运行到i >= 0为止。
2.计算索引
(int left = 2 * i + 1; and int right = 2 * i + 2;):
对每个 i, 计算它的左和右子节点的索引。
譬如 i = 2, then 则left = 2 * 2 + 1 = 5 ,right = 2 * 2 + 2 = 6.
在此过程中,算法调整子树中的元素以满足堆属性,当循环结束时,将整个数组转换为堆。然后可以使用这个堆来有效地提取最大元素(堆的根)并继续排序过程。
public class HeapSort { public static void main(String[] args) { int[] array = {12, 11, 13, 5, 6, 7}; System.out.println("Original array:"); printArray(array); heapSort(array); System.out.println("\nSorted array:"); printArray(array); } public static void heapSort(int[] array) { int n = array.length; // Build max heap for (int i = n / 2 - 1; i >= 0; i--) { heapify(array, n, i); } // Extract elements from the heap one by one for (int i = n - 1; i > 0; i--) { // Swap the root (maximum element) with the last element int temp = array[0]; array[0] = array[i]; array[i] = temp; // Heapify the reduced heap heapify(array, i, 0); } } public static void heapify(int[] array, int n, int i) { int largest = i; int left = 2 * i + 1; int right = 2 * i + 2; // If left child is larger than root if (left < n && array[left] > array[largest]) { largest = left; } // If right child is larger than largest so far if (right < n && array[right] > array[largest]) { largest = right; } // If largest is not the root if (largest != i) { int swap = array[i]; array[i] = array[largest]; array[largest] = swap; // Recursively heapify the affected sub-tree heapify(array, n, largest); } } public static void printArray(int[] array) { for (int value : array) { System.out.print(value + " "); } System.out.println(); } }
优势
缺点
代码实现 50-60行
一种分而治之的算法. 它的工作原理是从数组中选择一个“支点”元素,并根据它们是小于还是大于支点,将其他元素划分为两个子数组。然后将该过程递归地应用于子数组。
想象一下,你有一堆动物玩具,每个上面都有一个数字。你要把它们按顺序排列。
1.选择一个领导者 首先,你选择一个动物作为领导者。假设它是一头狮子。
2.分组 你把所有数字较低的动物放在狮子的一边,数字较高的动物放在另一边。
3.狮子在正确的位置 现在,狮子在正确的位置,因为所有在它左边的动物数量较少,而在它右边的动物数量较多。
4.每边重复 现在,你对左边和右边的动物做同样的事情。你为每组挑选一个领导,根据他们的人数进行分组,并确保每个领导都在正确的位置。
5.重复此过程 一直这样做,直到每个动物都是自己群体的领导者,每个群体的动物都有正确的顺序。
6.把它们放在一起 最后,你把所有的动物放在一起,然后根据它们的数量从最小到最大进行排序。
public class QuickSort { public static void main(String[] args) { int[] array = {12, 11, 13, 5, 6, 7}; System.out.println("Original array:"); printArray(array); quickSort(array, 0, array.length - 1); System.out.println("\nSorted array:"); printArray(array); } public static void quickSort(int[] array, int low, int high) { if (low < high) { // Partition the array, elements <= pivot on the left, elements > pivot on the right int pivotIndex = partition(array, low, high); // Recursively sort the sub-arrays quickSort(array, low, pivotIndex - 1); quickSort(array, pivotIndex + 1, high); } } public static int partition(int[] array, int low, int high) { // Choose the rightmost element as the pivot int pivot = array[high]; // Index of the smaller element int i = low - 1; for (int j = low; j < high; j++) { // If current element is smaller than or equal to the pivot if (array[j] <= pivot) { i++; // Swap array[i] and array[j] int temp = array[i]; array[i] = array[j]; array[j] = temp; } } // Swap array[i + 1] and array[high] (pivot) int temp = array[i + 1]; array[i + 1] = array[high]; array[high] = temp; return i + 1; } public static void printArray(int[] array) { for (int value : array) { System.out.print(value + " "); } System.out.println(); } }
优势
缺点
代码实现 40-50 行.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。