数据结构基本概念——慕课笔记
1.1什么是数据结构
1.1.1时钟打点的概念
- 常数CLK_TCK(或CLOCKS_PER_SEC):机器时钟每秒所走的时钟打点数
- clock():捕捉从程序开始运行到clock()被调用时所耗费的时间。这个时间单位是clock tick,即“时钟打点”。
#include <stdio.h>
#include <time.h>//使用clock相关函数需包含的头文件
clock_t start, stop;
//clock_t是clock()函数返回的变量类型
double duration;
//记录被测函数运行时间,以秒为单位
int main ()
{
start = clock();//开始计时
MyFunction();//被测函数
stop = clock();//停止计时
duration = ((double)(stop - start))/CLK_TCK;//计算运行时间(开始与结束的时钟打点之差除时钟每秒所走的时钟打点数)
return 0;
}
1.3应用实例:最大子列和问题
1.3.1最大子列和问题
- 问题
- 算法1(T(N)=O(N^3))
int MaxSubseqSum1(int A[] , int N){
int ThisSum,MaxSum = 0;
int i ,j ,k;
for ( i = 0 ; i < N ; ++i){
//i是子列最左端的位置
for ( j = 0 ; j < N ; ++j){
//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;
}
- 算法2(T(N)=O(N^2))
int MaxSubseqSum1(int A[] , int N){
int ThisSum,MaxSum = 0;
int i ,j ,k;
for ( i = 0 ; i < N ; ++i){
//i是子列最左端的位置
ThisSum = 0//每轮循环(即每次移动左端点)ThisSum均需归零
for ( j = i ; j < N ; ++j){
//j是子列最右端的位置
ThisSum += A[j]; //递推公式 f(i,j) =f(i,j-1)+A[j]
if (ThisSum > MaxSum) //新得到的子列和更大
MaxSum = ThisSum;//更新结果
}//循环j结束
}//循环i结束
return Maxsum;
}
-
算法三 分治(T(N)=O(NlogN))
-
思想:先分后治,先将大问题拆分成小的子问题,递归解决子问题的子问题从而合并几个子问题的结果解决整个问题(例如序列有八个元素a[0]-a[7])
- 先分:不断地将数列二分又二分(递归的过程)直至数列只有一个元素停止递归,求出该子问题的结果,接着以该结果返回上一步的递归求出上一步的结果,最后求解整个问题的结果
- 后治:对于每个拆分的子列,先求中线两边的子列的最大和,再求跨越中线的子列的最大和
- 流程图
-
int Max3( int A, int B, int C )
{
/* 返回3个整数中的最大值 */
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 )
{
/* 分治法求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];//正数返回该子列数值,负数则为0
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, 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;
}