跟左神学算法1 算法入门

内容:

1、时间复杂度和额外空间复杂度

2、简单排序

3、对数器使用

4、递归

注:实现代码为Java

1、时间复杂度

什么是常数时间的复杂度:一个操作如果跟数据量没有关系,每次都是固定时间内完成的操作就叫常数操作

关于时间复杂度:

时间复杂度为一个算法流程中常数操作数量的指标(一般是最差情况下),常用O(读作big O)来表示

具体来说在常数操作数量的表达式中,只要高阶项,不要低阶项,也不要高阶项的系数

然后把剩下的部分记为f(N),那么时间复杂度为O(f(N))

时间复杂度是衡量算法好坏的重要指标之一。时间复杂度反映的是不确定性样本量的增长对于算法操作所需时间的影响程度,与算法操作是否涉及到样本量以及涉及了几次直接相关,

如遍历数组时时间复杂度为数组长度n(对应 时间复杂度为 O(n) ),而对数据的元操作(如加减乘除与或非等)、逻辑操作(如if判断)等都属于常数时间内 的操作(对应时间复杂度 O(1) )

在化简某算法时间复杂度表达式时需遵循以下规则:

  • 对于同一样本量,可省去低阶次数项,仅保留高阶次数项,如 O(n^2)+O(n) 可化简为 O(n^2) , O(n)+O(1) 可化简为 O(n)
  • 可省去样本量前的常量系数,如 O(2n) 可化简为 O(n) , O(8) 可化简为 O(1)
  • 对于不同的不确定性样本量,不能按照上述两个规则进行化简,要根据实际样本量的大小分析表达式增量。 如 O(logm)+O(n^2) 不能化简为 O(n^2) 或 O(logm) 。而要视m、n两者之间的差距来化简,比如m>>n时 可以化简为 O(logm) ,因为表达式增量是由样本量决定的

如何平均一个算法好坏:

评价一个算法的好坏,先看时间复杂度的指标,

然后再分析不同数据样本下的实际运行时间,也就是常数项时间

算法的额外空间复杂度:

指的是对于输入样本,经过算法操作需要的额外空间。比如使用冒泡排序对一个数组排序,期 间只需要一个临时变量 temp ,那么该算法的额外空间复杂度为 O(1) 。

又如归并排序,在排序过程中需要创建一 个与样本数组相同大小的辅助数组,尽管在排序过后该数组被销毁,但该算法的额外空间复杂度为 O(n) 。 

2、简单排序

简单排序是指:冒泡排序、选择排序、插入排序

时间复杂度及空间复杂度:

  • 冒泡排序: 时间复杂度: 严格O(N^2) 额外空间复杂度: O(1)
  • 选择排序: 时间复杂度: 严格O(N^2) 额外空间复杂度: O(1)
  • 插入排序: 时间复杂度: O(N^2) 额外空间复杂度: O(1)
  • 注: 插入排序的真正时间复杂度和数组状态有关,如果数组有序则时间复杂度为O(N)

代码:

 1 // 后面都要用到的swap代码:
 2 public static void swap(int[] arr, int i, int j){
 3     int temp = arr[i];
 4     arr[i] = arr[j];
 5     arr[j] = temp;
 6 }
 7 
 8 // 冒泡排序 -》 每次把最大的数放到最后
 9 public static void bubbleSort(int[] arr){
10     if(arr==null || arr.length<2){
11         return;
12     }
13     for(int end=arr.length-1; end>=0; end--){
14         for(int i=0; i<end; i++){
15             if(arr[i] > arr[i+1]){
16                 swap(arr, i, i+1);
17             }
18         }
19     }
20 }
21 
22 // 选择排序 -》 每次选出最小的数放在起始位置
23 public static void selectionSort(int[] arr){
24     if(arr==null || arr.length<2){
25         return;
26     }
27     for(int i=0; i<arr.length-1; i++){
28         int minIndex = i;
29         for(int j=i+1; j<arr.length; j++){
30             minIndex = arr[j] < arr[minIndex] ? j : minIndex;
31         }
32         swap(arr, i, minIndex);
33     }
34 }
35 
36 // 插入排序 -》 将一个数据插入到已经排好序的有序数据中
37 public static void insertionSort(int[] arr) {
38     if (arr == null || arr.length < 2) {
39         return;
40     }
41     for (int i = 1; i < arr.length; i++) {
42         for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
43             swap(arr, j, j+1);
44         }
45         
46     }
47 }

3、对数器使用

什么是对数器:

 1 有一个你想要测试的方法a
 2     实现一个绝对正确但是复杂度不好的方法b(好实现能实现的方法)
 3     实现一个随机样本产生器
 4     实现将两种方法产生的结果对比的方法
 5     把方法a和方法b对比很多次来验证方法a是否正确
 6     如果有一个样本使得对比出错,打印样本分析是哪个方法出错
 7     当样本数量很多时比对测试依然正确,可以确定方法a已经正确
 8             
 9     另外当我们无法保证绝对正确的方法b正确时我们可以减少样本的数量,我们可以在
