算法

算法(algorithm)是为求解一个问题需要遵循的、被清楚地指定的简单指令的集合。

数学基础

四个定义

1. 大O表示法:

如果存在正常数 c 和 n使得当 N ≥ n0时,T(N) ≤ cf(N),则记为T(N) = O(f(N))。

(描述了T(N)的相对增长率小于等于f(N)的相对增长率。)

2. 大Ω表示法:

如果存在正常数 c 和 n使得当 N ≥ n0时,T(N) ≥ cf(N),则记为T(N) = Ω(f(N))。

(描述了T(N)的相对增长率大于等于f(N)的相对增长率。)

3. 大Θ表示法:

如果 T(N) = O(f(N)) 且 T(N) = Ω(f(N)),则 T(N) = Θ(f(N))。

(描述了T(N)的相对增长率等于f(N)的相对增长率。)

4. 小o表示法:

如果 T(N) = O(f(N)) 且 T(N) ≠ Θ(f(N)),则 T(N) = o (f(N))。

(描述了T(N)的相对增长率小于f(N)的相对增长率。)

三个结论

1. 如果T1(N) = O(f(N)) 且 T2(N) = O(g(N)),那么

  (a). 加法法则:T1(N) + T2(N) = max(O(f(N)), O(g(N)));【大O的和等于大O的最大值】

  (b). 乘法法则:T1(N) * T2(N) = O(f(N) * g(N)).【大O的积等于积的大O】

2. 如果T(N) 是一个k次多项式,则T(N) =  Θ(Nk).

3. 对任意常数k,logkN = O(N)。它告诉我们对数增长得非常缓慢。

时间复杂度

一个算法在输入规模为N时运行的耗时称为时间复杂度,常用大O表示。一般来说,它描述了最坏情况下的时间复杂度(平均情况下的时间复杂度需要更加复杂的数学分析)。

为了简化分析,约定:不存在特定的时间单位。因此,常抛弃一些常数系数和低阶项,从而便于计算大O运行时间。

看个例子:

int sum(int N)
{
int i, partialSum; partialSum = 0;  //1 个时间单元
for (i = 1; i < N; i++)  //初始化耗时 1 个时间单元,测试比较耗时 N + 1 个时间单元,自增运算耗时 N 个时间单元
partialSum += i * i * i;    //4 个时间单元(2 次乘,1 次加,1 次赋值),循环 N 次耗时 4N 个时间单元
return partialSum;   //1 个时间单元
}

声明不耗时间,忽略函数调用和返回值的开销,总共耗时1 + 1 + N + 1 + N + 4N+ 1 = 6N + 4。按照之前的约定,忽略低阶项4和常系数6,我们说该函数是O(N),时间复杂度是线性级。

这仅仅是一个小函数,如果有一个较大的程序,那么计算时间复杂度需要的工作量就太琐碎繁杂了。考虑大O的结果,它只关注得到的最高阶项。常数级运行时间相对于有关输入规模N的语句的耗时是很小的(无关紧要),所以忽略掉常数级O(1)的语句第5行、第7行、第8行,跟输入规模N有关的耗时主要是for循环,循环大小为N,所以该函数的运行时间就是O(N)线性级的。

计算时间复杂度的一般法则

法则1——for循环

一次for循环的运行时间至多是该for循环内语句(包括测试)的运行时间乘以迭代的次数。

法则2——嵌套的for循环

从里向外分析这些循环。在一组嵌套循环内部的一条语句总的运行时间为该语句的运行时间乘以该组所有for循环的大小的乘积。

法则3——顺序语句

将各个语句的运行时间求和即可(这意味着,其中的最大值就是所得的运行时间)

法则4——if/else 语句

一个if/else语句的运行时间从不超过判断再加上分支语句中运行时间长者的总的运行时间。显然在某些情况下这么估计有些过高,但绝不会估计过低。

最大子序列和问题

问题描述:给定整数A1,A2,,... ,AN(可能有负数),求∑jk=i Ak的最大值(为方便起见,如果所有整数均为负数,则最大子序列和为0)。

下面给出四种算法。

1. 穷举法

枚举所有的子序列之和,返回最大值。时间复杂度O(n3)。

int maxSequenceSum1(const int A[], int N)
{
int i, j, k, maxSum, thisSum; maxSum = 0;
for (i = 0; i < N; i++)
{
for (j = i; j < N; j++)
{
thisSum = 0;
for (k = i; k <= j; k++)
thisSum += A[k]; if (thisSum > maxSum)
maxSum = thisSum;
}
}
  return maxSum;
}

2. 撤销一个for循环,降低立方级的运行时间

考虑到∑jk=i A= ∑j-1k=i A+ Aj。修改如下。算法复杂度O(N2)。

