1 时间复杂度O
时间复杂度即描述算法增长率的一种方。算法的增长率即随着自变量n的改变算法本身运算量的变化大小。
如O(n)指的是线性变化,即增加
了n,运算量相对应的增加n。如下面的程序
for (int i = 1; i <= n; i ++){
k = k +5;
}
就是一个时间复杂度为O(n)的算法。
相对应还有O(logn)(二分查找法), O(n^2)(选择排序法), O(2^n)(汉诺塔问题),等时间复杂度。其中logn默认以2为底n的对数。
2 插入排序
重复地将新的元素插入一个排好序地子线性表中,直至整个线性表排好序。
书中对于该方法思路介绍具有误导性,它的思路应该是将每次所得元素与之前已放入地元素进行比较,已决定所放入地位置。而排好序的子线性表其实不过是多次比较后的插入,并不是真的每次插入就排一次序。
public class InsertionSort{
public static void insertionSort (int[] list){
for (int i = 1; i < list.length; i++){
int currentElement = list[i];
int k;
for (k = i - 1; k >= 0 && list[k] > currentElement; k--){
list[k+1] = list[k];
}
list[k+1] = currentElement;
}
}
}
时间复杂度为O(n^2)
3 冒泡排序,不介绍. 时间复杂度O(n^2)
4 归并排序(空间复杂度O(nlogn))
归并排序算法将数组分为两半,对每部分递归地应用归并排序。在两部分都排好序后,对他们进行归并。
在理解该算法前,需要先理解递归调用只有在调用函数运行结束后才会运行之后的函数,如下代码,
public class MergeSort {
public static void train(int k) {
if(k < 10) {
k = k + 1;
train(k);
System.out.println(k);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
train(0);
}
}
它在调用后最先打印出的是10,而不是1.
另外两个排序好的数组在进行归并排序后的数组一定也是排好序的。(亲自测试了就知道了)
归并排序,就是不断地递归将数组一分为二,知道最后一个数组只剩两个,并且将这两个比较大小进行排序。然后两个排序后的子数结合合并排序,就是一个更大的排好序的数组。这样最后得到的就是已排好序的所需数组了。
public class MergeSort {
public static void mergeSort(int[] list) {
if(list.length > 1) {
int[] firstHalf = new int[list.length/2];
System.arraycopy(list, 0, firstHalf, 0, list.length/2);
mergeSort(firstHalf);
int[] secondHalf = new int[list.length - list.length / 2];
System.arraycopy(list, list.length / 2, secondHalf,
0, (list.length - list.length / 2));//考虑奇数的情况
mergeSort(secondHalf);
merge(firstHalf,secondHalf,list);
}
}
public static void merge(int[] firstHalf, int[] secondHalf, int[] list) {
int current1 = 0 ;
int current2 = 0;
int current3 = 0;
while(current1 < firstHalf.length && current2 < secondHalf.length) {
if(firstHalf[current1] < secondHalf[current2]) {//这里的小于、大于符号控制了升序降序
list[current3] = firstHalf[current1];
current1 ++;
current3 ++;
}
else {
list[current3] = secondHalf[current2];
current2 ++;
current3 ++;
}
}
while (current1 < firstHalf.length) { //这两个循环就是解决某个数组的个数有剩的情况,
//也可以看出数组是排序好了的,不然这样肯定不行
list[current3] = firstHalf[current1];
current3 ++;
current1 ++;
}
while (current2 < secondHalf.length) {
list[current3] = secondHalf[current2];
current3 ++;
current2 ++;
}
}
public static void main(String[] args) {
int[] sort = {1, -1, 9, 5, 8, 2, 7, 9, 10};
mergeSort(sort);
for(int a : sort) {
System.out.print(a + " ");
}
}
}
5 堆排序
堆排序使用的是二叉堆。它首先将所有的元素添加到一个堆上,然后不断移除最大的元素以获得一个排好序的线性表。很显然堆在插入键和删除根节点时,执行效率很高。
6 桶排序和基数排序
取到每个数的最末位对所有数排序,然后取到所有数的倒数第二位进行排序,一直取到所有数的第一位进行排序,直到排序完成。这种排序方法用途有限,只能对于那些位数比较少的数进行排序。时间复杂度O(dn) 其中的d就是位数。
7 快速排序 时间复杂度O(nlogn)
该算法在数组中不断选择一个称为基准的元素,将数组分为两部分,使得第一部分中的所有元素都小于或等于基准元素,而第二部分中的所有元素都大于基准元素。(一看就知道递归没跑了)。
即先在一个数组中找到比数组第一个数大的值和比这个数小的值,将这个数放在其中,然后对左右两边的两个数组进行如上查找(注 : 如果数组中的第一个数就是最小数,那么就在这个数组的第二个数选为基准数)
public class QuickSort {
public static void quickSort(int[] list, int first, int last) {
if(last > first) {
int pivotIndex = partition(list, first, last);
quickSort(list, first, pivotIndex - 1);
quickSort(list, pivotIndex + 1, last);
}
}
public static int partition(int[] list,int first,int last) {
int pivot = list[first];
int low = first + 1;
int high = last;
while(high > low) {
while (low <= high && list[low] <= pivot) {
low ++;
}
while(low <= high && list[high] > pivot) {
high --;
}
if(high > low) {
int temp = list[high];
list[high] = list[low];
list[low] = temp;
}
}
while(high > first && list[high] >= pivot)
high --; // 这两行代码就是处理基准数恰好是最小数的情况
if(pivot > list[high]) {
list[first] = list[high];
list[high] = pivot;
return high;
}
else {
return first;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] list = {2,8,9,10,-2,-29,18};
quickSort(list, 0, list.length - 1);
for(int a : list) {
System.out.println(a);
}
}
}
运行结果如下
8 外部排序
即在大型外部文件中对数据排序。
由于JVM内存有限,不可能直接将整个文件放入虚拟机中进行内部排序,因此我们必须先将外部文件拆分,分成几个分段,然后再对每个分段排序,最后将每个分段归并起来(听起来好像就是用归并排序。。。只不过这些储存分段用临时文件夹代替了而已,不想写。。懒。。)