陈越《数据结构》第一讲 基本概念
1什么是数据结构
1.1 引子
例子:如何在书架上摆放图书?
- 随便放;
- 按照书名的拼音字母顺序排放;
- 把书架划分成几块区域,每块区域指定摆放某种类别的图书;在每种类别内,按照书名的拼音字母顺序排放。
解决问题方法的效率,跟数据的组织方式有关。\color{red}{解决问题方法的效率, 跟数据的组织方式有关。}解决问题方法的效率,跟数据的组织方式有关。
例2:写程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数。
- 循环实现;
- 递归实现。//数值从10到10610到10^610到106
解决问题方法的效率,跟空间的利用效率有关。\color{red}{解决问题方法的效率, 跟空间的利用效率有关。}解决问题方法的效率,跟空间的利用效率有关。
例3:写程序计算给定多项式在给定点x处的值。
利用 p+=(a[i]∗pow(x,i));p += (a[i] * pow(x, i));p+=(a[i]∗pow(x,i));进行计算;
秦九韶利用 p=a[i−1]+x∗p;p = a[i-1] + x*p;p=a[i−1]+x∗p;进行计算;
用 time.h中的常数CLK_TCK,clock_t start, stop;计算时间。
解决问题方法的效率,跟算法的巧妙程度有关。\color{red}{解决问题方法的效率, 跟算法的巧妙程度有关。}解决问题方法的效率,跟算法的巧妙程度有关。
即:解决问题方法的效率,跟数据的组织方式、跟空间的利用效率和跟算法的巧妙程度有关。
数据结构是:
- 数据对象\color{red}{数据对象}数据对象 在计算机中的组织方式(逻辑结构、物理存储结构);
2.数据对象必定与一系列加在其上的 操作\color{red}{操作}操作相关联;
3.完成这些操作所用的方法就是算法\color{red}{算法}算法。
1.2 抽象数据类型
数据结构
- 数据对象在计算机中的组织方式(逻辑结构、物理存储结构);
- 数据对象操作的关联关系;
- 数据对象的最高效算法。
抽象数据类型
数据类型
- 数据对象集;
- 数据集合相关联的操作集。
抽象(描述数据类型的方法不依赖于具体实现)
- 与存放数据的机器无关;
- 与数据存储的物理结构无关;
- 与实现操作的算法和编程语言均无关。
2. 什么是算法
算法(Algorithm)\color{red}{算法(Algorithm )}算法(Algorithm)定义:
- 一个有限指令集;
- 接受一些输入(有些情况下不需要输入);
- 产生输出(必须);
- 一定在有限步骤之后终止;
- 每一条指令必须:
- 有充分明确的目标,不可以有歧义;
- 计算机能处理的范围之内;
- 描述应不依赖于任何一种计算机语言以及具体的实现手段。
2.1什么是好的算法?
- 空间复杂度S(n)\color{red}{空间复杂度S(n)}空间复杂度S(n) 占用存储单元的长度。
- 时间复杂度T(n)\color{red}{时间复杂度T(n)}时间复杂度T(n) 耗费时间的长度。
在例3中,第一种方法的时间复杂度是T(n)=C1n2+C2nT(n) = C_1n^2+C_2nT(n)=C1n2+C2n;秦九韶的时间复杂度是T(n)=C.nT(n) =C.nT(n)=C.n。
在分析一般算法的效率时,我们经常关注下面两种复杂度:
- 最坏情况复杂度\color{red}{最坏情况复杂度}最坏情况复杂度Tworst(n)T_{worst}( n )Tworst(n);
- 平均复杂度\color{red}{平均复杂度}平均复杂度Tavg(n)T_{avg}( n )Tavg(n)。
其中我们最关心最坏情况复杂度\color{red}{最坏情况复杂度}最坏情况复杂度。
2.2 一些基本概念
复杂度的渐进表示法\color{red}{复杂度的渐进表示法}复杂度的渐进表示法
- 上界:T(n)=O(f(n))T(n) = O(f(n))T(n)=O(f(n));
- 下界:T(n)=Ω(g(n))T(n) = Ω(g(n))T(n)=Ω(g(n));
- 上下界等价:T(n)=Θ(h(n));Θ(h(n))=O(f(n)),Θ(h(n))=Ω(g(n))T(n) = Θ(h(n));Θ(h(n))=O(f(n)),Θ(h(n))=Ω(g(n))T(n)=Θ(h(n));Θ(h(n))=O(f(n)),Θ(h(n))=Ω(g(n))。
我们写O(f(n))时,是写最小上界,写Ω(g(n)时,是写最大下界,这样才有意义。\color{red}{我们写{O(f(n))}时,是写最小上界,写Ω(g(n)时,是写最大下界,这样才有意义。}我们写O(f(n))时,是写最小上界,写Ω(g(n)时,是写最大下界,这样才有意义。
复杂度的渐进表示法\color{red}{复杂度的渐进表示法}复杂度的渐进表示法
- 若两段算法分别有复杂度T1(n)=O(f1(n))T_1(n) = O(f_1(n))T1(n)=O(f1(n))和T2(n)=O(f2(n))T_2(n) =O(f_2(n))T2(n)=O(f2(n)),则:
- T1(n)+T2(n)=max(O(f1(n)),O(f2(n)))T_1(n)+T_2(n)=max( O(f_1(n)),O(f_2(n)))T1(n)+T2(n)=max(O(f1(n)),O(f2(n)));
- T1(n)∗T2(n)=O(f1(n)∗f2(n))。T_1(n) * T_2(n) = O( f_1(n) * f_2(n) )。T1(n)∗T2(n)=O(f1(n)∗f2(n))。
若T(n)T(n)T(n)是关于n的k阶多项式\color{red}{n的k阶多项式}n的k阶多项式,那么起作用的是最大项,即T(n)=Θ(nk);\color{red}{T(n)=Θ(n^k);}T(n)=Θ(nk);
一个for循环的时间复杂度\color{red}{一个for循环的时间复杂度}一个for循环的时间复杂度等于循环次数\color{red}{循环次数}循环次数乘以循环体代码的复杂度;\color{red}{循环体代码的复杂度;}循环体代码的复杂度;
if−else结构\color{red}{if-else 结构}if−else结构的复杂度取决于if的条件判断复杂度和两个分枝部分的复杂度,总体复杂度取三者中最大;\color{red}{总体复杂度取三者中最大;}总体复杂度取三者中最大;
O(n2)复杂度的算法本能的优化为O(nlogn)。\color{red}{O(n^2)复杂度的算法本能的优化为O(n log n)。}O(n2)复杂度的算法本能的优化为O(nlogn)。
3.应用实例:最大子列和问题
应用实例:最大子列和问题
01 - 复杂度1 最大子列和问题(20分)
例如给定序列{−2,11,−4,13,−5,−2}\{ -2, 11, -4, 13, -5, -2 \}{−2,11,−4,13,−5,−2},其连续子列{11,−4,13}\{ 11, -4, 13 \}{11,−4,13}有最大的和202020。现要求你编写程序,计算给定整数序列的最大子列和。
- 本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:
- 数据1:与样例等价,测试基本正确性;
- 数据2:10210^2102个随机整数;
- 数据3:10310^3103个随机整数;
- 数据4:10410^4104个随机整数;
- 数据5:10510^5105个随机整数;
- 输入格式 :
输入第1行给出正整数K(≤100000);第2行给出K个整数,其间以空格分隔。 - 输出格式 :
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。 - 输入样例 :
6
-2 11 -4 13 -5 -2 - 输出样例 :
20
解决方法:\color{red}{解决方法}:解决方法:
- 时间复杂度为T(N)=O(N3)T( N ) = O( N^3 )T(N)=O(N3);
- 时间复杂度为T(N)=O(N2)T( N ) = O( N^2 )T(N)=O(N2);
- 时间复杂度为T(N)=O(NlogN)T( N ) = O( N logN )T(N)=O(NlogN);(分而治之)
- 时间复杂度为T(N)=O(N)T( N ) = O( N )T(N)=O(N)。(在线处理)
时间复杂度为T(N)=O(N2)T( N ) = O( N^2 )T(N)=O(N2)的代码:
#include<stdio.h>
#include<iostream>
#define MAXN 100000
int arr[MAXN];
int MaxSubseqSum(int A[], int N);
int main()
{
int i, n;
scanf("%d",&n);
for (i = 0; i < n; i++)
scanf("%d",&arr[i]);
printf("%d\n",MaxSubseqSum(arr,n));
system("pause");
return 0;
}
int MaxSubseqSum(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j;
for (i = 0; i < N; i++)
{
ThisSum = 0;
for (j = i; j < N; j++)
{
ThisSum = ThisSum + A[j];
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
分而治之的代码:
#include<stdio.h>
#include<iostream>
#define MAXN 100000
int arr[MAXN + 10];
int maxThree(int a,int b,int c);
int maxSubSeq(int arr[],int low,int height);
int maxSubSeq1(int arr[],int n);
int main()
{
int i, n;
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%d", &arr[i]);
printf("%d\n", maxSubSeq(arr, 0, n-1));
system("pause");
return 0;
}
int maxSubSeq1(int arr[],int n)
{
int i = 0, iThisSum = 0, iMaxSum = 0;
for(i = 0; i < n; i++)
{
iThisSum += arr[i];
if(iThisSum > iMaxSum) iMaxSum = iThisSum;
else if(iThisSum < 0) iThisSum = 0;
}
return iMaxSum;
}
int maxSubSeq(int arr[], int low, int height)
{
int i = 0, iMid = 0, iThisSum = 0, iLeftMax = 0, iRightMax = 0, iLeftMaxSum = 0, iRightMaxSum = 0;
if(low >= height) return arr[low];
iMid = (low + height)/2;
iLeftMax = maxSubSeq(arr,low,iMid);//左边最大
iRightMax = maxSubSeq(arr,(iMid+1),height);//右边最大
//中间(跨越)最大
iThisSum = iLeftMaxSum = 0;
for(i = iMid ; i >low ; i-- )
{
iThisSum += arr[i];
if(iThisSum > iLeftMaxSum) iLeftMaxSum = iThisSum;
}
iThisSum = iRightMaxSum = 0;
for(i = iMid ; i <height ; i++)
{
iThisSum += arr[i];
if(iThisSum > iRightMaxSum) iRightMaxSum = iThisSum;
}
return maxThree(iLeftMax , iRightMax, (iRightMaxSum + iLeftMaxSum));
}
int maxThree(int a,int b,int c)
{
int max = a;
if(b > max) max = b;
if(c > max) max = c;
return max;
}