1 概论
来源:互联网 发布:有没有画画软件 编辑:程序博客网 时间:2024/06/05 03:05
1 概论
1 引子
- 数据结构(Data Structure)的定义:计算机中存储、组织数据的方式。通常情况下,精心选择的数据结构可以带来最优效率的算法。
- 数据结构在计算机内部的存储:1、逻辑结构;2、物理存储结构
- 矩阵的抽象数据类型:
AM∗N=(aij) ,由MxN个三元集<a,i,j> 组成
ElementType GetEntry(Matrix A, int i, int j): //返回矩阵A的第i行第j列元素
- 为什么要抽象?
抽象可以过滤掉细节,只研究事物的某一个特定的角度,从而触及到问题的本质。
- 例子1:如何在书架上摆放图书?看规模!摆放书需要考虑:1、如何插入一本新书;2、如何查找到某一本指定的书。
- 1.随便放:新书插入很方便,但是找到某一本书很难!
- 2.按书名拼音字母顺序:二分查找!但是新书插入很难
- 3.划分区域,每个区域摆放一类图书,按照拼音顺序拜访:查找和插入都方便一些。但是事先需要给出很大的空间,否则可能导致没地方放。另外,类别分的粗和细又是一个问题。
- 例子2:实现PrintN函数:打印1~N的全部正整数
//循环实现void PrintN(int N){ int i; for(i = 1; i <= N; i++){ printf("%d\n", i); }}
//递归实现void PrintN(int N){ if (N){ PrintN(N-1); printf("%d\n", N); }}
对于递归实现,当N = 100000时,递归程序崩溃。因为递归函数对计算机空间占用很大。这说明,解决问题方法的效率和空间的利用率有关。
- 例子3:计算给定多项式
f(x)=a0+a1x+…+anxn 在点x处的值
double f(int n, double a[], double x){ int i; double p = a[0]; for(i = 1; i <= n; i++){ p += (a[i] * pow(x,i)); } renturn p;}
或者:
double f(int n, double a[], double x){ int i; double p = a[n]; for(i = n; i > 0; i--){ p = a[i-1] + x*p; } renturn p;}
第二个函数更好,计算效率更高。因此解决问题的效率和算法有关。
- clock()函数:捕捉程序开始运行到clock()被调用时耗费的时间。单位:clock tick, CLK_TCK:机器时钟每秒所走的时钟打点数。
#include<stdio.h>#include<time.h>clock_t starrt, stop; //clock_t是clock()函数返回的变量类型double duration; //以秒为单位int main(){ start = clock(); MyFunction(); stop = clock(); duration = ( (double)(stop - start) ) / CLK_TCK; return 0;}
2 算法
- 算法(Algorithm)的定义:一个有限的指令集,接受一些输入并产生输出,一定在有限的步骤之后终止。其中每一条指令有充分目标没有歧义,在计算机的处理范围内,不依赖于任何一种计算机语言以及具体的实现手段。
- 衡量算法好坏的指标:
- 1 空间复杂度S(n):占用存储单元的长度
- 2 时间复杂度T(n):执行耗费时间的长度
//递归实现void PrintN(int N){ if (N){ PrintN(N-1); printf("%d\n", N); }}
例如在递归算法中,假设我们需要输出PrintN(100000),那么函数首先需要调用PrintN(99999),于是100000需要存储在内存中,同样的,99999、99998……、1都需要存储,直到PrintN(0)可以输出。所以
而在循环算法中,只涉及到临时变量,所以不管N多大,占用的空间始终是固定的常量。
在求多项式的值的算法中,由于计算机对于加减法的计算效率要远高于乘除法,pow(x,i)函数中执行了i次乘法,于是乘法的总次数为
什么是好的算法?
- 最坏情况复杂度:
Tworst(n) - 平均复杂度:
Tavg(n) ——最关心的问题
- 最坏情况复杂度:
复杂度的渐进表示:
T(n)=O(f(n)) 表示存在常数C>0,n0>0 使得当n≥n0 时,T(n)≤Cf(n) T(n)=Ω(g(n)) 表示存在常数C>0,n0>0 使得当n≥n0 时,T(n)≥Cg(n) T(n)=Θ(h(n)) 表示同时有T(n)=O(h(n)) 和T(n)=Θ(h(n))
太大的上界和太小的下界是无意义的。
- 常见的算法时间复杂度由小到大依次为:
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<…<O(2n)<O(n!) - 若两段算法分别有复杂度
T1(n)=O(f1(n)) 、T2(n)=O(f2(n)) ,那么T1(n)+T2(n)=max(O(f1(n))+O(f2(n))) T1(n)∗T2(n)=O(f1(n)∗f2(n))
- 若
T(n) 是关于n的k阶多项式,那么T(n)=Θ(nk) - for循环的时间复杂度等于循环次数乘循环体代码的复杂度。
- if-else总体复杂度为三者中最大
3 最大子列和问题
最大子列和问题:给定N个整数的序列
- 算法1:暴力搜索,
T(N)=O(N3)
int MaxSubseqSum1( int A[], int N){ int ThisSum, MaxSum = 0; int i, j, k; 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:
T(n)=O(n2)
int MaxSubseqSum2( 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++){ //对于相同的i,不同的j,只在j-1次循环的基础上累加一项即可 ThisSum += A[j]; if (ThisSum > MaxSum) MaxSum = ThisSum; } } return MaxSum;}
- 算法3:分而治之,将序列在中间切分,左边有一个最大子列和,右边有一个最大子列和,跨越中间线的有一个最大子列和,取三者中最大的即可。
T(n)=O(nlogn) - 时间复杂度分析:
T(1)=O(1) ,左右两边问题为T(N/2) ,中间为O(N) ,所以T(N)=2T(N/2)+O(N) ,因此T(N)=O(N)+O(NlogN)=O(NlogN) - 空间复杂度分析:左右两部分空间复杂度为
S(N/2) ,跨边界空间复杂度为O(N) 。故整个空间复杂度S(N)=2S(N/2)+O(N)=O(NlogN)
- 时间复杂度分析:
int Max3( int A, int B, int C ){ /* 返回3个整数中的最大值 */ return A > B ? A > C ? A : C : B > C ? B : C;}int DivideAndConquer( int List[], int left, int right ){ /* 分治法求List[left]到List[right]的最大子列和 */ int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */ int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/ int LeftBorderSum, RightBorderSum; int center, i; if( left == right ) { /* 递归的终止条件,子列只有1个数字 */ if( List[left] > 0 ) return List[left]; else return 0; } /* 下面是"分"的过程 */ center = ( left + right ) / 2; /* 找到中分点 */ /* 递归求得两边子列的最大和 */ MaxLeftSum = DivideAndConquer( List, left, center ); MaxRightSum = DivideAndConquer( List, center+1, right ); /* 下面求跨分界线的最大子列和 */ MaxLeftBorderSum = 0; LeftBorderSum = 0; for( i=center; i>=left; i-- ) { /* 从中线向左扫描 */ LeftBorderSum += List[i]; if( LeftBorderSum > MaxLeftBorderSum ) MaxLeftBorderSum = LeftBorderSum; } /* 左边扫描结束 */ MaxRightBorderSum = 0; RightBorderSum = 0; for( 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 );}
- 算法4:在线处理,
T(N)=O(N) 。“在线”:每输入一个数据就进行即时处理,在任何一个地方终止输入,算法都能给出正确的解。
int MaxSubseqSum4( int A[], int N){ int ThisSum = 0, MaxSum = 0; int i; for(i = 0; i < N; i++){ ThisSum += A[i]; if (ThisSum > MaxSum) MaxSum = ThisSum; else if(ThisSum < 0) //如果当前子列和为负,不可能通过累加后面的数达到最大。 ThisSum = 0; } return MaxSum;}
- 概论1
- 1-概论
- 1 概论
- 组合算法概论(1)
- 计算机系统概论(1)
- 操作系统(1)概论
- 1-oracle-概论
- 机器学习方法概论1
- 数据结构 1____概论
- 1 - 统计学习方法概论
- 密码学概论(1)
- javaScript--概论(1)
- 数据结构1--概论
- <统计学习方法>1 概论
- 概论
- 概论
- 概论
- 1数据库管理系统概论
- HTML5功能案例
- HDOJ 1506 Largest Rectangle in a Histogram(单调栈)
- 自学笔记の函数的参数传递
- swagger响应信息码枚举设置方式
- 【LeetCode】Two Sum II
- 1 概论
- 运算符重载
- Android CTS 问题分析
- Navicat for mysql 导入数据
- 2017校赛题解
- py-faster-rcnn安装与配置
- Vue组件开发实践
- Angular2环境配置
- Service 与 Thread 的区别与应用场景理解