10     对比验证不等时打印变量,然后看出问题所在

如何使用对数器:

 1 以上面的冒泡排序为例:
 2     // for test
 3     public static void comparator(int[] arr) {
 4         // call java built-in sort
 5         Arrays.sort(arr);
 6     }
 7         
 8     // for test
 9     public static int[] generateRandomArray(int maxSize, int maxValue) {
10         // generate a random array
11         // (int) ((maxSize + 1) * Math.random()) -> [0, maxSize]
12         int[] arr = new int[(int) ((maxSize + 1) * Math.random())];        
13         for (int i = 0; i < arr.length; i++) {
14             // value of array element -> [1 - maxValue, maxValue]
15             arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
16         }
17         return arr;
18     }
19         
20     // for test
21     public static int[] copyArray(int[] arr) {
22         // copy a array to another array
23         if (arr == null) {
24             return null;
25         }
26         int[] res = new int[arr.length];
27         for (int i = 0; i < arr.length; i++) {
28             res[i] = arr[i];
29         }
30         return res;
31     }
32         
33     // for test
34     public static boolean isEqual(int[] arr1, int[] arr2) {
35         // to judge two array is equal
36         if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
37             return false;
38         }
39         if (arr1 == null && arr2 == null) {
40             return true;
41         }
42         if (arr1.length != arr2.length) {
43             return false;
44         }
45         for (int i = 0; i < arr1.length; i++) {
46             if (arr1[i] != arr2[i]) {
47                 // 可以在此打印不一样的值
48                 // printArray(arr1); printArray(arr2);
49                 return false;
50             }
51         }
52         return true;
53     }
54             
55     // for test
56     public static void printArray(int[] arr) {
57         // print all elements of array 
58         if (arr == null) {
59             return;
60         }
61         for (int i = 0; i < arr.length; i++) {
62             System.out.print(arr[i] + " ");
63         }
64         System.out.println();
65     }
66             
67     // for test
68     public static void main(String[] args) {
69         // test main function
70         int testTime = 500000;        // test times
71         int maxSize = 100;            // max size of array
72         int maxValue = 100;            // max value of array element
73         boolean succeed = true;
74         for (int i = 0; i < testTime; i++) {
75             // generate a random array and copy the array
76             int[] arr1 = generateRandomArray(maxSize, maxValue);
77             int[] arr2 = copyArray(arr1);
78             bubbleSort(arr1);
79             comparator(arr2);
80             if (!isEqual(arr1, arr2)) {
81                 succeed = false;
82                 break;
83             }
84         }
85         // if no errors print Nice! else print Funcking fucked!
86         System.out.println(succeed ? "Nice!" : "Fucking fucked!");
87         
88         // to generate a array and bubbleSort it
89         int[] arr = generateRandomArray(maxSize, maxValue);
90         printArray(arr);
91         bubbleSort(arr);
92         printArray(arr);
93     }
94             
95     如果输出结果中输出了Fucking fucked!,说明有地方输出不一样,
96     此时我们可以在isEqual函数中打印相关变量来查看

上述函数的解释:

  • comparator: 比对的函数或方法(在上面是通过调用Java内置排序实现)
  • generateRandomArray:实现随机生成数组用于测试
  • copyArray:将一个数组中的值复制到另一个数组中
  • isEqual:判断两个数组是否相等
  • printArray:输出数组中的每一个元素

4、递归

递归实质:

递归实质就是系统将当前函数的所有信息压入栈中,然后调用子过程(函数),子过程调用结束后系统从栈中取出栈顶函数相关信息还原现场继续执行函数,故所有递归都可以改成非递归(自己来写压栈)

递归复杂度计算(master公式):

 1 通式(master公式): T(N) = aT(N/b) + O(n^d)
 2         eg: a=2 b=2 d=0时 T(N) = 2T(N/2) + O(1)
 3     计算:
 4         log(b, a) > d => 复杂度为: O(N^log(b, a))
 5         log(b, a) = d => 复杂度为: O(N^d * logN)
 6         log(b, a) < d => 复杂度为: O(N^d)
 7     示例:
 8         T(N) = 2T(N/2) + O(1) => O(N)             实际情况: 递归求数组最大值(递归之后只用比较)
 9         T(N) = 2T(N/2) + O(N) => O(N*logN)        实际情况: 递归之后还要遍历的
10         T(N) = 2T(N/2) + O(N^2) => O(N^2)         

注:其中 a >= 1 and b > 1 是常量,其表示的意义是n表示问题的规模,a表示递归的次数也就是生成的子问题数,b表示每次递归是原来的1/b之一个规模,O(n^d)表示分解和合并所要花费的时间之和(简单说就是递归调用之后的那个常数项时间)

原文地址:https://www.cnblogs.com/wyb666/p/10106010.html