本笔记来自于浙大陈越,何钦铭老师https://www.bilibili.com/video/av55114968?p=1
一、数据结构
1.到底什么是数据结构?
- 数据对象在计算机中的组织方式
- 逻辑结构
- 物理存储结构
2.抽象数据类型(Abstarct Data Type)
- 数据类型
- 数据对象集
- 数据集合相关联的操作集
- 抽象:描述数据类型的方法不依赖于具体实现
- 与存放数据的机器无关
- 与数据存储的物理结构无关
- 与实现操作的算法和编程语言均无关
只描述数据对象集和相关操作集“是什么”,并不涉及“如何做到”的问题。
二、算法(Algorithm)
1.定义
- 一个有限指令集
- 接受一些输入(有些情况下不需要输入)
- 产生输出
- 一定在有限步骤之后终止
- 每一条指令必须:
- 有充分明确的目标,不可以有歧义
- 计算机能处理范围内
- 描述应不依赖于任何一种计算机语言以及具体的实现手段
2.评价算法好坏
- 空间复杂度 S(n) ——————根据算法写成的程序在执行时占用存储空间的长度
- 时间复杂度 T(n) ——————根据算法写成的程序在执行时耗费的时间的长度
3.分析算法效率
- 最坏情况复杂度Tworst(n)
- 平均复杂度Tavg(n) Tavg(n)≤ Tworst(n)
一般情况下我们更关心最坏情况复杂度。
4.复杂度的渐进表示法
- T(n)=O(f(n))表示存在常数C>0,n0 >0,使得当n>n0时有T(n)≤C·f(n)
5.复杂度分析小窍门
应用实例
1. 最大子列和问题
给定N个整数的序列{A1,A2…AN},求函数的最大值。(wordpress的markdown插件有问题实在打不出公式)
算法一:
思路:把所有的连续子列和全部算出来,从中找最大的一个
int MaxsubseqSum1(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j, k;
for (i = 0; i < j; i++)//i是子列左端位置 A[i]
{
for (j = i; j < N; j++)//j是子列右端位置 A[j]
{
ThisSum = 0; //ThisSum是从A[i]到A[j]的子列和
for (k = i; k <= j; k++)
{
ThisSum += A[k];
}
if (ThisSum > MaxSum)//如果刚刚得到的子列更大
{
MaxSum = ThisSum;//则更新结果
}
}//j循环结束
}//i循环结束
return MaxSum;
}
复杂度分析:T(N)=O(N3) 三层嵌套,非常笨拙
算法二:
分析:优化一,一中k循环可以改为j循环时加上A[j]的值,直接求出i到j的和,不需要从头开始算
int MaxsubseqSum2(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j, k;
for (i = 0; i < j; i++)//i是子列左端位置 A[i]
{
ThisSum = 0; //ThisSum是从A[i]到A[j]的子列和
for (j = i; j < N; j++)//j是子列右端位置 A[j]
{
ThisSum += A[j];//对于相同的i,不同的j,只需要在j-1次循环的基础上累加一次即可
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}//j循环结束
}//i循环结束
return MaxSum;
}
复杂度分析:T(N)=O(N2) 两层嵌套
当一个程序的复杂度为(O(N2)时,应该想能否将复杂度改为
算法三:分而治之
分析: 分而治之
所谓“分而治之” 就是把一个复杂的算法问题按一定的“分解”方法分为等价的规模较小的若干部分,然后逐个解决,分别找出各部分的解,把各部分的解组成整个问题的解百度百科 https://www.jianshu.com/p/038352946ba3这篇文章讲的很好
如这个问题可以把它分解为最小,即两个元素。
//分成最小,返回最大值
int Max3(int A, int B,int C)
{
return A > B ? A > C ? A : C : B > C ? B : C;
// // 即A > B ? (A > C ? A : C) : (B > C ? B : C) ;
}
/*分治法*/
int DivideAndConquer(int List[], int left, int right)//这个函数的目的是求最大子列和
{
//分
//分解到最后剩一个数字时,停止,终止条件
if (List[left] > 0)
return List[left];
else
return 0;
//先找左右最大子列和
int center = (left + right) / 2;//找中心点
int MaxLeftSum = DivideAndConquer(List, left, center);//递归调用求最大子列和
int MaxRightSum = DivideAndConquer(List, center + 1, right);//记得加一
//再求跨分界线的最大子列和
//从边界出发向左找最大子列和
int MaxLeftBorderSum = 0;
int LeftBorderSum = 0;
for (int i = center; i >= left; i--)
{
/*中线向左扫描*/
LeftBorderSum += List[i];
if (LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
//从边界出发向右找最大子列和
int MaxRightBorderSum = 0;
int RightBorderSum = 0;
for (int i = center + 1; i <= right; i++)
{
/*中心线向右扫描*/
RightBorderSum += List[i];
if (RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
//治
//返回三段最大值
return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubseqSum3(int List[], int N)
{
/* 重要:保持与前2种算法相同的函数接口 */
return DivideAndConquer(List, 0, N - 1);//数组与左右值
}
复杂度分析:
- T(N)为左右和中间三部分复杂度之和,只有一个元素时,复杂度为1,
- 左右子列也可以再次分解为左右子列,因此可以将1代入
即
- 一共可以展开k次,展开到(向上取整),即时(N是元素个数,当分到1时停止)
- 因为,所以,所以3为,两项在一起取比较大的一项,所以
算法四:在线处理
int MaxSubseqSum4(int A[], int N)
{
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for (i = 0; i < N; i++)
{
ThisSum += A[i];//向右累加
if (ThisSum > MaxSum)
MaxSum = ThisSum;//发现更大和则更新当前结果
else if (ThisSum < 0)//如果当前子列和为负
ThisSum = 0;//则不可能使当前子列和增大,故弃之
}
return MaxSum;
}
“在线”的意思是指每输入一个数剧就进行及时处理,在任何一个地方终止输入,算法都能正确给出当前的解。
参考参考
如上图,在线处理运行中的一个断点的状态。对于这个数组,最后得到的最大子列和为7(i=5与i=6的和)。
最后欢迎大家访问我的个人博客青蛙听禅的个人博客