int maxSequenceSum2(const int A[], int N)
{
int i, j, maxSum, thisSum; maxSum = 0;
for (i = 0; i < N; i++)
{
thisSum = 0;
for (j = i; j < N; j++)
{
thisSum += A[j]; if (thisSum > maxSum)
maxSum = thisSum;
}
}
return maxSum;
}

3. 分治算法

把一个问题分成两个大致相等的子问题,然后递归地对它们求解,这是“分”部分。“治”阶段将两个子问题的解合到一起并可能再做少量的附加工作,最后得到整个问题的解。

思路:最大子序列和只可能出现在三处:左半部分、右半部分、跨越并穿过中间而占据左右两半部分。前两种情况可以递归求解,第三部分的最大和可以通过求出前半部分的最大和(包括前半部分最后一个元素)以及后半部分的最大和(包括后半部分第一个元素)而得到,然后将这两个和加在一起。时间复杂度O(logN)。

考虑下列输入:

前半部分 后半部分
4 -3 5 -2 -1 2 6 -2

其中前半部分的最大子序列和为6(从元素A1到A3)而后半部分的最大子序列和为8(从元素A6到A7)。

前半部分包含其最后一个元素的最大和是4(从元素A1到A4),而后半部分包含其第一个元素的最大和是7(从元素A5到A7)。因此,跨越这两部分且通过中间的最大和为4+7 = 11(从元素A1到A7)。

int maxSubSum(const int A[], int left, int right)
{
int maxLeftSum, maxRightSum;
int maxLeftBorderSum, maxRightBorderSum;
int leftBorderSum, rightBorderSum;
int center, i; if (left == right) /*Base case*/
{
if (A[left] > 0)
return A[left];
else
return 0;
} center = (left + right) / 2;
maxLeftSum = maxSubSum(A, left, center);
maxRightSum = maxSubSum(A, center + 1, right); maxLeftBorderSum = 0; leftBorderSum = 0;
for (i = center; i >= left; i--)
{
leftBorderSum += A[i];
if (leftBorderSum > maxLeftBorderSum)
maxLeftBorderSum = leftBorderSum;
} maxRightBorderSum = 0; rightBorderSum = 0;
for (i = center + 1; i <= right; i++)
{
rightBorderSum += A[i];
if (rightBorderSum > maxRightBorderSum)
maxRightBorderSum = rightBorderSum;
}
return max(maxLeftSum, maxRightSum,
maxLeftBorderSum + maxRightBorderSum);
} int maxSequenceSum3(const int A[], int N)
{
return maxSubSum(A, 0, N - 1);
}

4.联机算法

每个数据只访问一次。仅需要常量空间并以线性时间运行的联机算法几乎是完美的算法。所以联机算法的时间复杂度是O(N)

int maxSequenceSum4(const int A[], int N)
{
int i, maxSum, thisSum; maxSum = 0; thisSum = 0;
for (i = 0; i < N; i++)
{
thisSum += A[i]; if (thisSum> maxSum)
maxSum = thisSum;
else if (thisSum < 0)
thisSum = 0;
}
return maxSum;
}

时间复杂度中的对数规律

某些分治算法将以O(NlogN)运行。除分治算法外,可将对数最常出现的规律概括为以下一般法则:

如果一个算法用常数时间O(1)将问题的大小削减为其一部分(通常是1/2),那么该算法就是O(logN)的。另一方面,如果使用常数时间只是把问题减少一个常数(如将问题减少1)那么这种算法那就是O(N)的。

具有对数特点的三个例子

三个例子的时间复杂度均为O(logN)。

1.对分查找

给定一个整数X和A0,A1,... ,AN-1,后者已经预先排序并在内存中,求使得Ai = X的下标i,如果X不在数据中,则返回i = -1。

int binarySearch(const int A[], int N, int X)
{
int low, high, mid; low = 0;high = N - 1;
while (low <= high)
{
mid = (low + high) / 2;
if (A[mid] < X)
low = mid + 1;
else if (A[mid] > X)
high = mid - 1;
else
return mid;
}
return -1; //not found
}

2. 欧几里算法

计算最大公因数。两个整数的最大公因数(Gcd)是同时整除两者的最大整数。

算法通过连续计算余数直到为 0 时停止,最后的非零余数就是最大公因数。

unsigned int gcd(unsigned int M, unsigned int N)
{
int rem; while (N > 0)
{
rem = M % N;
M = N;
N = rem;
}
return M;
}

3.幂运算

计算XN

如果N是偶数,则X= X(N/2) * X(N/2);如果N是奇数,则X= X(N-1/2) * X(N-1/2) * X。

long pow(long X, unsigned int 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;
}

(完)

