三种常用的排序算法
三种常用的排序算法
三种常用的排序算法
引言
笔者 是一名在校大学生,在学校常用的算法有很多,毕竟算法是程序的灵魂 ,我就整理一点算法笔记放在博客上以供参考。PS: Java版本
冒泡排序
-
平均时间复杂度:O(n^2)
-
稳定性:稳定[1]
-
排序算法的稳定性(腾讯校招2016笔试题曾考过)
冒泡排序是一种极其简单的排序算法,也是我所学的第一个排序算法。它重复地走访过要排序的元素,依此比较相邻两个元素,如果他们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端。
代码如下
import java.util.Arrays; public class BubbleSort { public static void bubbleSort(int[] arr){ //定义临时变量 int p; for(int i =0; i<arr.length-1; i++){ //对n个数排序只要交换n-1次即可 //外层循环,每次把最大的元素像气泡一样“浮”到数组的最后 for(int j =0; j<arr.length-i-1; j++){ //内层循环 if(arr[j]>arr[j+1]){ //依此比较相邻的两个元素,使较大的那个向后移 //三变量交换法 p = arr[j]; arr[j] = arr[j+1]; arr[j+1] = p; } } } } public static void main(String[] args){ int[] arr = new int[] {4,7,6,7,5,3,28,1,8}; bubbleSort(arr); System.out.println(Arrays.toString(arr)); } }
运行结果如下
[1, 3, 4, 5, 6, 7, 7, 8, 28]
选择排序
-
平均时间复杂度:O(n^2)
-
稳定性:不稳定
选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。
代码如下:
import java.util.Arrays; public class SelectionSort { public static void selectionSort(int[] arr){ for (int i = 0; i < arr.length-1; i++) { //i 为已排序序列的末尾 int k = i; for (int j= i+1;j<arr.length;j++) { //未排序序列 if (arr[k]>arr[j]) { //找出未排序序列中的最小值 k = j; } } if (i!=k) { //放到已排序序列的末尾,该操作很有可能把稳定性打乱,所以选择排序是不稳定的排序算法 int temp = arr[k]; arr[k] = arr[i]; arr[i] = temp; } } } public static void main(String[] args) { int[ ] arr = new int[] {4,11,6,7,5,3,28,1,8}; selectionSort(arr); System.out.println(Arrays.toString(arr)); } }
运行结果如下:
[1, 3, 4, 5, 6, 7, 8, 11, 28]
快速排序
-
平均时间复杂度:O(nlogn)
-
稳定性:不稳定
-
快速排序是不稳定的排序算法,不稳定发生在基准元素与A[tail+1]交换的时刻。
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlog n)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlog n) 算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为:
- 从序列中挑出一个元素,作为"基准"(pivot).
- 把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
- 对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。
快速排序的代码如下:
import java.util.Arrays;
public class QuickSort {
//快速排序的递归版本
public static void quickSort(int[] arr, int startIndex,int endIndex) {
// 递归结束条件:startIndex大等于endIndex的时候
if (startIndex >= endIndex) {
return;
}
// 得到基准元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
// 根据基准元素,分成两部分递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
// 取第一个位置的元素作为基准元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while (left != right) {
//控制right指针比较并左移
while (left<right && arr[right] > pivot) {
right--;
}
//控制left指针比较并右移
while (left<right && arr[left] <= pivot) {
left++;
}
//交换left和right指向的元素
if (left<right) {
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}
//pivot和指针重合点交换
int p = arr[left];
arr[left] = arr[startIndex];
arr[startIndex] = p;
return left;
}
public static void main(String[] args) {
int[] arr = new int[] {4,7,6,7,5,3,28,1,8};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
运行结果如下:
[1, 3, 4, 5, 6, 7, 7, 8, 28]
-
快速排序的非递归实现:
绝大多数用递归来实现的问题,都可以用【栈】的方式来代替。
因为我们代码中一层一层的方法调用,本身就是一个函数栈。每次进入一个新方法,就相当于入栈;每次有方法返回,就相当于出栈。
所以,我们可以把原本的递归实现转化成一个栈的实现,在栈当中存储每一次方法调用的参数。
代码如下:
import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Stack; public class QuickSortWithStack{ public static void quickSort(int[] arr, int startIndex, int endIndex){ // 用一个集合栈来代替递归的函数栈 Stack<Map<String, Integer>> quickSortStack = new Stack<Map<String, Integer>>(); // 整个数列的起止下标,以哈希的形式入栈 Map rootParam = new HashMap(); rootParam.put("startIndex", startIndex); rootParam.put("endIndex", endIndex); quickSortStack.push(rootParam); //循环结束条件: 栈为空时结束 while(!quickSortStack.isEmpty()){ //栈顶元素出栈,得到起止下标 Map<String, Integer> param = quickSortStack.pop(); //得到基准元素位置 int pivotIndex = partition(arr, param.get("startIndex"), param.get("endIndex")); //根据基准元素分成两部分,把每一部分的起止下标入栈 if (param.get("startIndex")< pivotIndex -1) { Map<String, Integer> leftParam = new HashMap<String, Integer>(); leftParam.put("startIndex", param.get("startIndex")); leftParam.put("endIndex", pivotIndex -1); quickSortStack.push(leftParam); } if (pivotIndex + 1 < param.get("endIndex")) { Map<String, Integer> rightParam = new HashMap<String, Integer>(); rightParam.put("startIndex", pivotIndex + 1); rightParam.put("endIndex", param.get("endIndex")); quickSortStack.push(rightParam); } } } private static int partition(int[] arr, int startIndex, int endIndex) { // 取第一个位置的元素作为基准元素 int pivot = arr[startIndex]; int left = startIndex; int right = endIndex; while (left != right) { //控制right指针比较并左移 while (left<right && arr[right] > pivot) { right--; } //控制left指针比较并右移 while (left<right && arr[left] <= pivot) { left++; } //交换left和right指向的元素 if (left<right) { int p = arr[left]; arr[left] = arr[right]; arr[right] = p; } } //pivot和指针重合点交换 int p = arr[left]; arr[left] = arr[startIndex]; arr[startIndex] = p; return left; } public static void main(String[] args) { int[] arr = new int[] {4,7,6,5,3,2,8,1}; quickSort(arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); } }
运行结果如下:
[1, 2, 3, 4, 5, 6, 7, 8]
结语:
emmm,常用的排序算法就介绍到这里,如有错漏之处,或者有疑问可联系笔者,愿一起进步。
Email: 15270013718@163.com
-
如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。 ↩︎