搜尋二叉樹程式碼實現簡析
阿新 • • 發佈:2019-01-01
> 樹和節點
typedef struct tree_node_t { struct tree_node_t* left; struct tree_node_t* right; struct tree_node_t* parent; int value; } tree_node_t;
typedef struct tree_t { tree_node_t* head; } tree_t;
> 安裝節點畫圖軟體
sudo apt-get install graphviz
自帶dot命令。
> graphviz的使用格式:
void tree_draw_node(tree_node_t* node, FILE* fp) { if(node == NULL) return;
// 繪製點 /* * node[shape=record,style=filled,color=black,fontcolor=white]; * 93[label="<f0> | <f1> 93 | <f2> "]; * */
fprintf(fp, "node[shape=record];%d[label=\"<f0>|<f1> %d|<f2>\"];\n", node->value, node->value);
// 繪製線條 if(node->parent != NULL) { // 93:f0:se->37:f1; if(node == node->parent->left) fprintf(fp, "%d:f0:sw->%d:f1;\n", node->parent->value, node->value); else fprintf(fp, "%d:f2:se->%d:f1;\n", node->parent->value, node->value); }
// 遞迴遍歷 tree_draw_node(node->left, fp); tree_draw_node(node->right, fp); }
void tree_print(tree_t* tree, const char* filename) { FILE* fp = fopen(filename, "w"); fprintf(fp, "digraph G{\n");
tree_draw_node(tree->head, fp);
fprintf(fp, "}\n"); fclose(fp);
// xxx.dot char cmd[2048]; sprintf(cmd, "dot %s -Tpng -o %s.png", filename, filename); popen(cmd, "r"); }
> 初始化樹和節點
void tree_init(tree_t* tree) { tree->head = NULL; }
void tree_init_node(tree_node_t* node) { node->left = node->right = node->parent = NULL; }
> 節點的插入:
void tree_insert(tree_t* tree, int value) { tree_node_t* node = malloc(sizeof(tree_node_t)); tree_init_node(node); node->value = value;
if(tree->head == NULL) { tree->head = node; return; }
tree_node_t* parent = tree->head; while(1) { if(parent->value > value) { if(parent->left == NULL) { parent->left = node; node->parent = parent; break; } parent = parent->left; } else { if(parent->right == NULL) { parent->right = node; node->parent = parent; break; } parent = parent->right; } } }
> 節點的查詢
tree_node_t* tree_find(tree_t* tree, int value) { if ( NULL == tree) { return; }
if (tree->head->value == value) { return tree->head; }
tree_node_t* node = tree->head; while (node) { if (value < node->value) { node = node->left; } else if (value > node->value) { node = node->right; } else { break; } }
return node; }
> 節點的刪除
// 並不是真的刪除節點而是用節點左邊的最大值或者右邊的最小值來覆蓋節點 void tree_remove(tree_t* tree, tree_node_t* node) { if (NULL == tree || NULL == node) { return ; }
// 先選取節點右邊的最大值替換要刪除的節點的值 tree_node_t* left = node->left; if (left) { tree_node_t* leftMax = left->right; if (leftMax == NULL) // 當left右邊沒有節點的時候,left就是最大值節點 { leftMax = left; } else { while (leftMax->right) // 1、找到左邊的最大值 { leftMax = leftMax->right; } }
node->value = leftMax->value; // 2、找到最大值後,覆蓋要刪除節點的值
// 3、將最大值節點的左節點的值,掛到覆蓋節點的 右邊 / 左邊( 不然後面的節點就沒有掛上,,,重要) if (leftMax->parent->right == leftMax) // { leftMax->parent->right = leftMax->left; } else { leftMax->parent->left = leftMax->left; } free(leftMax); leftMax = NULL; } else // 選取節點右邊的最大值替換要刪除的節點的值 { tree_node_t* right = node->right; if (right) { tree_node_t* rightMin = right->left; if (NULL == rightMin) { rightMin = right; } else { while (rightMin->left) { rightMin = rightMin->left; } }
node->value = rightMin->value;
// 3、將最小節點的右節點掛到 覆蓋後節點的 右邊 / 左邊 ( 不然後面的節點沒有掛上,,,重要) if (rightMin->parent->right == rightMin) { rightMin->parent->right = rightMin->right; } else { rightMin->parent->left = rightMin->right; } } else // 沒有左節點,也沒有右節點,那就是根節點。 { free(tree->head); tree->head = NULL; } } }
> 搜尋二叉樹的左旋:
void tree_rotate_left(tree_t* tree, tree_node_t* node) { tree_node_t* right = node->right; if(right == NULL) return; // parent可能為空 tree_node_t* parent = node->parent;
// 1. 將原先node節點的右邊的最小值,移到 後來node的右邊 node->right = right->left; if(right->left) right->left->parent = node;
// 2. 父子節點換位置 right->left = node; node->parent = right;
// 3. 旋轉後parent節點的指向 right->parent = parent; if(parent) { if(parent->left == node) parent->left = right; else parent->right = right; } else { tree->head = right; } }
> 搜尋二叉樹的右旋:
void tree_rotate_right(tree_t* tree, tree_node_t* node) { tree_node_t* left = node->left; if(left == NULL) return; tree_node_t* parent = node->parent;
// 1. 將原先node節點左邊的最大值,移動到後來Node節點的左邊 node->left = left->right; if(left->right) left->right->parent = node;
// 2. 父子節點的指向 left->right = node; node->parent = left;
// 3. 旋轉後parent節點的指向 left->parent = parent; if(parent) { if(parent->right == node) parent->right = left; else parent->left = left; } else { tree->head = left; } }
typedef struct tree_node_t { struct tree_node_t* left; struct tree_node_t* right; struct tree_node_t* parent; int value; } tree_node_t;
typedef struct tree_t { tree_node_t* head; } tree_t;
> 安裝節點畫圖軟體
sudo apt-get install graphviz
自帶dot命令。
> graphviz的使用格式:
void tree_draw_node(tree_node_t* node, FILE* fp) { if(node == NULL) return;
// 繪製點 /* * node[shape=record,style=filled,color=black,fontcolor=white]; * 93[label="<f0> | <f1> 93 | <f2> "]; * */
fprintf(fp, "node[shape=record];%d[label=\"<f0>|<f1> %d|<f2>\"];\n", node->value, node->value);
// 繪製線條 if(node->parent != NULL) { // 93:f0:se->37:f1; if(node == node->parent->left) fprintf(fp, "%d:f0:sw->%d:f1;\n", node->parent->value, node->value); else fprintf(fp, "%d:f2:se->%d:f1;\n", node->parent->value, node->value); }
// 遞迴遍歷 tree_draw_node(node->left, fp); tree_draw_node(node->right, fp); }
void tree_print(tree_t* tree, const char* filename) { FILE* fp = fopen(filename, "w"); fprintf(fp, "digraph G{\n");
tree_draw_node(tree->head, fp);
fprintf(fp, "}\n"); fclose(fp);
// xxx.dot char cmd[2048]; sprintf(cmd, "dot %s -Tpng -o %s.png", filename, filename); popen(cmd, "r"); }
> 初始化樹和節點
void tree_init(tree_t* tree) { tree->head = NULL; }
void tree_init_node(tree_node_t* node) { node->left = node->right = node->parent = NULL; }
> 節點的插入:
void tree_insert(tree_t* tree, int value) { tree_node_t* node = malloc(sizeof(tree_node_t)); tree_init_node(node); node->value = value;
if(tree->head == NULL) { tree->head = node; return; }
tree_node_t* parent = tree->head; while(1) { if(parent->value > value) { if(parent->left == NULL) { parent->left = node; node->parent = parent; break; } parent = parent->left; } else { if(parent->right == NULL) { parent->right = node; node->parent = parent; break; } parent = parent->right; } } }
> 節點的查詢
tree_node_t* tree_find(tree_t* tree, int value) { if ( NULL == tree) { return; }
if (tree->head->value == value) { return tree->head; }
tree_node_t* node = tree->head; while (node) { if (value < node->value) { node = node->left; } else if (value > node->value) { node = node->right; } else { break; } }
return node; }
> 節點的刪除
// 並不是真的刪除節點而是用節點左邊的最大值或者右邊的最小值來覆蓋節點 void tree_remove(tree_t* tree, tree_node_t* node) { if (NULL == tree || NULL == node) { return ; }
// 先選取節點右邊的最大值替換要刪除的節點的值 tree_node_t* left = node->left; if (left) { tree_node_t* leftMax = left->right; if (leftMax == NULL) // 當left右邊沒有節點的時候,left就是最大值節點 { leftMax = left; } else { while (leftMax->right) // 1、找到左邊的最大值 { leftMax = leftMax->right; } }
node->value = leftMax->value; // 2、找到最大值後,覆蓋要刪除節點的值
// 3、將最大值節點的左節點的值,掛到覆蓋節點的 右邊 / 左邊( 不然後面的節點就沒有掛上,,,重要) if (leftMax->parent->right == leftMax) // { leftMax->parent->right = leftMax->left; } else { leftMax->parent->left = leftMax->left; } free(leftMax); leftMax = NULL; } else // 選取節點右邊的最大值替換要刪除的節點的值 { tree_node_t* right = node->right; if (right) { tree_node_t* rightMin = right->left; if (NULL == rightMin) { rightMin = right; } else { while (rightMin->left) { rightMin = rightMin->left; } }
node->value = rightMin->value;
// 3、將最小節點的右節點掛到 覆蓋後節點的 右邊 / 左邊 ( 不然後面的節點沒有掛上,,,重要) if (rightMin->parent->right == rightMin) { rightMin->parent->right = rightMin->right; } else { rightMin->parent->left = rightMin->right; } } else // 沒有左節點,也沒有右節點,那就是根節點。 { free(tree->head); tree->head = NULL; } } }
> 搜尋二叉樹的左旋:
void tree_rotate_left(tree_t* tree, tree_node_t* node) { tree_node_t* right = node->right; if(right == NULL) return; // parent可能為空 tree_node_t* parent = node->parent;
// 1. 將原先node節點的右邊的最小值,移到 後來node的右邊 node->right = right->left; if(right->left) right->left->parent = node;
// 2. 父子節點換位置 right->left = node; node->parent = right;
// 3. 旋轉後parent節點的指向 right->parent = parent; if(parent) { if(parent->left == node) parent->left = right; else parent->right = right; } else { tree->head = right; } }
> 搜尋二叉樹的右旋:
void tree_rotate_right(tree_t* tree, tree_node_t* node) { tree_node_t* left = node->left; if(left == NULL) return; tree_node_t* parent = node->parent;
// 1. 將原先node節點左邊的最大值,移動到後來Node節點的左邊 node->left = left->right; if(left->right) left->right->parent = node;
// 2. 父子節點的指向 left->right = node; node->parent = left;
// 3. 旋轉後parent節點的指向 left->parent = parent; if(parent) { if(parent->right == node) parent->right = left; else parent->left = left; } else { tree->head = left; } }