1 概论

1 概论


1 引子

  • 数据结构(Data Structure)的定义:计算机中存储、组织数据的方式。通常情况下,精心选择的数据结构可以带来最优效率的算法。
  • 数据结构在计算机内部的存储:1、逻辑结构;2、物理存储结构
  • 矩阵的抽象数据类型: AMN=(aij) ,由MxN个三元集 <a,i,j> <script type="math/tex" id="MathJax-Element-2"> </script>组成
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;
}

  或者: f(x)=a0+x(a1+x())

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)可以输出。所以 S(N)=CN
  而在循环算法中,只涉及到临时变量,所以不管N多大,占用的空间始终是固定的常量。
  在求多项式的值的算法中,由于计算机对于加减法的计算效率要远高于乘除法,pow(x,i)函数中执行了i次乘法,于是乘法的总次数为 (n2+n)/2 。而第二个算法中,一共执行了n次乘法。因此 T1(n)=C1n2+C2n C1,C2 分别为乘除法和加减法的复杂度。 T2(n)=Cn

  • 什么是好的算法?

    • 最坏情况复杂度: Tworst(n)
    • 平均复杂度: Tavg(n) ——最关心的问题
  • 复杂度的渐进表示:

    • T(n)=O(f(n)) 表示存在常数 C>0,n0>0 使得当 nn0 时, T(n)Cf(n)
    • T(n)=Ω(g(n)) 表示存在常数 C>0,n0>0 使得当 nn0 时, 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个整数的序列 {A1,A2,,AN} ,求函数 f(i,j)=max{0,ijAk} 的最大值。

  • 算法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;
}
本项目是一个基于SSM(Spring+SpringMVC+MyBatis)框架和Vue.js前端技术的大学生第二课堂系统,旨在为大学生提供一个便捷、高效的学习和实践平台。项目包含了完整的数据库设计、后端Java代码实现以及前端Vue.js页面展示,适合计算机相关专业的毕设学生和需要进行项目实战练习的Java学习者。 在功能方面,系统主要实现了以下几个模块:用户管理、课程管理、活动管理、成绩管理和通知公告。用户管理模块支持学生和教师的注册、登录及权限管理;课程管理模块允许教师上传课程资料、设置课程时间,并由学生进行选课;活动管理模块提供了活动发布、报名和签到功能,鼓励学生参与课外实践活动;成绩管理模块则用于记录和查询学生的课程成绩和活动参与情况;通知公告模块则实时发布学校或班级的最新通知和公告。 技术实现上,后端采用SSM框架进行开发,Spring负责业务逻辑层,SpringMVC处理Web请求,MyBatis进行数据库操作,确保了系统的稳定性和扩展性。前端则使用Vue.js框架,结合Axios进行数据请求,实现了前后端分离,提升了用户体验和开发效率。 该项目不仅提供了完整的源代码和相关文档,还包括了详细的数据库设计文档和项目部署指南,为学习和实践提供了便利。对于基础较好的学习者,可以根据自己的需求在此基础上进行功能扩展和优化,进一步提升自己的技术水平和项目实战能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值