跟左神学算法_1 算法入门

Posted wyb666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跟左神学算法_1 算法入门相关的知识,希望对你有一定的参考价值。

内容:

1、时间复杂度

2、简单排序

3、对数器使用

4、递归

注:实现代码为Java

 

 

 

1、时间复杂度

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

 

关于时间复杂度:

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

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

然后把剩下的部分记为f(N),那么时间复杂度为O(f(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)表示分解和合并所要花费的时间之和(简单说就是递归调用之后的那个常数项时间)

 

以上是关于跟左神学算法_1 算法入门的主要内容,如果未能解决你的问题,请参考以下文章

算法竞赛入门码蹄集进阶塔335题(MT3330-3335)

算法竞赛入门经典_4.3_递归

Java基础入门五)之方法以及递归算法

算法竞赛入门经典_3.1_数组_逆序输出_开灯问题

以下代码片段的算法复杂度

算法竞赛入门经典_2_变量及其输入