《数据结构与算法分析–C++语言描述》(第四版) 第2章 算法分析

第2章 算法分析

2.1 数学基础

1. T(N)=O(f(N)) T(N)的增长率小于或等于f(N)的增长率 T(N)是以不快于f(N)的速度增长
2. T(N)=Ω(f(N)) T(N)的增长率大于或等于f(N)的增长率
3. T(N)=Θ(h(N)) T(N)的增长率等于f(N)的增长率

上界(upper bound)
下界(lower bound)

大O表示法
	常数因子、低阶项,可以忽略、去掉

2.3 要分析的问题

Tavg(N) :算法对输入大小为N所花费的平均情形的运行时间
Tworst(N) :算法对输入大小为N所花费的最坏情形的运行时间

程序是算法以一种特殊编程语言的实现
一般来说,若无其他的指标,则所要求的量是最坏情况的运行时间,它为所有的输入提供了一个界限,包括特别坏的输入

最大的子序列和问题

2.4 运行时间计算

不存在特定的时间单位
计算大O运行时间
	抛弃前导的常数
	抛弃低阶项
大O是一个上界,程序可以提前结束,但绝不能错后

2.4.3 最大子序列和问题的求解

分治:
	分:把问题分成两个大致相同的子问题,然后递归地对它们求解
	治:将两个子问题的解修补到一起,并可能再做些小量的附加工作,最后得到整个问题的解
分治法解决最大子序列和问题,算法时间复杂度为:O(NlogN)
//fig2.8_P54.cpp
//线性时间最大相连子序列的解法(最佳解法)

#include <iostream>
#include <vector>
using namespace std;

//1.j代表当前序列的终点,i代表当前序列的终点
//2.如果我们不需要知道最佳子序列在哪里,那么i的使用可以从程序上被优化掉,不过在设计算法的时候还是假设i是需要的
//3.如果arr[i]是负的,那么它不可能代表最佳子序列的起点,因为任何包含arr[i]作为起点的子序列都可以通过arr[i+1]作起点而得到改进
//4.同理,任何负的子序列都不可能是最优子序列的前缀
int maxSubSum4(const vector<int> &a)
{
    
    
    int maxSum = 0, thisSum = 0;
    for (int j = 0; j < a.size(); ++j)
    {
    
    
        thisSum += a[j];
        if (thisSum > maxSum)
            maxSum = thisSum;
        else if (thisSum < 0) //
            thisSum = 0;
    }
    return maxSum;
}

int main()
{
    
    
    vector<int> arr{
    
    -2, 11, -4, 13, -5, -2}; //答案为20, arr[1]到arr[3]
    cout << maxSubSum4(arr) << endl;
    return 0;
}

2.4.4 运行时间中的对数

分析算法最混乱的方面大概几种在对数上面
某些分治算法将以O(NlogN)时间运行
对数最常出现的规律可以概括为下列一般法则
1. 如果一个算法用常数(O(1))时间将问题的大小削减为其一部分(通常是1/2),那么该算法就是O(logN)
2. 如果使用常数时间只是把问题减少一个常数的数量(如将问题减少1),那么这种算法就是O(N)

介绍3个具有对数特点的例子

1. 二分查找/折半查找

fig2.9_P55_binarySearch.cpp

//fig2.9_P55_binarySearch.cpp 
#include <iostream>
#include <vector>
using namespace std;
const int NOT_FOUND = -1;

/**
 * Performs the standard binary search using two comparisons per level.
 * Returns index where item is found or -1 if not found
 */
template <typename Comparable>
int binarySearch(const vector<Comparable> &a, const Comparable &x)
{
    
    
    int low=0,high=a.size()-1;
    while(low<=high)
    {
    
    
        int mid=(low+high)/2;
        if(a[mid]<x)
            low=mid+1;
        else if(a[mid]>x)
            high=mid-1;
        else
            return mid;//Found
    }
    return NOT_FOUND;
}

int main()
{
    
    
    const int SIZE=8;
    vector<int> a(SIZE);
    for(int i=0;i<SIZE;++i)
        a[i]=i*2;
    for(int j=0;j<SIZE*2;++j)
        cout<<"FOUND "<<j<<" at"<<binarySearch(a,j)<<endl;
    return 0;
}

2. 计算最大公因数(gcd)的欧几里得算法

//fig2.10_P56_gcd.cpp

//fig2.10_P56_gcd.cpp
//计算最大公因数(gcd)的欧几里得算法
//通过连续计算余数知道余数是0位为止,最后的非零余数就是最大公因数
#include <iostream>
using namespace std;

long gcd(long long m, long long n)
{
    
    
    while (n != 0)
    {
    
    
        long long rem = m % n;
        m = n;
        n = rem;
    }
    return m;
}
// Test program
int main()
{
    
    
    cout << "gcd( 45, 35 ) = " << gcd(45, 35) << endl;
    cout << "gcd( 1989, 1590 ) = " << gcd(1989, 1590) << endl;
    return 0;
}

3. 幂运算,返回x的n次方

//fig2.11_P57_pow.cpp
//幂运算,返回x的n次方
#include <iostream>
using namespace std;

bool isEven(int n) //判断是否是偶数,是:true
{
    
    
    return (n % 2 == 0);
}
long long pow(long long x, int n)//返回x的n次方
{
    
    
    if (n == 0)
        return 1;
    if (n == 1)
        return x;
    if (isEven(n))
        return pow(x * x, n / 2);
    else
        return pow(x * x, n / 2) * x;
}
int main()
{
    
    
    cout << "2^61 = " << pow(2, 61) << endl;
    cout << "2^62 = " << pow(2, 62) << endl;
}

猜你喜欢

转载自blog.csdn.net/liangwenhao1108/article/details/105169939