數據結構學習筆記(一)(基本定義,最大子列和)

本筆記來自於浙大陳越,何欽銘老師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的和)。

     
    最後歡迎大家訪問我的個人博客青蛙聽禪的個人博客

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