学习算法第一天:算法初步

一、学习算法必要性why:

应用:机器学习、数据挖掘、自然语言处理、密码学、计算机图形学等

找工作:贪心、分治、动态规划、树、图等.

二、怎么做how?

  • 穷举法(万能算法)

求N个数的全排列

8皇后问题

  • 分而治之(减而治之)

二分查找——减而治之

归并排序——分而治之

  • 贪心

最小生成树 Prim, Kruskal

单源最短路 Dijkstra

  • 动态规划

揹包

士兵路径

三、时间、空间复杂度

常见时间复杂度分析方法

  • 数循环次数
  • 均摊分析
  • 递归式——主定理

• O(1)   基本运算,+,-,*,/,%,寻址

• O(logn)   二分查找

• O(n1/2)   枚举约数

• O(n)    线性查找

• O(n^{2})    朴素最近点对

• O(n3)    Floyd最短路、普通矩阵乘法

• O(nlogn)    归并排序、快速排序的期望复杂度、基于比较排序的算法下界

• O(2n)   枚举全部的子集

• O(n!)    枚举全排列

• 总结:

优秀 O(1) < O(logn) < O(n1/2) < O(n) < O(nlogn)

可能可以优化 O(n^{2}) < O(n^{3}) < O(2^{n}) < O(n!)

证明:O(nlogn)是基于比较的算法时间复杂度的下限

因为排序就是排列组合中的一种,总的组合次数是n!种,每一次比较如i<j即可排除一半的组合,

所以当比较了k次,即2^{k}>n! 所以k>ln(n!)     ln(n!) < ln(n^{n})  所以k> nln(n) 所以O(nlogn)是下限

四、时间复杂度的均摊分析

  • 多个操作,一起算时间复杂度,MULTIPOP的队列,可以一次性出队k个元素,每个元素只出入队列一次
  • 动态数组尾部插入操作(vector)一旦元素超过容量限制,则扩大一倍,再复制原来的数组,再释放原数组空间

所以vector当插入n个元素时,可能发生复制的次数是1+2+4+8+.....+2^{logn} 次 = 2*2^{logn}-1次=O(2n)   所以插入n个数时间复杂度是 O(n);所以插入一个元素的时间复杂度是O(1),所以vector特别快,值得学习。

五、例题1:

• 给定数组a[1…n],求最大子数组和,即找出1<=i<=j<=n,使a[i]+a[i+1]+…+a[ j]最大,LeetCode第53题,最大子序和

• 介绍三个算法

  1. 暴力枚举 O(n3)
  2. 优化枚举 O(n2)
  3. 贪心法 O(n)

要求:手写暴力枚举算法必须会;暴力枚举:三重循环,时间复杂度 O(n3) 附加空间复杂度 O(1)

for i = 1 to n

     for j = i to n

         sum = a[i]+..+a[j]

         ans =max(ans, sum)


class Solution {
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        int ans = nums[0];
        for(int i = 0; i < n; ++i){
            for(int j = i; j < n; ++j){
                int sum = 0;
                for(int k = i; k <= j; ++k){
                    sum += nums[k];
                    if(sum > ans){
                        ans = sum;
                    }
                }
            }
        }
        return ans;
    }
}

重点:优化过程就是解决重复计算的过程上述存在i...j的求和与i....j,j+1的求和,存在i...j求和的重复,所以i....j,j+1 = sum(i....j) + nums[j]即可,提前记录sum(i....j)和,优化如下

优化枚举:两重循环

for i = 1 to n

    sum = 0

    for j = i to n

        sum = sum + a[i]

        ans = max(ans, sum)

时间复杂度 O(n2) 附加空间复杂度 O(1) 

class Solution {
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        int ans = nums[0];
        for(int i = 0; i < n; ++i){
            int sum = 0;
            for(int j = i; j < n; ++j){
                sum += nums[j];
                if(sum >= ans){
                    ans = sum;
                }
            }
        }
        return ans;
    }
}

贪心法:一重循环

sum = 0 ans = 0

for i = 1 to n

    sum = sum + a[i]

    ans = max(sum, ans)

    if (sum < 0) sum = 0

时间复杂度 O(n) 附加空间复杂度 O(1)

class Solution {
    public int maxSubArray(int[] nums) {
        int ans = nums[0];
        int sum = 0;
        int n=nums.length;
        for(int i=0; i< n; i++){
            sum += nums[i];
            if(sum > ans){
                ans = sum;
            }
            if(sum < 0){
                sum = 0;
            }
        }
        return ans;
    }
}

例题2:设计一个队列;支持:出队,入队,求最大元素;要求O(1);均摊分析

例题3:给定一个正整数组a,是否能以3个数为边长构成三角形?即是否存在不同的i,j,k,

• 满足 a[i] < a[ j] + a[k]

• 并且 a[ j] < a[i] + a[k]

• 并且 a[k] < a[i] + a[ j]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章