第二章 算法基础
本章首先讲了插入排序算法, 然后分析了插入排序算法的时间复杂性,引出了分治法的设计思想,分析了分治算法。
1. 插入排序
插入排序采用了增量算法:在排序子数组A[1...j-1]后,将单个元素A[j]插入到子数组的适当位置,产生排序好的子数组A[1....j]。
1 package sort; 2 3 import util.RandomArray; 4 5 public class Insert { 6 public static void insertSort(int[] array) { 7 // 插入排序 8 for (int i = 1; i < array.length; i++) { 9 int key = array[i]; 10 // Insert array[i] into the sorted sequence array[0....i-1] 11 int j = i - 1; 12 while (j >= 0 && array[j] > key) { 13 array[j + 1] = array[j]; 14 j--; 15 } 16 array[j + 1] = key; 17 } 18 } 19 20 public static void main(String arg[]) { 21 RandomArray randomArray = new RandomArray(10); 22 23 // 排序前,先把内容打印出来 24 randomArray.display(); 25 26 insertSort(randomArray.value); 27 28 // 排序后,先把内容打印出来 29 randomArray.display(); 30 31 } 32 }
事先定义了一个随机数组的类,方便接下来的排序算法。
1 package util; 2 3 4 public class RandomArray { 5 public int[] value; 6 7 public RandomArray(int size) { 8 value = new int[size]; 9 for (int i = 0; i < size; i++) { 10 value[i] = (int)(Math.random()*100) - 50; 11 } 12 } 13 14 public void display() { 15 for (int i = 0; i < value.length; i++) { 16 System.out.print(value[i] + " "); 17 } 18 System.out.println(); 19 } 20 }
插入排序算法的最好情况是O(n),最坏的情况是O(n^2)。
2. 归并排序
许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或者多次的递归调用自身一解决紧密相关的若干子问题。这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。
1 package sort; 2 3 import util.RandomArray; 4 5 public class Merge { 6 7 public static void mergeSort(int[] array) { 8 sort(array, 0, array.length - 1); 9 } 10 11 public static void sort(int[] array, int left, int right) { 12 if (left < right) { 13 int mid = (left + right) / 2; 14 sort(array, left, mid); 15 sort(array, mid + 1, right); 16 merge(array, left, mid, right); 17 } 18 } 19 20 public static void merge(int[] array, int left, int mid, int right) { 21 int n1 = mid - left + 1; 22 int[] L = new int[n1 + 1]; 23 24 int n2 = right - mid; 25 int[] R = new int[n2 + 1]; 26 27 for (int i = 0; i < n1; i++) { 28 L[i] = array[left + i]; 29 } 30 L[n1] = Integer.MAX_VALUE; 31 32 for (int j = 0; j < n2; j++) { 33 R[j] = array[mid + j + 1]; 34 } 35 R[n2] = Integer.MAX_VALUE; 36 37 int i = 0; 38 int j = 0; 39 for (int k = left; k <= right; k++) { 40 if (L[i] <= R[j]) { 41 array[k] = L[i]; 42 i++; 43 } else { 44 array[k] = R[j]; 45 j++; 46 } 47 } 48 } 49 50 public static void main(String[] args) { 51 // TODO Auto-generated method stub 52 RandomArray randomArray = new RandomArray(1000000); 53 54 // 排序前,先把内容打印出来 55 // randomArray.display(); 56 57 mergeSort(randomArray.value); 58 59 // 排序后,先把内容打印出来 60 // randomArray.display(); 61 System.out.println("ends"); 62 } 63 64 }
上面的merge方法使用了哨兵的方式,也可以不适用哨兵,代码如下:
1 //不采用哨兵 2 public static void merge(int[] array, int left, int mid, int right) { 3 int n1 = mid - left + 1; 4 int[] L = new int[n1]; 5 6 int n2 = right - mid; 7 int[] R = new int[n2]; 8 9 for (int i = 0; i < n1; i++) { 10 L[i] = array[left + i]; 11 } 12 13 for (int j = 0; j < n2; j++) { 14 R[j] = array[mid + j + 1]; 15 } 16 17 int i = 0; 18 int j = 0; 19 for (int k = left; k <= right; k++) { 20 if(i == n1) { 21 System.arraycopy(R, j, array, k, right-k+1); 22 break; 23 } 24 25 if(j == n2) { 26 System.arraycopy(L, i, array, k, right-k+1); 27 break; 28 } 29 30 if (L[i] <= R[j]) { 31 array[k] = L[i]; 32 i++; 33 } else { 34 array[k] = R[j]; 35 j++; 36 } 37 } 38 }
归并排序的算法效率为O(n*lgn),但在合并中分配了左右两个子数组,牺牲了一定的空间。