1. 程式人生 > >聽課筆記---資料結構(浙江大學)MOOC---第一週

聽課筆記---資料結構(浙江大學)MOOC---第一週

第一週:預備知識—演算法與資料結構

解決問題的策略效率與什麼有關?

  1. 資料組織方式

    圖書館插入新書和查詢書籍,用不同的資料組織方式,對應的操作就會有不同的時間複雜度。

    資料組織方式好壞的分析,與它所相關的操作有直接聯絡。

  2. 空間利用效率

    觀察兩段用於輸出1到N的數字的程式碼

    其一迴圈演算法:

    void PrintN(int N)
    {
    int i;
    for (i = 1; i <= N; i++) {
        printf("%d\n", i);
       }
    return;
    }

    其二遞迴演算法:

    void PrintN(int N)
    {
    if (N) {
        PrintN(N - 1
    ); printf("%d\n", N); } return; }

    第二段程式碼在資料過大時由於不斷堆疊,空間爆炸,異常終止。

  3. 演算法巧妙程度

    計算多項式在給定點x處的值,兩段程式碼

    其一:f(x)=a0+a1x++an1xn1+anxn

    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));
       }
    return p;
    }

    其二;f(x)=a0+x(a1+x((an1+x(an

    ))))

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

    兩段程式碼在執行時間上有很大區別,究其原因,將乘法視為關鍵操作,冪次視作多次乘法,則第一段程式碼演算法O(n2), 第二段程式碼O(n).

測試(比較)演算法執行效率小方法
#include<stdio.h>
#include<time.h>
#include<math.h>
#define MAX 100 double poly1(int n, double a[], double x) // 坊間演算法 { double p = 0; for (int i = 0; i <= n; i++) { p += a[i] * pow(x, i); } return p; } double poly2(int n, double a[], double x) // 專業演算法 { double p = a[0]; for (int i = n; i > 0; i--) { p += a[i - 1] + p * x; } return p; } int main() { double coef[MAX], x = 1.1; clock_t start, stop; for (int i = 0; i < MAX; i++) { coef[i] = i / 3.0; } start = clock(); for (int i = 0; i < MAX; i++) poly1(MAX - 1, coef, x); stop = clock(); printf("Polynomial Algorithm 1---plain Algorithm\n" "used ticks: %f\n\n", (double)(stop - start)); start = clock(); for (int i = 0; i < MAX; i++) poly2(MAX - 1, coef, x); stop = clock(); printf("Polynomial Algorithm 2---professional Algorithm\n" "used ticks: %f\n\n", (double)(stop - start)); return 0; }

資料結構與抽象資料結構

資料結構

  • 資料物件在計算機中的組織方式
    • 邏輯結構
    • 物理儲存結構
  • 資料物件與施加於其上的操作相關聯
  • 實現操作的就是演算法

抽象資料結構

  • 資料結構
    • 資料物件集
    • 與資料物件集相關的操作集
  • 抽象
    • 與物理儲存方式無關
    • 與實現的演算法和程式語言無關

只定義了物件集和操作集,不涉及如何實現。如下例:1

演算法與最大子列和問題

熟悉問題,只敲了程式碼,在QuizCode裡有

初識PTA

PTA是資料結構這門課採用的OJ系統。

  • 會將各個樣例的特徵標在Wrong Answer的標籤旁邊,便於Debug。
  • 函式填空題會將一些部分隱藏起來,因此看上去沒有不代表沒有,需仔細讀題。

最大子列和(課堂演算法)程式碼

課上講的演算法O(nlogn)實現

#include<stdio.h>
#include<stdlib.h>      // for malloc

int MaximumSubSum(int Data[], int s, int e);    // 尋找最大和
int max(int a, int b, int c);
int MaxCrossing(int Data[], int s, int e);      // Conquer 尋找跨越中間元素的最大和

int main()
{
    int * Data = NULL, Size = 0;

    scanf("%d", &Size);
    Data = malloc(sizeof(int) * Size);          // 動態分配記憶體

    for (int i = 0; i < Size; i++) {
        scanf("%d", &Data[i]);
    }
    int Max = MaximumSubSum(Data, 0, Size - 1); // 尋找
    printf("%d\n", Max);

    return 0;
}

int MaximumSubSum(int Data[], int s, int e)
{
    if (s == e) {                               // Base Case 只有一個元素
        if (Data[s] <= 0) {
            return 0;
        }
        else {
            return Data[s];
        }
    }

    int mid = (s + e) / 2;
    int Max1 = MaximumSubSum(Data, s, mid);
    int Max2 = MaximumSubSum(Data, mid + 1, e); // Divide, 遞迴求解
    int Max3 = MaxCrossing(Data, s, e);         // Conquer

    return max(Max1, Max2, Max3);
}

int max(int a, int b, int c)
{
    int result = a;
    if (result < b) {
        result = b;
    }
    if (result < c) {
        result = c;
    }

    return result;
}

int MaxCrossing(int Data[], int s, int e)
{
    int mid = (s + e) / 2, maxl = 0, maxr = 0, curr = 0;

    for (int i = mid; i >= s; i--) {    // 左側最大
        curr += Data[i];
        if (curr > maxl) {
            maxl = curr;
        }
    }

    curr = 0;       // initialize
    for (int i = mid + 1; i <= e; i++) {// 右側最大
        curr += Data[i];
        if (curr > maxr) {
            maxr = curr;
        }
    }

    return maxl + maxr;                 // 和為最大
}

演算法O(n)實現

#include<stdio.h>

int main()
{
    int Size = 0, max = 0, curr = 0, Data = 0; // O(1) extra space

    scanf("%d", &Size);
    for (int i = 0; i < Size; i++) {
        scanf("%d", &Data);
        curr += Data;
        if (curr > max) {                       // refresh max
            max = curr;
        }
        if (curr < 0) {                         // discard curr
            curr = 0;
        }
    }

    printf("%d\n", max);

    return 0;
}

最大子列和(課後習題)程式碼

O(n)實現

#include<stdio.h>
#include<stdlib.h>  // for malloc

int main()
{
    int Bool = 0, Size = 0, max = 0, curr = 0, curr_s = 0, curr_e = 0, s = 0, e = 0;
    int * Data; // O(n) extra space

    scanf("%d", &Size);
    Data = malloc(sizeof(int) * (Size + 1));
    for (int i = 0; i < Size; i++) {
        scanf("%d", &Data[i]);
    }
    curr_s = curr_e = s = e = Data[0];
    for (int i = 0; i < Size; i++) {
        curr += Data[i];
        curr_e = Data[i];
        if (curr < 0) {
            curr = 0;
            curr_s = Data[i + 1];
        }
        if (curr > max) {
            max = curr;
            s = curr_s;
            e = curr_e;
        }
    }
    if (max == 0) {         // 為0特判
        s = Data[0];
        e = Data[Size - 1];
        for (int i = 0; i < Size; i++) {
            if (Data[i] == 0) {
                s = e = 0;
                break;
            }
        }
    }
    printf("%d %d %d\n", max, s, e);

    return 0;
}