上一篇咱們對數據結構中經常使用的樹作了介紹,本篇博客主要以二叉樹爲例,講解一下樹的數據結構和代碼實現。回顧二叉樹:二叉樹是每一個節點最多有兩個子樹的樹結構。一般子樹被稱做「左子樹」(left subtree)和「右子樹」(right subtree)node
看看以下的數據:使用鏈表形式存放python
咱們要向查找數據6,須要從頭開始查找,找到最後一個,查找比較麻煩。再來看看使用二叉樹的形式存儲數據結構
顯然,咱們很清楚本身要查找的目標大體會在那裏出現;app
例如查找的目標是6,那麼我知道6小於9因此根本不會去看右邊的數據;post
咱們繼續看6大於5因此找到啦目標;spa
換句話說咱們只對比了兩次找到啦目標;code
而對於鏈表,咱們發現6排在了鏈表的尾部;到此爲止咱們知道這樣的二叉樹的確是高效的;blog
typedef struct N { int data; struct N *left_node; struct N *right_node; }Node;
typedef struct tree { struct node *root; }Tree;
咱們的樹定義得更加簡單,注意咱們是先定義節點,再定義樹;排序
由於樹的定義須要用到節點結構體;繼承
接下來咱們須要初始化咱們的樹
Tree * init_tree() { Tree *tree = (Tree *)malloc(sizeof(Tree)); if (tree) { tree->root = NULL; } return tree; }
Node *make_node(int data) { Node *node = (Node *)malloc(sizeof(Node)); node->left_node = NULL; node->right_node = NULL; node->data = data; return node; }
// 插入節點 Node* insert_node(Tree *tree,int data) { // 判斷根節點是否存在 if (tree->root == NULL) { // 不存在就建立 tree->root = make_node(data); } else { Node *current = tree->root; // 一直循環知道找到準確的插入數據的位置 while (1) { // 咱們的二叉樹不容許重複數字插入,相等直接退出 if (current->data == data) { return tree->root; } // 若是要插入的數據比根節點大,就放在右邊的子樹中 else if(current->data<data) { if (current->right_node == NULL) { // 建立右節點 current->right_node = make_node(data); break; } current = current->right_node; } else { // 若是要插入的數據比根節點小,就放在左邊的子樹中 if (current->left_node == NULL) { // 建立左節點 current->left_node = make_node(data); break; } current = current->left_node; } } } return tree->root; }
void print_inorder(Node *root) { if (root) { print_inorder(root->left_node); printf("data:%d\n",root->data); print_inorder(root->right_node); } }
樹的刪除比較麻煩,總體分爲二種狀況:
1、要刪除的節點左右都有子節點
2、要刪除的節點只有一個或者0個節點(即有左節點或者右節點或者一個都沒有)
其中第一種狀況又分幾種小狀況。例如:咱們要刪除節點6
1.1 咱們如今要刪除的是節點6,這時候6節點下面的右節點只有一個7,而且7下面沒有節點,有一個也同樣的,只須要將其右邊的節點7替代他的位置便可。
1.2 咱們如今要刪除的是節點6,如今7下面5和8兩個節點,若是仍是按照上面的思路刪除的話,刪除以後7下面就有1,5,8三個節點,明顯不對
正確的作法應該是找到要刪除的節點6的右節點7,這時候在找到7的作節點5,去繼承刪除節點6的位置
1.三、以要刪除節點6的右節點7爲樹的左邊分支的最小子節點是左節點的狀況(很繞口)
1.四、以要刪除節點6的右節點7爲樹的左邊分支的最小子節點是右節點的狀況(很繞口)
int remove_node(Tree *tree,int data) { if (tree->root != NULL) { Node *p = NULL; Node *s ; Node *current = tree->root; while (1) { // 根節點都沒有直接返回 if (current == NULL) { return 0; } // 要刪除的節點就是跟節點 else if(current->data == data) { break; } // 要刪除的節點在根節點的右邊 else if(current->data<data) { p = current; current = current->right_node; } // 要刪除的節點在根節點的左邊 else { p=current; current = current->left_node; } } /**********************上面的代碼片斷是找到要刪除的節點**************************/ if (current->left_node != NULL && current->right_node != NULL) { p = current; // 找到要刪除節點的右節點 s = current->right_node; while (s->left_node != NULL) { // p = s當current要深刻到下一個分叉時,給本身留一個後路;因此保存了本身的前一個備份; p = s; // 沿着左邊一直找到最小的節點 s = s->left_node; } current->data = s->data; // 最小值在分支的右邊 if ( p->right_node == s) { p->right_node = s->right_node; } free(s); } /***************上面的代碼片斷是根據要刪除節點左右都有子節點的狀況**************/ else { // 左子節點爲空,只有右子節點 if (current->left_node == NULL) { // 並且要刪除的節點是跟節點 if (p==NULL) { // 直接將跟節點的右節點設置爲跟節點 tree->root = current->right_node; } else { if (p->right_node == current) { p->right_node = current->right_node; } else { p->left_node = current->right_node; } } } // 右子節點爲空,只有左子節點 else { // 並且要刪除的節點是跟節點 if (p == NULL) { tree->root = current->left_node; } else { if (p->right_node == current) { p->right_node = current->left_node; } else { p->left_node = current->left_node; } } } } /***************上面的代碼片斷是根據要刪除節點左右只有一個或者沒有子節點的狀況**********/ } return 1; }
int find_node(Node *root,int data) { if (root == NULL) { return 0; } else if(root->data == data) { return 1; } else { if (root->data <data) { return find_node(root->right_node, data); } else { return find_node(root->left_node, data); } } }
void preOrder(Node *root) { if (root != NULL) { printf("%d ",root->data); preOrder(root->left_node); preOrder(root->right_node); } }
void inOrder(Node *root) { if (root != NULL) { inOrder(root->left_node); printf("%d ",root->data); inOrder(root->right_node); } }
void postOreder(Node *root) { if (root != NULL) { postOreder(root->left_node); postOreder(root->right_node); printf("%d ",root->data); } }
void level_order(Tree *tree) { Node *node = tree->root; Node *queue[10]; int current = 0; int after_current = 0; if (node == NULL) { return; } queue[current++] = node; while (current!=after_current) { node = queue[after_current++]; printf("%d ",node->data); if (node->left_node != NULL) { queue[current++] = node->left_node; } if (node->right_node != NULL) { queue[current++] = node->right_node; } } }
因爲C語言版寫的很詳細了,python就簡單的實現排序,思路徹底同樣。
# coding:utf-8 class Node(object): """""" def __init__(self, item): self.elem = item self.lchild = None self.rchild = None class Tree(object): """二叉樹""" def __init__(self): self.root = None def add(self, item): node = Node(item) if self.root is None: self.root = node return queue = [self.root] while queue: cur_node = queue.pop(0) if cur_node.lchild is None: cur_node.lchild = node return else: queue.append(cur_node.lchild) if cur_node.rchild is None: cur_node.rchild = node return else: queue.append(cur_node.rchild) def breadth_travel(self): """廣度遍歷""" if self.root is None: return queue = [self.root] while queue: cur_node = queue.pop(0) print(cur_node.elem, end=" ") if cur_node.lchild is not None: queue.append(cur_node.lchild) if cur_node.rchild is not None: queue.append(cur_node.rchild) def preorder(self, node): """先序遍歷""" if node is None: return print(node.elem, end=" ") self.preorder(node.lchild) self.preorder(node.rchild) def inorder(self, node): """中序遍歷""" if node is None: return self.inorder(node.lchild) print(node.elem, end=" ") self.inorder(node.rchild) def postorder(self, node): """後序遍歷""" if node is None: return self.postorder(node.lchild) self.postorder(node.rchild) print(node.elem, end=" ") if __name__ == "__main__": tree = Tree() tree.add(5) tree.add(2) tree.add(3) tree.add(7) tree.add(4) tree.add(8) tree.add(6) tree.preorder(tree.root) print(" ") tree.inorder(tree.root) print(" ") tree.postorder(tree.root) print(" ") tree.breadth_travel()
運行結果爲:
5 2 7 4 3 8 6 7 2 4 5 8 3 6 7 4 2 8 6 3 5 5 2 3 7 4 8 6
寫到此處以吐血,你看到次數也吐血了吧。