數(shù)據(jù)結(jié)構(gòu)分享

數(shù)據(jù)結(jié)構(gòu)學習

數(shù)據(jù):是描述客觀事物的符號,是計算機中可以操作的對象,是能被計算機識別,并輸入給計算機處理的符號集合。balabala 這都是概念啦,可能大家都忘了,沒關(guān)系,反正這次分享跟這個概念也每個鳥關(guān)系。

不過能雖然有些概念還是比較坑爹的沒用,有些大家還是要關(guān)注一下

邏輯結(jié)構(gòu)

集合結(jié)構(gòu)

線性結(jié)構(gòu)

樹形結(jié)構(gòu)

圖形結(jié)構(gòu)

物理結(jié)構(gòu)

順序存儲結(jié)構(gòu)

鏈式存儲結(jié)構(gòu)

Q1 :ok 那么大家認為邏輯結(jié)構(gòu)中每個元素的對應(yīng)關(guān)系是什么呢?

Q2:思考一下,順序存儲結(jié)構(gòu)和鏈式存儲結(jié)構(gòu)的區(qū)別

數(shù)據(jù)結(jié)構(gòu)最最最最最基本的東西就這點

那么說數(shù)據(jù)結(jié)構(gòu)肯定會涉及到算法這個東西

算法的概念我就不跟大家說了,就跟大家說說算法的時間復雜度和空間復雜度吧

默認的我們把算法的時間復雜度 叫做復雜度

時間復雜度,我們用大O階來表示

現(xiàn)在舉一個例子

假如我現(xiàn)在要求設(shè)計一個 從0加到n的算法

有兩種方法

int sumSilly(int n) {
    int sum = 0;  //1
    for(int i =0; i <= n; ++ i) { //n +1
        sum += i //n
    }
    return sum /1
}

int sumSmart(int n) {
    int sum = n * (n + 1) / 2; //1
    return sum // 1
}

void main() {
    int result1 = sumSilly(100)
    
    int result2 = sumSmart(100)
}

現(xiàn)在我們來計算一下兩種方法分辨計算了幾次

silly: 1+(n+1)+n +1 = 2n + 3

smart: 1+1 = 2

顯而易見的大家都覺得第二種方法比較好,大O階推到出來的話,一個是O(n) 一個是 O(1),我們一推到大O的時候會把常數(shù)的舍去,因為當n無窮大的時候常數(shù)對結(jié)果的影響就會非常的小。

大家能寫一些如:線性階O(1),對數(shù)階O(log n),指數(shù)階O(n^m)的算法么

時間復雜度的大小關(guān)系,就跟o里面那個大小關(guān)系有關(guān)(前提n很大)

以下的n為變量,m為常量(n很大)

1 < logn < n < nlogn < n^m < m^n < n! < n^n

扯淡扯完了,下一個

線性表,有兩種,順序表和鏈表,對應(yīng)的就是順序存儲和鏈式存儲
可能有些小伙伴表示好奇,靜態(tài)鏈表去哪了,靜態(tài)鏈表是一個特殊的東西但是也算是鏈表吧我覺得,長度固定的鏈表么。我說的長度是最大長度。

鏈表里面又有循環(huán)鏈表,單鏈表,雙向鏈表。
那么單鏈表的定義

typedef struct Node {
    ElemType data;
    struct Node* next;
} Node,*DuLinkList;

插入p到s之后:

p.next = s.next;
s.next = p;

刪除p后面的一個節(jié)點s

p.next = s.next
free(s)

Q3:雙向鏈表怎么刪除節(jié)點p,插入節(jié)點p到s后面呢?
雙向鏈表的定義如下

typedef struct DulNode {
    ElemType data;
    struct DulNode* prior;
    struct DulNode* next;
}DulNode,*DuLinkList;

棧和隊列

棧這個東西是先入后出的誰都知道吧,那考考大家

如果我入棧1,2,3
那么下面不可能被輸出的是哪些

123,213,321,132,231,312

只能在表尾插入和刪除,結(jié)構(gòu)定義如下

typedef struct {
    SelemType data[MAXSIZE];
    int top;    
}SqStack;

插入操作

status Push(SqStack *s,SelemType e) {
    if  (s->top == MAXSIZE -1) {
        return error; 
    }
    s->top ++;
    s->data[s->top] = e;
    return ok;
}

刪除操作

status Pop(SqStack *s,SelemType e) {
    if  (s->top == -1) {
        return error;
    }
    *e = s->data[s->top];
    s->top--;
    return ok;
}

棧的空間是事先確定好的,如果小了就需要擴充,而且事先申明打了么,又浪費。

大家還記不記得 小時候的三八線。

這個三八線,我們用計算機的思維,就是兩棧共享空間,那么我們來看一下定義

typedef struct {
    SelemType data[MAXSIZE];
    int top1;   
    int top2;
}SqStack;

插入操作

status Push(SqStack *s,SelemType e,int stacNumber)  {
    if  (s->top1 + 1  == s->top2) {
        return error; 
    }
    if  (stackNumber == 1){
        s->data[++s->top1] = e
    } else if (stackNumber == 2) {
        s->data[--s->top2] = e
    }
    return ok;
}

