沒有父節點的節點叫作根節點。沒有子節點的節點叫作葉子節點或葉節點。node
關於樹還有三個類似概念:高度(Height)、深度(Depth)、層(Level)。數組
三種方法:前序遍歷、中序遍歷、後序遍歷(前中後指的是根節點的位置)。遍歷是一個遞歸的過程。bash
遞歸公式:post
前序遍歷的遞推公式:
preOrder(r) = print r->preOrder(r->left)->preOrder(r->right)
中序遍歷的遞推公式:
inOrder(r) = inOrder(r->left)->print r->inOrder(r->right)
後序遍歷的遞推公式:
postOrder(r) = postOrder(r->left)->postOrder(r->right)->print r
// 出口就是r=nil
複製代碼
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} Node;
void preOrder(Node *root) {
if (!root) return;
printf("%d",root->data);
preOrder(root->left);
preOrder(root->right);
}
void inOrder(Node *root) {
if (!root) return;
inOrder(root->left);
printf("%d", root->data);
inOrder(root->right);
}
void postOrder(Node *root) {
if (!root) return;
postOrder(root->left);
postOrder(root->right);
printf("%d",root->data);
}
複製代碼
遍歷的時間複雜度:每一個節點最多被訪問兩次,因此遍歷的時間複雜度跟節點個數n成正比,所以O(n);ui
Q: 給定一組數據,能夠構建出多少種不一樣的二叉樹?spa
A:徹底二叉樹能夠存儲爲一個數組,則樹的排序也就是數組中n個數據的排序:n!3d
二叉查找樹支持快速查找、插入、刪除數據。指針
二叉查找樹的要求:在樹中的任意一個節點,其左子樹中的每一個節點值,都要小於這個節點的值,而右子樹中節點值都大於這個節點的值。所以不能有重複的數據。(左子樹<父節點<右節點)code
Node* search(Node *root, int data) {
if (!root) return nil;
if (data == root->data) {
return root;
} else if (data > root->data) {
return search(root->right, data);
} else {
return search(root->left, data);
}
}
Node * search2(Node *root, int data) {
while (root != nil) {
if (data == root->data) {
return root;
} else if (data > root->data) {
root = root->right;
} else {
root = root->left;
}
}
return nil;
}
複製代碼
相似查找。把新插入的數據放在葉子節點上,這樣既不影響二叉查找樹的特性,又不須要破壞本來樹的結構。cdn
void insert(Node *root, int data) {
if (!root) {
// 注意結構體指針的賦值 Node *node = {data, nil, nil}不行的,此時node是個指針,卻把結構體值類型數據賦值給它了(int *a = 2❎)
/* 平時
① NSObject *obj = [[NSObject alloc] init];
obj 它也是一個指向NSObject類型對象數據的指針,[[NSObject alloc] init]返回的也是一個指針,而不是實際的對象的值
② char *b = "String"; "String"字符串常量,它返回給b的也是地址。
③ obj = nil;讓obj指針變量置空,也就是讓它指向空,nil是空指針的意思,不是讓obj所指向的數據置空
*/
Node node = {data, nil, nil};
root = &node;
}
while (root != nil) {
if (data > root->data) {
if (!root->right) {
Node node = {data, nil, nil};
root->right = &node;
return;
} else {
root = root->right;
}
} else {
if (!root->left) {
Node node = {data, nil, nil};
root->left = &node;
return;
} else {
root = root->left;
}
}
}
}
複製代碼
分三種狀況:
int delete(Node *root, int data) {
if (!root) return -1;
Node *preNode = nil;
BOOL find = NO;
while (root != nil) {
if (root->data == data) {
find = YES;
break;
}
preNode = root;
if (root->data > data) {
root = root->left;
} else {
root = root->right;
}
}
if (!find) return -1;
if (!root->left && !root->right) {
if (preNode->left == root) preNode->left = nil;
else preNode->right = nil;
return 1;
}
if (!root->left || !root->right) {
Node *child = nil;
if (!root->left) {
child = root->right;
} else {
child = root->left;
}
if (preNode->left == root) preNode->left = child;
else preNode->right = child;
return 1;
}
Node *min = root->right;
Node *preM = root;
while (!min) {
preM = min;
min = min->left;
}
root->data = min->data;
if (preM != root) {
if (min->right) preM->left = min->right;
else preM->left = nil;
} else {
preM->right = nil;
}
return 1;
}
複製代碼
能夠快速排序:
中序遍歷二叉查找樹,能夠輸出有序數據列,O(n)。
實際開發中每一個節點的數據能夠是一個更大的結構,如包含不少字段的對象,咱們利用某個字段做爲key來構建樹。
這樣若是存在相同鍵值的對象,怎麼解決:
typedef struct TreeNode {
int key;
struct TreeNode *left;
struct TreeNode *right;
NSMutableArray *data;
} Node;
複製代碼
相同一組數據二叉查找樹的排列方式會有不少種:
對於極度不平衡的樹,他們已經接近鏈表了,查找插入刪除O(n)。
對於徹底二叉樹,時間複雜度和樹的層數L成正比。除了底層,每層包含的節點數2^(L-1)。
// 假設最大層數爲K
n >= 1+2+4+8+...+2^(K-2)+1
n <= 1+2+4+8+...+2^(K-2)+2^(K-1)
複製代碼
藉助等比數列求和公式,算出K的範圍[log2(n+1), log2n+1],也就得出時間複雜度O(logn)
注:等比數列(每一項與它前一項的比都是同一個常數,也叫公比 q)
求樹的高度?
用遞歸的思想:height = Max(Height(left), Height(right))+1;