数据结构与算法 第一章 引入

第一章 引入

1.1 什么是数据结构

  • 解决问题方法的效率,和数据的组织方式有关。
  • 递归函数在处理大量数据时,会大量占用空间。这体现了解决问题方法的效率,跟空间的利用效率有关。
  • 解决问题方法的效率,与算法的巧妙程度有关。

一、数据结构是数据对象在计算机中的组织方式

1. 数据对象的逻辑结构
  • 线形结构(一对一)
  • 树(一对多)
  • 图(多对多)
2. 数据对象在计算机中的物理存储结构
  • 数组
  • 链表 等
3. 描述数据对象的方法 —— 抽象数据类型
(1)数据类型
  • 数据对象集

  • 数据集合相关联的操作集

    注意:在C语言中,这两个分开处理;但在面向对象的编程语言中,这两个会在同一个类(class)中出现

(2) 抽象:描述数据类型的方法不依赖于具体实现

对数据类型的描述:

  • 与存放数据的机器无关

  • 与数据存储的物理结构无关

  • 与是实现操作的算法和编程语言无关

    注意:我们只描述数据对象集和相关操作集 “是什么” ,并不涉及 “如何做到” 的问题

二、数据对象必定与一系列加在其上的操作相关联

三、完成这些操作所用的方法是算法


1.2 什么是算法

一、算法

  • 是一个有限指令集

  • 接收一些输入,有时候不需要输入

  • 一定产生输出

  • 一定在有限步骤后终止

  • 其中的每一条指令必须:

    • 有明确的目标,不可以有歧义
    • 在计算机能处理的范围之内
    • 算法描述应该抽象,不依赖与任何一种计算机语言,以及具体的实现手段

二、什么是好的算法

1. 空间复杂度 S(n) :程序在执行时占用存储单元的长度
  • 占用存储单元的长度,与输入数据的规模有关
  • 空间复杂度过高的算法可能导致使用的内存超限,从而造成程序的中断
2. 时间复杂度 T(n) :程序在执行时耗费时间的长度
  • 耗费时间的长度与输入数据的规模有关
  • 时间复杂度过高的低效算法可能导致在规定时间内等不到运行结果
3. 在分析一般算法的效率时,一般关注下面两种复杂度
  • 最坏情况复杂度 Tworst(n):一般通过该标准比较算法间的优劣
  • 平均复杂度 Tavg(n)
  • Tavg(n) <= Tworst(n)

三、复杂度的渐进表示法

1. 复杂度的渐进表示

在这里插入图片描述

  • 在上图中,我们可以看出,f(n)T(n)的某种上界,g(n)T(n)的某种下界
  • 为了保证能够准确地、有意义地分析算法,一般选取上界中最大的、下界中最小的
2. 复杂度分析小窍门
  • 若两段算法分别有复杂度T1(n) = O(f1(n))T2(n) = O(f2(n)),则

    • T1(n) + T2(n) = max(O(f1(n)), o(f2(n)))
    • T1(n) x T2(n) = O(f1(n) x f2(n))
  • T(n)是关于nk阶多项式,则T(n) = θ(n^k)

    • 考虑最高阶的一项即可,可以忽略其他的项
  • 一个 for 循环的时间复杂度 = 循环次数 x 循环体代码的复杂度

  • if-else 结构的复杂度 = 在各种分情况下的复杂度中最大的复杂度


1.3 应用实例:求连续最大子列和问题

  • 题目:给定N个整数的序列 { A 1 , A 2 , . . . , A N } \{ A_1, A_2, ... , A_N \} {A1,A2,...,AN},求函数 f ( i , j ) = m a x { 0 , ∑ k = i j } f(i, j) = max\{0, \sum_{k=i}^j\} f(i,j)=max{0,k=ij} 的最大值

  • 注意:再计算子列和的过程中,参与计算的元素必须前后相邻。

算法1:

/*
 * 输入:int 	A[] 	整数序列
 * 输出:int 	N		整数序列中元素的个数
 */
