第一节
冒泡排序
public static void bubbleSort(int[] arr){
if(arr == null || arr.length < 2)
return;
for(int end = arr.length-1; end > 0; end--){
for(int i = 0; i < end; i++)
if(arr[i] > arr[i+1])
swap(arr,i,i+1);
}
}
选择排序
public static void selectionSort(int[] arr){
if(arr == null || arr.length < 2)
return;
for(int i = 0; i < arr.length-1; i++){
int minIndex = i;
for(int j = i+1; j < arr.length; j++)
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
swap(arr, i, minIndex);
}
}
插入排序
public static void insertionSort(int[] arr){
if(arr == null || arr.length < 2)
return;
for(int i = 1; i < arr.length; i++)
for(int j = i - 1;j >= 0 && arr[j] > arr[j + 1]; j--)//一次次往前交换
swap(arr, j, j + 1);
}
对数器
- 随机产生器:产生随机数组
- 准备一个绝对正确的方法:不需考虑时间复杂度,保证绝对正确即可
- 实现比对的方法
- 把方法a和方法b比对很多次来验证方法a是否正确
- 如果有一个样本是的比对出错,打印样本分析是哪个方法出错
- 当样本数量很多时比对测试依然正确,可以确定方法a已经正确
以冒泡排序为例
-
随机发生器
public static int[] generateRandomArray(int size, int value){ int[] arr = new int[(int) ((size + 1) * Math.random())]; for (int i = 0; i < arr.length; i++) arr[i] = (int)((value + 1) * Math.random()) - (int)(value * Math.random());//只要是随机数就行 return arr; }
-
准备一个绝对正确的方法
public static void rightMathod(int[] arr){ Arrays.sort(arr); }
-
实现比对的方法
//判断两个数组相等 public static boolean isEqual(int[] arr1, int[] arr2){ if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) return false; if (arr1 == null && arr2 ==null) return true; if (arr1.length != arr2.length) return false; for (int i = 0; i < arr1.length; i++) { if(arr1[i] != arr2[i]) return false; } return true; }
-
剩下放在main中的步骤
//Main public static void main(String[] args) { //自己设置 int testTime = 500000; int size = 10; int value = 100; boolean succeed = true; for (int i = 0; i < testTime; i++){ int[] arr1 = generateRandomArray(size,value);//生成器 int[] arr2 = copyArray(arr1);//拷贝arr1 int[] arr3 = copyArray(arr1); bubbleSort(arr1); rightMathod(arr2); if(!isEqual(arr1,arr2)){ succeed = false; printArrays(arr3);//将错误的样本打印出来 break; } } System.out.println(succeed ? "Nice!" : "Error!"); } //复制数组 public static int[] copyArray(int[] arr){ if (arr == null) return null; int[] res = new int[arr.length]; for (int i = 0; i< arr.length; i++) res[i] = arr[i]; return res; } //打印样本 public static int[] printArrays(int[] arr){ ... }
剖析递归行为和递归行为时间复杂度的估算
递归求数组最大值
public static int getMax(int[] arr, int l,int r){
if(r == l){
return arr[l];
}
int mid = (l + r)/2;
int maxLeft = getMax(arr, l, mid);
int maxRight = getMax(arr, mid+1, r);
return Math.max(maxLeft, maxRight);
}
递归的本质是系统对函数进行了栈操作。
master公式
T(N) = a*T(N/b) + O(N^d)
1) log(b,a) > d -> 复杂度为O(N^log(b,a))
2) log(b,a) = d -> 复杂度为O(N^d * logN)
3) log(b,a) < d -> 复杂度为O(N^d)
只要子问题的规模是一致的,就可以用master公式
归并排序
先左边排好序,然后右边排好序,最后总体排好序(分治的思想)
T(N)=2T(N/2)+O(N)
所以时间复杂度:O(N*logN)
//归并排序
public static void MergeSort(int[] arr) {
if(arr == null || arr.length < 2)
return;
sortProcess(arr, 0, arr.length-1);
}
public static void sortProcess(int[] arr, int L, int R){
if(L == R)
return;
int mid = (L+R)/2;
sortProcess(arr, L, mid);
sortProcess(arr, mid+1, R);
Merge(arr, L, mid, R);
}
public static void Merge(int[] arr, int L, int mid, int R){
int[] help = new int[R - L + 1];
int i = 0;
int p1 = L;
int p2 = mid+1;
//比较坐标p1和p2的值,小的复制进help数组
while (p1 <= mid && p2 <= R)
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
//两个while只会执行一个
while(p1 <= mid)
help[i++] = arr[p1++];
while (p2 <= R){
help[i++] = arr[p2++];
}
for(i = 0; i < help.length; i++){
arr[L + i] = help[i];
}
}
分治法应用:二分查找
public static boolean Bfind(int[] arr, int a, int L, int R){
if(arr == null || L > R)
return false;
int mid = (L + R)/2;
if(a == arr[mid])
return true;
if(a < arr[mid])
return Bfind(arr, a, L, mid+1);
if(a > arr[mid])
return Bfind(arr, a, mid+1, R);
return false;
}
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组
的小和。
例子:
[1,3,4,2,5]
1左边比1小的数,没有;
3左边比3小的数,1;
4左边比4小的数,1、3;
2左边比2小的数,1;
5左边比5小的数,1、3、4、2;
所以小和为1+1+3+1+1+3+4+2=16
最容易的方法:O(N^2),对数器可用此方法
法二:归并排序