數(shù)據(jù)結(jié)構(gòu)學習
數(shù)據(jù):是描述客觀事物的符號矮台,是計算機中可以操作的對象乏屯,是能被計算機識別,并輸入給計算機處理的符號集合嘿架。balabala 這都是概念啦瓶珊,可能大家都忘了,沒關系耸彪,反正這次分享跟這個概念也每個鳥關系伞芹。
不過能雖然有些概念還是比較坑爹的沒用,有些大家還是要關注一下
邏輯結(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)中每個元素的對應關系是什么呢蝉娜?
Q2:思考一下唱较,順序存儲結(jié)構(gòu)和鏈式存儲結(jié)構(gòu)的區(qū)別
數(shù)據(jù)結(jié)構(gòu)最最最最最基本的東西就這點
那么說數(shù)據(jù)結(jié)構(gòu)肯定會涉及到算法這個東西
算法的概念我就不跟大家說了,就跟大家說說算法的時間復雜度和空間復雜度吧
默認的我們把算法的時間復雜度 叫做復雜度
時間復雜度召川,我們用大O階來表示
現(xiàn)在舉一個例子
假如我現(xiàn)在要求設計一個 從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)的算法么
時間復雜度的大小關系概疆,就跟o里面那個大小關系有關(前提n很大)
以下的n為變量,m為常量(n很大)
1 < logn < n < nlogn < n^m < m^n < n! < n^n
扯淡扯完了峰搪,下一個
線性表岔冀,有兩種,順序表和鏈表概耻,對應的就是順序存儲和鏈式存儲
可能有些小伙伴表示好奇使套,靜態(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;
}
棧的應用-----遞歸北秽,遞歸就要說
斐波那契數(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);
}
}
棧的應用----后綴表達式
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