Data structure Week1.Day6
樹形結構
1、樹的基本概念
是一種表示層次關係(一對多)的資料結構
有且僅有一個特定的節點、該節點沒有前驅、被稱為根節點
剩餘的n個節點互不相交的子集、其中的每個子集也都是一棵樹
注意:樹型結構具有遞迴性(樹中有樹)
2、樹的表示方法
倒懸樹、巢狀發、凹凸發
3、樹的專業術語
節點:組成樹的基礎元素、同時節點也可看做一棵樹
節點的度:該節點子樹的數量
樹的度(樹的密度):該樹所有節點的數量
樹的高度:樹的層數
樹的深度:樹的最大層次數
節點的層次:根節點的層次1 從1開始不是0
葉子節點:節點的度為0的節點
雙親和孩子:節點的子樹被稱為孩子節點、該節點就是孩子節點的雙親
兄弟:同一個雙親的節點之間兄弟相稱
堂兄弟:雙親是兄弟
祖先節點:從根節點出發到該節點的所有節點
子孫:一個節點的子樹中任意一個節點
森林:n個互不相交的數的結合稱為森林
4、樹的儲存
樹可以順序儲存、鏈式儲存、也可以混合儲存,根據儲存的資訊不同、有以下表示方法:
雙親表示法
順序:
位置 | data | 雙親 |
---|---|---|
0 | A | -1 |
1 | B | 0 |
2 | C | 0 |
3 | D | 2 |
4 | E | 1 |
5 | F | 1 |
6 | G | 4 |
7 | H | 4 |
優點:方便找到雙親 缺點:查詢孩子節點不方便
typedef struct TreeNode
{
char data;
int parent;
}TreeNode;
typedef struct Tree
{
TreeNode* arr;
size_t cal;
size_t cnt;
}Tree;
Tree* create_tree(cal)
{
Tree* tree =malloc(sizeof(Tree));
tree->arr = malloc(sizeof(TreeNode)*cal);
tree->cal = cal;
tree->cnt = 0;
return tree;
}
bool add_tree(Tree* tree,char data,char parent)
{
if(tree-> cnt == tree->cal)
{
tree->cal *= 2;
tree->arr = realloc(tree->arr,tree->cal);
}
if('\0' == parent)
{
tree->arr[0].data = data;
tree->arr[0].parent = -1;
tree->cnt++;
return true;
}
for(int i=0;i<tree->cnt;i++)
{
if(tree->arr[i].data == parent)
{
tree->arr[tree->cnt].data = data;
tree->arr[tree->cnt++].parent = i;
return ture;
}
}
return false;
}
void show_tree(Tree* tree)
{
for(int i=0;i<tree->cnt;i++)
{
printf("index:%d data:%c parent:%d\n",i.tree->arr[i].data,tree->arr[i].data,tree->arr[i].parent);
}
}
int main()
{
Tree* tree = create_tree(10);
add_tree(tree,'A','\0');
add_tree(tree,'B','A');
add_tree(tree,'C','A');
add_tree(tree,'D','C');
add_tree(tree,'E','B');
add_tree(tree,'F','B');
add_tree(tree,'G','E');
show_tree(tree);
}
孩子表示法
順序:浪費大量空間 找孩子方便、但是找雙親麻煩
位置 | data | 孩子 |
---|---|---|
0 | A | 1,2 |
1 | B | 4,5 |
2 | C | 3 |
3 | D | |
4 | E | 6,7 |
5 | F | |
6 | G | |
7 | H |
鏈式:相對上面的順序節約了空間
位置 | data | 孩子 |
---|---|---|
0 | A | 1->2->NULL |
1 | B | 4->5->NULL |
2 | C | 3->NULL |
3 | D | ->NULL |
4 | E | 6->7->NULL |
5 | F | ->NULL |
6 | G | ->NULL |
7 | H | ->NULL |
兄弟表示法
雙親的孩子指標域只儲存第一個孩子節點、兄弟指標域只儲存第一個兄弟節點
優點:方便查詢兄弟
缺點:查詢雙親比較麻煩
總結:普通樹不常用、一般會轉換為二叉樹使用。
二叉樹:
是一種常用的樹型結構、處理起來比普通樹要簡單、而且普通樹可以很方便的轉換成二叉樹。
定義
節點數最多為2、二叉樹是n個有限元素的結合、該有一個稱為根的元素及兩個不相交的、被分別稱為左子樹和右子樹的二叉樹組成、是有序樹。當集合為空時、稱該二叉樹為空二叉樹。
特殊的二叉樹
滿二叉樹:每一層的節點數都是2^(i-1)個
完全二叉樹:深度為k,有n個結點的二叉樹當且僅當其每一個結點都與深度為k的滿二叉樹中編號從1到n的結點一一對應時,稱為完全
二叉樹
二叉樹的性質
- 性質1:二叉樹的第i層上至多有2^(i-1)個節點
- 性質2:深度為h的二叉樹、至多有2^h -1個節點
- 性質3:二叉樹中有n0個葉子節點,有n2個度為2的節點,則有n0=n2+1
- 性質4:具有n個節點的完全二叉樹深為log2x+1
- 性質5:若對一棵有n個節點的完全二叉樹進行順序編號(1≤i≤n),那麼,對於編號為i(i≥1)的節點:
當i=1時,該節點為根,它無雙親節點
當i>1時,該節點的雙親節點的編號為i/2
若2i≤n,則有編號為2i的左節點,否則沒有左節點
若2i+1≤n,則有編號為2i+1的右節點,否則沒有右節點
二叉樹的操作:
構建、銷燬、遍歷、高度、密度、插入、刪除、查詢、求左、求右
二叉樹的儲存:
順序:必須嚴格按照二叉樹節點書序儲存、空位置使用特殊資料代替
資料項:儲存節點的記憶體首地址 容量
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#include"list_queue.h"
//#define TYPE char
typedef struct BinTree
{
TYPE* arr;
size_t cal;
}BinTree;
BinTree* create_tree(TYPE* arr,size_t len)
{
BinTree* tree = malloc(sizeof(BinTree));
tree->arr = malloc(sizeof(TYPE)*len);
memcpy(tree->arr,arr,len*sizeof(TYPE));
tree->cal = len;
return tree;
}
void _dlr_show(BinTree* tree,size_t index)
{
if(index-1 >= tree->cal) return;
printf("%c ",tree->arr[index-1]);
_dlr_show(tree,index*2);//left subtree
_dlr_show(tree,index*2+1);//right subtree
}
void _ldr_show(BinTree* tree,size_t index)
{
if(index-1 >= tree->cal) return;
_ldr_show(tree,index*2);//left subtree
printf("%c ",tree->arr[index-1]);
_ldr_show(tree,index*2+1);//right subtree
}
void _lrd_show(BinTree* tree,size_t index)
{
if(index-1 >= tree->cal) return;
_lrd_show(tree,index*2);//left subtree
_lrd_show(tree,index*2+1);//right subtree
printf("%c ",tree->arr[index-1]);
}
void tier_show_tree(BinTree* tree)//Sequence traversal
{
ListQueue* queue = create_list_queue();
push_list_queue(queue,1);
while(!empty_list_queue(queue))
{
size_t index = head_list_queue(queue);
size_t left = index*2;
size_t right = index*2+1;
if(left-1 < tree->cal && '#'!=tree->arr[left-1])
{
push_list_queue(queue,left);
}
if(right-1 < tree->cal && '#' != tree->arr[right-1])
{
push_list_queue(queue,right);
}
printf("%c ",tree->arr[index-1]);
pop_list_queue(queue);
}
destroy_list_queue(queue);
printf("\n");
}
void dlr_show(BinTree* tree)
{
_dlr_show(tree,1);
}
void ldr_show(BinTree* tree)
{
_ldr_show(tree,1);
}
void lrd_show(BinTree* tree)
{
_lrd_show(tree,1);
}
void destroy_tree(BinTree* tree)
{
free(tree->arr);
free(tree);
}
int main(int argc,const char* argv[])
{
char* str = "ABCD#EF####GH";
BinTree* tree = create_tree(str,strlen(str));
dlr_show(tree);
printf("\n");
ldr_show(tree);
printf("\n");
lrd_show(tree);
printf("\n");
tier_show_tree(tree);
}
二叉樹的遍歷:
前序遍歷:根-左-右
中序遍歷:左-根-右
後序遍歷:左-右-根
層序遍歷:從上到先-從左到右、必須要與佇列配合使用
注意:前中後由根節點遍歷位置決定、並且左右子樹的次序不能調換
注意:根據前序+中序 或者 中序+後序的組合 來還原一棵唯一的樹 前+後不能!
樹:
平衡二叉樹
完全二叉樹
有序二叉樹
圖:
有向圖
無向圖
圖的儲存
深度優先遍歷
廣度優先遍歷
最短路徑
演算法:
查詢
排序
動態規劃
時間複雜度