一. 引言
算法可能在实际开发中用不到,但是算法的思想依然是很重要的,毕竟"程序=算法+数据结构"这句话不是拍着脑袋讲出的.路慢慢其修远兮.
二. 各种排序算法
- 1. 直接插入排序
**直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。参考这个
实现的注意点: 数组为a[0…n-1]。
- 初始时,a[0]自成1个有序区,无序区为a[1..n-1]。令i=1
- 将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。
插入的过程是从a[0,i-1]为a[i]找到一个合适的位置j,找到后把j~i-1之间的数字往后移动一个,再把a[i]插入到j+1的位置; - i++并重复第二步直到i==n-1。排序完成。
核心点:
public static void sortInsert(Comparable[] a){
int N=a.length;
for(int i=1;i0&&less(a[j],a[j-1]);j--){
swap(a,j,j-1);
}
}
}
private static boolean less(Comparable v,Comparable w) {
return v.compareTo(w)<0;
}
- 2. 冒泡排序
冒泡排序:是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,这样最大的数就到最尾了。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
for(i=0;idata[j+1])
swap();
}
}
- 3. 选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
(最开始把i=0当最小的元素a[min],再从i+1~n之间找出最小的元素,再和a[min]交换,依次类推;)
for (int i = 0; i < data.length - 1; i++) {
int min = i;// 将当前下标定为最小值下标
for (int j = i + 1; j < data.length; j++) {
if (data[j] < data[min]) {
min = j;
}
}
if (i != min) {
int tmp = data[i];
data[i] = data[min];
data[min] = tmp;
}
}
选择排序和冒泡相比:比较次数还是O(n2), 但是交换次数变成了O(n), 所以选择排序无疑会更快;
- 4. 归并算法排序
两路归并排序算法思路(参考这个):
- 把 n 个记录看成 n 个长度为1的有序子表;
- 进行两两归并使记录关键字有序,得到 n/2 个长度为 2 的有序子表;
- 重复第②步直到所有记录归并成一个长度为 n 的有序表为止。
java中的Arrays.sort(Object[] o)是对数组进行排序,它使用的是归并排序的方式;
快速排序要比归并排序更快一些,但为什么使用归并排序了?
原因是归并排序是一种稳定的排序方式,即归并排序不交换相同的元素,这就意味着,在按一种方式排序后同时可以按另外一种方式进行排序。比如员工可以首先按工资排序,然后按名字排序,一种排序不会打乱另一种排序的顺序。
- 5. 希尔排序
(目的是克服插入排序复制的次数太多的缺点)
该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
步长的选择也是需要考虑的, 常见的有h=(h-1)/3, h初始为总的个数
public class ShellSort {
public static void shellSort(int[] data) {
int j = 0;
int temp = 0;
for (int increment = (data.length -1)/ 3; increment > 0; increment=(increment-1) / 3) {
for (int i = increment; i < data.length; i++) {
temp = data[i];
for (j = i; j >= increment; j -= increment) {
if(temp > data[j - increment]){
data[j] = data[j - increment];
}else{
break;
}
}
data[j] = temp;
}
}
}
- 6. 快速排序
快速排序采用了分治和递归的思想。
它的基本思想是:
选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
最坏情况的时间复杂度为O(n2),最好情况时间复杂度为O(nlog2n)。
假设要排序的数组是A[1]……A[N],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一躺快速排序。一趟快速排序的算法是:
- 设置两个变量I、J,排序开始的时候I:=1,J:=N;
- 以第一个数组元素作为关键数据,赋值给X,即X:=A[1];
- 从J开始向前搜索,即由后开始向前搜索(J:=J-1),找到第一个小于X的值,两者交换;
- 从I开始向后搜索,即由前开始向后搜索(I:=I+1),找到第一个大于X的值,两者交换;
- 重复第3、4步,直到I=J;
例如:待排序的数组A的值分别是:(初始关键数据X:=49)
A[1] A[2] A[3] A[4] A[5] A[6] A[7]:
49 38 65 97 76 13 27
进行第一次交换后: 27 38 65 97 76 13 49
( 按照算法的第三步从后面开始找)
进行第二次交换后: 27 38 49 97 76 13 65
( 按照算法的第四步从前面开始找>X的值,65>49,两者交换,此时I:=3 )
进行第三次交换后: 27 38 13 97 76 49 65
( 按照算法的第五步将又一次执行算法的第三步从后开始找)
进行第四次交换后: 27 38 13 49 76 97 65
( 按照算法的第四步从前面开始找大于X的值,97>49,两者交换,此时J:=4 )
此时再执行第三步的时候就发现I=J,从而结束一躺快速排序,那么经过一躺快速排序之后的结果是:27 38 13 49 76 97 65
,
即所以大于49的数全部在49的后面,所以小于49的数全部在49的前面。
移动数据部分的代码
public int getMiddle(Integer[] list, int low, int high) {
int tmp = list[low]; //数组的第一个作为中轴
while (low < high) {
while (low < high && list[high] > tmp) {
high--;
}
list[low] = list[high]; //比中轴小的记录移到低端
while (low < high && list[low] < tmp) {
low++;
}
list[high] = list[low]; //比中轴大的记录移到高端
}
list[low] = tmp; //中轴记录到尾
return low; //返回中轴的位置
}
- 7. 堆排序(优先队列)
堆排序(参考这个)是一种利用完全二叉树来解决问题的高效算法,合法的最大堆树要满足一个条件就是每一个结点值都要大于或等于它的孩子结点值。
堆排序树的构造过程找最大值过程:数组arrays[0....n],找到最大值后把最大值即94放在数组的最后面arrays[n],然后进入递归把arrays[0...n-1]再进入这个过程,只是把排好序的最大值不放入到这个过程中,就这样把值一个个的冒出来。
它的复杂度最差和平均都是(nlogn)
三. 总结
最后, 简单的做一个基本排序算法总结: