时间复杂度再学习
1.认识时间复杂度
常数时间的操作:一个操作如果和数据量没有关系,每次都是 固定时间内完成的操作,叫做常数操作。
时间复杂度为一个算法流程中,常数操作数量的指标。常用O (读作big O)来表示。具体来说,在常数操作数量的表达式中, 只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分 如果记为f(N),那么时间复杂度为O(f(N))。
评价一个算法流程的好坏,先看时间复杂度的指标,然后再分 析不同数据样本下的实际运行时间,也就是常数项时间
时间复杂度还和数据规模有关,如果有两个算法的,看起优劣,还需要考虑样本量。
选择排序和冒泡排序时间复杂度与数据规模无关,因为数据流程已经确定,插入排序与数据规模有关,最好o(N),全部是有顺序的。
2.对数器的概念和使用
0,有一个你想要测的方法a。
1,实现一个绝对正确但是复杂度不好的方法b。
2,实现一个随机样本产生器 。
3,实现比对的方法 。
4,把方法a和方法b比对很多次来验证方法a是否正确。
5,如果有一个样本使得比对出错,打印样本分析是哪个方法出错。
6,当样本数量很多时比对测试依然正确,可以确定方法a已经 正确。
package basic_class_01;
import java.util.Arrays;
public class Code_00_BubbleSort{
public static void bubblesort(int []arr) {
if(arr==null||arr.length<2) {
return;
}
for(int e=arr.length-1;e>0;e--) {
for(int i=0;i<e;i++) {
if(arr[i]>arr[i+1]) {
swap(arr,i,i+1);
}
}
}
}
public static void swap(int []arr,int i,int j) {
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
//unsigned int a=60; //0011 1100
// unsigned int b=13; //0000 1101
// a=a^b; //a=a^b=0011 0001
// b=a^b; //b=a^b=0011 1100 相当于b1=(a^b)^b
// a=a^b; //a=a^b=0000 1101 相当于a1=(a^b)^((a^b)^b
}
//for test
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
//for test
public static int[] generateRandomArray(int maxSize,int maxValue) {
//Math.random()->double[0,1);
//(int)((size+1)*Math.random()) —>[0,size]整数
//size=6;size+1=7;
//Math.random()->[0,1)*7->[0,7)double
//double->int[0,6]->int
//生成长度随机的随机数组
int [] arr=new int[(int)((maxSize+1)*Math.random())];
for(int i=0;i<arr.length;i++) {
arr[i]=(int)((maxValue+1)*Math.random())-(int)(maxValue*Math.random());
}
return arr;
}
//for test
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;
}
//for test
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;
}//for test
public static void printArray(int [] arr) {
if(arr==null) {
return;
}
for(int i=0;i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
//for test
public static void main(String[] args) {
int testTime=500000;
int maxSize=10;
int maxValue=100;
boolean succeed=true;
for(int i=0;i<testTime;i++) {
int [] arr1=generateRandomArray(maxSize,maxValue);
int[] arr2=copyArray(arr1);
bubblesort(arr1);
comparator(arr2);
if(!isEqual(arr1,arr2)) {
succeed=false;
break;
}
}
System.out.println(succeed?"nice":"Fucking");
int[] arr=generateRandomArray(maxSize,maxValue);
printArray(arr);
bubblesort(arr);
printArray(arr);
}
}
3.剖析递归行为和递归行为时间复杂度的估算
一个递归行为的例子
递归函数就是系统在帮你压栈,调用子过程的时候在压栈,之后子过程返回值,进行弹栈,还原现场,任何递归行为都可以改为非递归行为。
master公式的使用
T(N) = a*T(N/b) + O(N^d)(划分的子问题的规模每一次是一样的)
例如,T(n)=T(n/5)+T(2/3n)+o(n^2)不能用master公式
T(N)样本量为N的时间复杂度,a表示子过程发生的次数,N/b表示子过程的样本量。
-
log(b,a) > d -> 复杂度为O(N^log(b,a))
-
log(b,a) = d -> 复杂度为O(N^d * logN)
-
log(b,a) < d -> 复杂度为O(N^d)
补充阅读:www.gocalf.com/blog/algorithm-complexity-and-mastertheorem.html
4.归并排序
T(n)=2T(n/2)+o(n)->o(n*logn)
package basic_class_01;
import java.util.Arrays;
public class Code_05_MergeSort {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
mergeSort(arr, 0, arr.length - 1);
}
public static void mergeSort(int[] arr, int l, int r) {
if (l == r) {
return;
}
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
public static void merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = m + 1;
while (p1 <= m && p2 <= r) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
}
// for test
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
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;
}
// for test
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;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// for test
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
mergeSort(arr1);
comparator(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
printArray(arr1);
printArray(arr2);
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
mergeSort(arr);
printArray(arr);
}
}
5.小和问题和逆序对问题
小和问题和逆序对问题
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组 的小和。
例子: [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
逆序对问题
在一个数组 中,左边的数如果比右边的数大,则折两个数构成一个逆序对,请打印所有逆序 对。
package basic_class_01;
import java.util.Arrays;
public class Code_01_InsertionSort {
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);
}
}
}
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
// for test
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
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;
}
// for test
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;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// for test
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
insertionSort(arr1);
comparator(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
insertionSort(arr);
printArray(arr);
}
}