【数据结构与算法分析——C语言描述】第二章总结 算法分析的更多相关文章

  1. 最小正子序列(序列之和最小,同时满足和值要最小)(数据结构与算法分析——C语言描述第二章习题2.12第二问)

    #include "stdio.h" #include "stdlib.h" #define random(x) (rand()%x) void creat_a ...

  2. 数据结构与算法分析——C语言描述 第三章的单链表

    数据结构与算法分析--C语言描述 第三章的单链表 很基础的东西.走一遍流程.有人说学编程最简单最笨的方法就是把书上的代码敲一遍.这个我是头文件是照抄的..c源文件自己实现. list.h typede ...

  3. C语言学习书籍推荐《数据结构与算法分析:C语言描述(原书第2版)》下载

    维斯 (作者), 冯舜玺 (译者) <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行 ...

  4. 《数据结构与算法分析——C语言描述》ADT实现(NO.00) : 链表(Linked-List)

    开始学习数据结构,使用的教材是机械工业出版社的<数据结构与算法分析——C语言描述>,计划将书中的ADT用C语言实现一遍,记录于此.下面是第一个最简单的结构——链表. 链表(Linked-L ...

  5. 《数据结构与算法分析-Java语言描述》 分享下载

    书籍信息 书名:<数据结构与算法分析-Java语言描述> 原作名:Data Structures and Algorithm Analysis in Java 作者: 韦斯 (Mark A ...

  6. 数据结构与抽象 Java语言描述 第4版 pdf (内含标签)

    数据结构与抽象 Java语言描述 第4版 目录 前言引言组织数据序言设计类P.1封装P.2说明方法P.2.1注释P.2.2前置条件和后置条件P.2.3断言P.3Java接口P.3.1写一个接口P.3. ...

  7. 数据结构(Java语言描述)-第一章:概述

    第一章 概述 1.0 序言 自己为啥要学数据结构嘞,我觉得主要有以下三个原因: 前段时间在看并发编程时,发现aqs,corrunthashmap等底层都用到了数据结构,主要的有队列,还有链表,学习数据 ...

  8. 《数据结构与算法分析:C语言描述_原书第二版》CH3表、栈和队列_reading notes

    表.栈和队列是最简单和最基本的三种数据结构.基本上,每一个有意义的程序都将明晰地至少使用一种这样的数据结构,比如栈在程序中总是要间接地用到,不管你在程序中是否做了声明. 本章学习重点: 理解抽象数据类 ...

  9. 《数据结构与算法分析:C语言描述_原书第二版》CH2算法分析_课后习题_部分解答

    对于一个初学者来说,作者的Solutions Manual把太多的细节留给了读者,这里尽自己的努力给出部分习题的详解: 不当之处,欢迎指正. 1.  按增长率排列下列函数:N,√2,N1.5,N2,N ...

随机推荐

  1. Win XP 如何禁用屏保

    如果你试过 “在桌面空白处点击右键-[属性]-[屏幕保护程序],选择[无],点击[确定]”后,当时是可以去掉屏保.但如果重启计算机或者从待机状态唤醒后,屏保依然会出现,那么你可以试试下面的方法. 首先 ...

  2. 你猜……你再猜

    『男』:你喜欢我吗? 『女』:你猜. 『男』:喜欢. 『女』:你再猜. 『男』:--

  3. 南阳理工ACM 括号匹配问题,并求出使得括号能够匹配需要新增的最小括号数(括号匹配(二))

    描述 给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起 ...

  4. combobox远程加载数据的总结和Json数据的小结

    1.从后台返回请求加载Combobox下拉框数据 html部分1 <select name="mateBelongZ" id="mateBelongZID" ...

  5. phpStorm中ftp的配置与使用

    小结:很方便,支持ftp功能和比较. 扩展,可以查看远程文件和日期

  6. Java里面instanceof怎么实现的

    开始完全一头雾水呀,后面看了Java指令集的介绍,逐渐理解了. https://www.zhihu.com/question/21574535/answer/18998914 下面这个答案比较直白 你 ...

  7. android 安装 出现Android Native Development Tools不能安装

    Software being installed: Android Native Development Tools 20.0.0.v201206242043-391819 (com.android. ...

  8. 剑指offer—算法之位运算(二进制中1的个数)

    位运算: 左移:m<<n将m左移n位,左移后低位补充0: 右移:m>>n将m右移n位,右移后高位补充的是符号位,负数补充1,整数补充0.(正数的边界值为(1,ox7FFFFFF ...

  9. Java异常体系结构

    1)系统错误(system error)是由Java虚拟机抛出的,用Error类表示.Error类描述的是内部系统错误.这样的错误很少发生.如果发生,除了通知用户以及尽量稳妥地终止程序外,几乎什么都不 ...

  10. Jsonp和java操作例子

    介绍JSONP之前,先简单的介绍一些JSON.JSON是JavaScript Object Notation的缩写,是一种轻量的.可读的基于文本的数据交换开放标准.源于JavsScript编程语言中对 ...