刪除操作

status Pop(SqStack *s,SelemType e,int stacNumber) {
    if (stackNumber == 1) {
        if (s->top1 == -1) {
            return error;
        }
        *e = s->data[s->top1 --];
    }
    if (stackNumber == 2) {
        if (s->top2 == MAXSIZE) {
            return error;
        }
        *e = s->data[s->top2 ++];
    }
    return ok;
}

棧的應(yīng)用-----遞歸,遞歸就要說

斐波那契數(shù)列

如果兔子出生兩個月之后可以生育,兔子成熟后每個月可以生一對兔子,假設(shè)兔子不會死,求n個月能有幾對兔子

第一個月  1
第二個月  1
第三個月  2
第四個月  3
第五個月  5
第六個月  8

這個數(shù)列的規(guī)律是什么? 前兩項之和等于第三項

int fbi(int i) {
    if (i < 2) {
        return i ==0 ? 0 : 1;
    }
    return fbi(i -1) + fbi (i - 2);
}

Q4.漢諾塔最優(yōu)算法的實現(xiàn)?

var hanoi = function (disc, src, aux, dst) {
        if (disc > 0) {
            hanoi(disc - 1, src, dst, aux);
            document.writeln('Move disc ' + disc + ' from ' + src + ' to ' + dst);
            hanoi(disc - 1, aux, src, dst);
        }
    }

棧的應(yīng)用----后綴表達式

9 + (3 - 1) * 3 + 10 / 2
答案是多少

·

·

·

·

·

·

·

·

然而我并不想知道算出來的結(jié)果,我只是用這個來告訴大家這個后綴表達式咋整

9 3 1 - 3 * 10 2 / +

然后現(xiàn)在來看計算機怎么算的

隊列

先入先出嘛。

說說循環(huán)隊列

typedef struct {
    QelemType data[MAXSIZE];
    int front;
    int rear;
}SqQueue;

隊列為空的時候我們定義為

q->front = q->rear;

那么滿的時候呢?

(q->rear + 1)%MAXSIZE = q->front

那么入隊和出隊操作就好搞了

status en(SqQueue *q.QelemType e) {
    if  ((q->rear + 1)%MAXSIZE == q->front) {
        return error;
    }
    q->data[q->rear] = e;
    q->rear = (q->rear + 1) % MAXSIZE;
    return ok;
}

status de(SqQueue *q.QelemType e) {
    if  (q->front == q->rear) {
        return error;
    }
    *e = q->data[q->front];
    q->front = (q->front+1)%MAXSIZE
    return ok
}

二叉樹

二叉樹有幾個性質(zhì),不管是考試還是面試都會考到

在二叉樹的第i層最多有幾個節(jié)點

深度為k的二叉樹最多有幾個節(jié)點

具有n個節(jié)點的二叉樹深度為?【log2n】 + 1

如果一個n個節(jié)點的完全二叉樹,那么
i=1 這個是跟節(jié)點

i>1 那么他的雙親節(jié)點是 i/2

2i> n 無左子節(jié)點 否則就是左節(jié)點

2i+1 >n 無右子節(jié)點 否則就是右節(jié)點

對于任意一個二叉樹T,如果其終端節(jié)點數(shù)為n0,度為2的節(jié)點數(shù)為n2,則n = n2 +1

二叉樹的遍歷方法

前序遍歷

先訪問根節(jié)點,后訪問左子樹,然后右子樹

void PreOrderTraverse(BiTree t) {
    if  (t == null) {
        return;
    }
    printf("%c",t->data);
    PreOrderTraverse(t->lchild)
    PreOrderTraverse(t->rchild)
}

中序遍歷

先訪問左子樹,然后跟,然后右子樹

void InOrderTraverse(BiTree t) {
if  (t == null) {
    return;
}
    InOrderTraverse(t->lchild);
    printf("%c",t->data);
    InOrderTraverse(t->rchild);
}

后序遍歷

先左后右在跟

void PostOrderTraverse(BiTree t) {
if  (t == null) {
    return;
}
    PostOrderTraverse(t->lchild);
    PostOrderTraverse(t->rchild);
    printf("%c",t->data);
}

層序遍歷

跟開始 從左到右

Q5.推導一下,前序遍歷是ABCDEF,中序遍歷CBAEDF,二叉樹是啥

樹轉(zhuǎn)換成二叉樹

1、加線。兄弟之間加線
2、去線。去掉和除了第一個孩子的線
3、層次調(diào)整。自己的孩子就是左孩子,兄弟就是右孩子

森林轉(zhuǎn)二叉樹

1、先變成二叉樹
2、第一個不變,后面的每個變成前一個的右子樹

二叉樹轉(zhuǎn)樹

倒一下

二叉樹轉(zhuǎn)森林

倒過來

霍夫曼樹

加權(quán)路徑最小的二叉樹就是霍夫曼樹

0-59  5%
60-69 15%
70-79 40%
80-89 30%
90-100 10%

算一下霍夫曼樹

霍夫曼編碼

BADCADFEED

A27
B8
C15
D15
E30
F5
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容