int MaxSubseqSum1 (int A[], int N) {
    int ThisSum, MaxSum = 0;
    int i, j, k;
    
    /* 遍历所有子列和 */
    for (i=0; i<N; i++) { /* i是子列左端位置。此行选定i */
        for (j=i; j<N; j++) { /* j是子列右端位置。此行选定j */
            ThisSum = 0; /* ThisSum是从A[i]到A[j]的子列和 */
            for (k=i; k<=j; k++) { /* A[k]的起点是A[i], 终点是A[j] */
                ThisSum += A[k];	/* 前面选定了i和j, 接下来计算出这一段子列和 */
            }
            if (ThisSum > MaxSum) { /* 如果刚得到的这个子列和更大 */
                MaxSum = ThisSum;	/* 则更新结果 */
            }
        } /* j循环结束 */
    } /* i循环结束 */
    
    return MaxSum;
}
  • 算法1复杂度: T ( N ) = O ( N 3 ) T(N)=O(N^3) T(N)=O(N3)
    有三层循环,每一层循环的循环变量都会取到最大值N

算法2:

int MaxSubseqSum2 (int A[], int N) {
    int ThisSum, MaxSum = 0;
    int i, j;
    for (i=0; i<N; i++) { /* i是子列左端位置。此行选定i */
        ThisSum = 0; /* ThisSum是从A[i]到A[j]的子列和 */
        for (j=i; j<N; j++) { /* j是子列右端位置。此行选定j */
            ThisSum += A[j]; /* i相同,j不同时,只要在j-1次循环的基础上累加一项即可 */
            if (ThisSum > MaxSum) { /* 如果刚得到的这个子列和更大 */
                MaxSum = ThisSum; 	/* 则更新结果 */
            }
        } /* j循环结束 */
    } /* i循环结束 */
    return MaxSum;
}
  • 算法2复杂度: T ( N ) = O ( N 2 ) T(N) = O(N^2) T(N)=O(N2)
    只有两层循环,每层循环的循环变量最大取N

算法3:运用 “分而治之” 思想,采取递归的方法

static int MaxSubSum (const int A[], int Left, int Right) {
    int MaxLeftSum, MaxRightSum;
    int MaxLeftBorderSum, MaxRightBorderSum;
    int LeftBorderSum, RightBorderSum;
    int Center, i;
    
    if (Left == Right) {
        if (A[Left] > 0) {
            return A[Left];
        } else {
            return 0;
        }
    }
}
  • 算法3复杂度:
    整体复杂度为 T ( N ) = 2 T ( N / 2 ) + c N T(N)=2T(N/2)+cN T(N)=2T(N/2)+cN,且 T ( 1 ) = O ( 1 ) T(1)=O(1) T(1)=O(1)
    递推式子(展开等号右边的 T ( . ) T(.) T(.)项),最终, T ( N ) = 2 k O ( 1 ) + c k N T(N)=2^kO(1)+ckN T(N)=2kO(1)+ckN,其中, N / 2 k = 1 N/2^k=1 N/2k=1
    进一步化简得, T ( N ) = O ( N ⋅ l o g N ) T(N)=O(N·logN) T(N)=O(NlogN)
  • 算法3算法抽象化
    表1
    左部分1
    左部分2
    右部分1
    右部分2
    4
    -3
    5
    -2
    -1
    2
    6
    -2

在左部分1中,从右往左,第一步取-3,第二步取4-3=1,取最大值得1。左部分2,从左往右,第一步取5,第二步取5-2=3,取最大值得5;同理,右部分1从右往左、右部分2从左往右执行相应操作。最终得到如下表所示的第一轮子列和。

表2
左部分1
左部分2
右部分1
右部分2
1
5
2
6

左部分在1,5,1+5=6中取最大值,得6;右部分在2,6,2+6=8中取最大值,得8,得到如下表所示的第二轮子列和

表3
左部分
右部分
6
8

如下表所示

表4
左部分
右部分
4
-3
5
-2
-1
2
6
-2

左部分从右往左依次相加,最大值为-2+5+(-3)+4=4。右部分从左往右依次相加,最大值为-1+2+6=7。这就是第三轮子列和;在4,7,4+7=11中,得到第四轮子列和

四轮子列和中,11最大,所以,最终结果为11

算法4:在线处理算法

  • “在线”指,每输入一个数据就进行即时的处理,在任何地方种植输入,算法都能正确地给出当前的解。
int MaxSubseqSum4(int A[], int N) {
    int ThisNum, 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;	// 则此时不可能使后面的部分和增大,抛弃现在的子列和,从0再开始计算子列和
        }
    }
    return MaxSum;
}
  • 算法4复杂度: T ( N ) = O ( N ) T(N)=O(N) T(N)=O(N)
    整个函数只有一个for循环,每次循环中的if结构都是常数数量级的复杂度,故该算法的复杂度是线性的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值