1、冒泡排序
1.1 冒泡排序算法的算法过程如下:
①. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
②. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
③. 针对所有的元素重复以上的步骤,除了最后一个。
④. 持续每次对越来越少的元素重复上面的步骤①~③,直到没有任何一对数字需要比较。
1.2 代码实现:
public class BubbleSort {
public static void sort(int[] array){
if (null == array || array.length == 0){
return;
}
int len = array.length;
for (int i = 0; i < len; i++){
for (int j = 0; j < len - 1 - i; j++){
if (array[j] > array[j+1]){
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
}
}
1.3 效率
该算法是稳定的排序算法,同样也是最容易实现的排序,当然最坏的一种场景就是每次都进行交换,一共遍历并交换接近n²/2次,时间复杂度为O(n²)。最理想的场景就是内循环遍历一次发现排序是对的,退出循环,此时的时间复杂度为O(n). 平均来讲, 时间复杂度为O(n²). 由于冒泡排序中只有缓存的tmp变量需要内存空间, 因此空间复杂度为常量O(1)。
平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 |
---|---|---|---|
O(n 2 ) | O(n) | O(n 2 ) | O(1) |
2、快速排序
2.1 算法描述
快速排序使用分治策略来把一个序列分为两个子序列。步骤为:
①. 从数列中挑出一个元素,称为”基准”(pivot)。
②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
2.2 代码实现:
public class QuickSort {
public static void sort(int[] array, int low, int high){
if (null == array || array.length ==0 || low >= high){
return;
}
int left = low;
int right = high;
//基准值
int key = array[low];
while (left < right){
//右->左找到第一个比基准值小的坐标
while (left < right && key <= array[right]){
right--;
}
//左->右找到第一个比基准值大的坐标
while (left < right && key >= array[left]){
left++;
}
//目前排序为: array[right] < key < array[left],要保证右边值要最大,需要交换
if (left < right){
int tem = array[left];
array[left] = array[right];
array[right] = tem;
}
}
//目前排序为: array[left] < array[low] < array[right]
//校准基准值,准备分治递归快排,要保证array[low]是最小值
int tmp = array[left];
array[left] = array[low];
array[low] = tmp;
//对key左边进行快排
sort(array, low, left - 1);
//对key右边进行快排
sort(array, left + 1, high);
}
}
2.3 效率
快速排序并不稳定,快速排序每次交换的元素都有可能不是相邻的, 因此它有可能打破原来值为相同的元素之间的顺序。
平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 |
---|---|---|---|
O(nlogn) | O(nlogn) | O(n 2 ) | O(1) |
3、希尔排序
3.1 算法描述
①. 选择一个增量序列t1,t2,…, tk ,其中ti>tj,tk=1;(一般初次取数组半长,之后每次再减半,直到增量为1)
②. 按增量序列个数k,对序列进行k 趟排序;
③. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
3.2 代码实现
public class ShellSort {
public static void sort(int[] array){
int gap = array.length >> 1;
for (; gap > 0; gap = gap >> 1){
//不断缩小gap,直到1为止
for (int i = 0; (i + gap) < array.length; i++){
//使用当前gap进行组内插入排序
for (int j = 0; (j + gap) < array.length; j += gap){
//交换
if (array[j] > array[j+gap]){
array[j] = array[j] + array[j+gap];
array[j+gap] = array[j] - array[j+gap];
array[j] = array[j] - array[j+gap];
}
}
}
}
}
}
3.3 效率
不稳定排序算法,希尔排序第一个超过O(n 2 )的排序算法;是简单插入排序的改进版;它与插入排序的不同之处在于,它会优先比较距离较远的元素,直接插入排序是稳定的;而希尔排序是不稳定的,希尔排序的时间复杂度和步长的选择有关,常用的是Shell增量排序,也就是N/2的序列,Shell增量序列不是最好的增量序列。
4、直接插入排序
4.1 算法描述
基本思想:将数组中的所有元素依次跟前面已经排好的元素比较,若选择的元素比已经排序的元素小,则进行交换,直到全部元素都比较过为止。具体描述如下:
①. 从第一个元素开始,该元素默认已经排好序。
②. 获取下一个元素,在已经排好序的元素序列中从后往前扫描。
③. 如果该元素(已经排好序)大于新元素,则将该元素移到下一个位置。
④. 重复步骤③,直到找到已经排序的元素小于或者等于新元素的位置。
⑤. 将新元素插入到该位置。
⑥. 重复步骤②—⑤。
4..2 代码实现
public class InsertSort {
/**
* 移位法
* @param arr
*/
public static void sort1(int[] arr){
if (null == arr || arr.length == 0){
return;
}
for (int i = 1; i < arr.length; i++){
int j = i - 1;
//提前取出待插入数据保存
int tmp = arr[i];
//若比待插入的数据大就后移
while (j >= 0 && arr[j] > arr[i]){
arr[j+1] = arr[i];
j--;
}
//找到比待插入数据小的位置,将待插入数据插入
arr[j+1] = tmp;
}
}
/**
* 交换法
* @param arr
*/
public static void sort2(int[] arr){
if (null == arr || arr.length == 0){
return;
}
for (int i = 1; i < arr.length; i++){
int j = i - 1;
while (j >= 0 && arr[j] > arr[i]){
//只要大就交换
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
4.3 算法效率
不稳定的排序算法。
平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 |
---|---|---|---|
O(n 2 ) | O(n) | O(n 2 ) | O(1) |
5、选择排序
5.1 算法描述
在未排序序列中找到最小(大)元素,存放到未排序序列的起始位置。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。可以描述如下:
①. 从待排序序列中,找到关键字最小的元素;
②. 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
③. 从余下的 N – 1 个元素中,找出关键字最小的元素,重复①、②步,直到排序结束。
5.2 代码实现
public class SelectSort {
public static void sort(int[] array){
for (int i = 0; i < array.length - 1; i++){
int min = i;
for (int j = i + 1; j < array.length ; j++){
if (array[j] < array[i]){
min = j;
}
}
if (min != i){
array[min] = array[min] + array[i];
array[i] = array[min] - array[i];
array[min] = array[i];
}
}
}
}
5.3 算法效率
不稳定排序算法,即使原数组已排序完成,它也将花费将近n²/2次遍历来确认一遍。 但是呢,它并不耗费额外的内存空间。
平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 |
---|---|---|---|
O(n 2 ) | O(n 2 ) | O(n 2 ) | O(1) |