数据结构学习笔记(一)(基本定义,最大子列和)

本笔记来自于浙大陈越,何钦铭老师https://www.bilibili.com/video/av55114968?p=1

一、数据结构

1.到底什么是数据结构?

  • 数据对象在计算机中的组织方式
  • 逻辑结构
  • 物理存储结构
  • 数据对象必定与一系列加在其上的操作相关联
  • 完成这些操作所用的方法就是算法
  • 2.抽象数据类型(Abstarct Data Type)

    • 数据类型
    • 数据对象集
    • 数据集合相关联的操作集
    • 抽象:描述数据类型的方法不依赖于具体实现
    • 与存放数据的机器无关
    • 与数据存储的物理结构无关
    • 与实现操作的算法和编程语言均无关

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

    二、算法(Algorithm)

    1.定义

    • 一个有限指令集
    • 接受一些输入(有些情况下不需要输入)
    • 产生输出
    • 一定在有限步骤之后终止
    • 每一条指令必须:
    • 有充分明确的目标,不可以有歧义
    • 计算机能处理范围内
    • 描述应不依赖于任何一种计算机语言以及具体的实现手段

    2.评价算法好坏

    1. 空间复杂度 S(n) ——————根据算法写成的程序在执行时占用存储空间的长度
    2. 时间复杂度 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},求函数f(i,j)=max0,k=ijAkf(i,j)=max({ 0,\sum_{k=i}^j A_k})的最大值。(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)时,应该想能否将复杂度改为O(nlogn)O(nlogn)

    算法三:分而治之

    分析: 分而治之
    所谓“分而治之” 就是把一个复杂的算法问题按一定的“分解”方法分为等价的规模较小的若干部分,然后逐个解决,分别找出各部分的解,把各部分的解组成整个问题的解百度百科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);//数组与左右值
    }
    

    复杂度分析:在这里插入图片描述

    1. T(N)为左右和中间三部分复杂度之和,只有一个元素时,复杂度为1,T(1)=O(1)T(1)=O(1)
      T(N)=2T(N/2)+cNT(N)=2T(N/2)+cN
    2. 左右子列也可以再次分解为左右子列,因此可以将1代入
      T(N)=2(2T((2T(N/2)+cN)/2)+cNT(N)=2(2T((2T(N/2)+cN)/2)+cN

      T(N)=2(2T(N/22)+cN/2)+cNT(N)=2(2T(N/2^2)+cN/2)+cN
      T(N)=22T(N/22)+2cNT(N)=2^2T(N/2^2)+2cN
    3. 一共可以展开k次,展开到N/2k=1N/2^k = 1(向上取整),即T(N/2k)=O(1)T(N/2^k) = O(1)时(N是元素个数,当分到1时停止)
      T(N)=2kO(1)+ckNT(N)=2^kO(1)+ckN
    4. 因为N/2k=1N/2^k = 1,所以k=log2Nk=log_2N,所以3为T(N)=2log2N+c(log2N)NT(N)=2log_2N+c(log_2N)N,两项在一起取比较大的一项,所以
      T(N)=O(Nlog2N)T(N)=O(Nlog_2N)

    算法四:在线处理

    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;
    }
    

    T(N)=O(N)T(N)=O(N)
    在线”的意思是指每输入一个数剧就进行及时处理,在任何一个地方终止输入,算法都能正确给出当前的解。
    在这里插入图片描述参考参考
    如上图,在线处理运行中的一个断点的状态。对于这个数组,最后得到的最大子列和为7(i=5与i=6的和)。

     
    最后欢迎大家访问我的个人博客青蛙听禅的个人博客

    發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章