OI-Wiki Tree Basic

相关定义

树叶(leaf):没有儿子的节点。
兄弟(sibling):有相同父亲的节点。
树 | Tree - 图1树 | Tree - 图2路径长(length):边的条数。
树 | Tree - 图3的深度(depth):根到树 | Tree - 图4的路径长。
树 | Tree - 图5的高度(height):从树 | Tree - 图6到一片树叶的最长路径长。

树的实现

FirstChild-NextSibling Representation

image.png
image.png image.png
这种表示方法不唯一,因为没有规定children的顺序。

二叉树 | Binary Tree

image.png image.png

树的遍历 | Tree Traversals

先、中、后序遍历

6E8350A1BC94649BEB04CA35FA697CD5.pngEE3D89E96254E565762A04D3D0F5A95F.png

层次遍历

用到了队列数据结构。每次出队一个节点,就把它的所有孩子加入队列。伪码如下:

  1. void levelorder ( tree_ptr tree ){ enqueue ( tree );
  2. while (queue is not empty) {
  3. visit ( T = dequeue ( ) );
  4. for (each child C of T )
  5. enqueue ( C );
  6. }
  7. }

具体实现(同时可以知道现在遍历到哪个层次)

  1. void levelOrder(struct tree *BT){
  2. int level = 1;
  3. struct *queue[MEM], **front = queue, **rear = queue;
  4. struct tree * curNode = BT;
  5. //enqueue
  6. if(BT != NULL){
  7. *rear = BT;
  8. rear++;
  9. *rear = NULL
  10. rear++; //把NULL作为分隔标志,借此确定层次
  11. }
  12. while(front != rear){
  13. curNode = *front;
  14. front++; //dequeue
  15. if (curNode == NULL) { //遇到NULL分隔符,说明确实遍历了一个层次
  16. if (front == rear) break;
  17. level++;
  18. *rear = NULL; //
  19. rear++;
  20. continue;
  21. }
  22. if (curNode->left != NULL) {
  23. *rear = curNode->left;
  24. rear++;
  25. }
  26. if (curNode->right != NULL) {
  27. *rear = curNode->right;
  28. rear++;
  29. }
  30. }
  31. }

用栈实现前、中、后序遍历(None Recursive Way)

利用栈的FILO特性和三种遍历的特点。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAXN 100
  4. #define INF -9223372036854775808
  5. struct tree {
  6. int data;
  7. struct tree *left, *right;
  8. }*binTree;
  9. struct tree*createTree(int in[],int pre[],int n) {
  10. if (n == 0) return NULL;
  11. struct tree *root = (struct tree *) malloc(sizeof(struct tree));
  12. root->data = pre[0];
  13. int n1 = 0, n2 = 0, here = 0,
  14. lIn[MAXN] = {0}, rIn[MAXN] = {0}, lPre[MAXN] = {0}, rPre[MAXN] = {0};
  15. while (in[here] != pre[0]) here++;
  16. for (int i = 0; i < here; i++) lIn[n1++] = in[i];
  17. for (int i = here + 1; i < n; i++) rIn[n2++] = in[i];
  18. for (int i = 0; i < n1; i++) lPre[i] = pre[i + 1];
  19. for (int i = 0; i < n2; i++) rPre[i] = pre[n1 + i + 1];
  20. root->left = createTree(lIn, lPre, n1);
  21. root->right = createTree(rIn, rPre, n2);
  22. return root;
  23. }
  24. /*入栈一个节点就出栈并访问其值,遇到空时弹出当前栈顶节点并访问其右子树,重复之前操作*/
  25. void stackPreOrder(struct tree*root) {
  26. int stack1[MAXN] = {0}, s1Bottom = INF, *sp1 = &s1Bottom; //actually do not need stack1 now
  27. struct tree **stack2[MAXN], *s2Bottom = NULL, **sp2 = &s2Bottom;
  28. while (root != NULL || sp2 != &s2Bottom) {
  29. if (root) {
  30. *(++sp1) = root->data;
  31. *(++sp2) = root; //push
  32. printf("%d ", root->data);
  33. root = root->left;
  34. } else {
  35. root = *(sp2--);
  36. root = root->right;
  37. }
  38. }
  39. }
  40. /*一直访问左子树直至遇到空,弹出栈顶节点,访问其值,并访问其右子树*/
  41. void stackInOrder(struct tree*root) {
  42. int stack1[MAXN] = {0}, s1Bottom = INF, *sp1 = &s1Bottom; //actually do not need stack1 now
  43. struct tree **stack2[MAXN], *s2Bottom = NULL, **sp2 = &s2Bottom;
  44. while (root != NULL || sp2 != &s2Bottom) {
  45. if (root) {
  46. *(++sp2) = root;
  47. root = root->left;
  48. } else {
  49. root = *(sp2--);
  50. printf("%d ",root->data);
  51. root = root->right;
  52. }
  53. }
  54. }
  55. void stackPostOrder(struct tree*root) {
  56. int stack1[MAXN] = {0}, s1Bottom = INF, *sp1 = &s1Bottom;
  57. struct tree **stack2[MAXN], *s2Bottom = NULL, **sp2 = &s2Bottom;
  58. while (root != NULL || sp2 != &s2Bottom) {
  59. if (root) {
  60. *(++sp1) = root->data;
  61. *(++sp2) = root;
  62. root = root->right;
  63. } else {
  64. root = *(sp2--);
  65. root = root->left;
  66. }
  67. }
  68. while (sp1 != &s1Bottom)
  69. printf("%d ", *(sp1--));
  70. }
  71. int main() {
  72. int n, in[MAXN], pre[MAXN];
  73. scanf("%d", &n);
  74. for (int i = 0; i < n; i++) scanf("%d", &in[i]);
  75. for (int i = 0; i < n; i++) scanf("%d", &pre[i]);
  76. binTree = createTree(in, pre, n);
  77. printf("preorder:\n");
  78. stackPreOrder(binTree);
  79. printf("\ninorder:\n");
  80. stackInOrder(binTree);
  81. printf("\npostorder:\n");
  82. stackPostOrder(binTree);
  83. }

树的建立

根据中序和后(前)序建立树

image.png
用这个树举个例子,inorder:12->11->20->17->1->15->8->5postorder:12->20->17->15->8->5->1
可以注意到postorder的特点是根节点总在最后一个被访问,据此可以把inorder分为左子树和右子树,进行完这个过程后,可以把postorder再分为左子树和右子树,至此,可以进行递归。如此操作直到左子树没有元素后到达递归出口。
如果后序改为前序,则根节点总在第一个被访问,其余相同。

  1. struct tree* createTree(int inSeq[], int postSeq[], int items) {
  2. if (items == 0) {
  3. return NULL;
  4. } //没有元素,开始返回
  5. struct tree* root = (struct tree*)malloc(sizeof(struct tree));
  6. int lInSeq[MEM], rInSeq[MEM], lPostSeq[MEM], rPostSeq[MEM];
  7. root->key = postSeq[items - 1];
  8. //将inorder分为左子树和右子树
  9. int lItems = 0, rItems = 0;
  10. for (int i = 0; i < items; i++) {
  11. if (i <= lItems && inSeq[i] != postSeq[items - 1]) {
  12. lInSeq[lItems] = inSeq[i];
  13. lItems++;
  14. }
  15. if (i > lItems) {
  16. rInSeq[rItems] = inSeq[i];
  17. rItems++;
  18. }
  19. }
  20. //将postorder分为左子树和右子树
  21. for (int i = 0; i < lItems; i++) lPostSeq[i] = postSeq[i];
  22. for (int i = lItems; i < items - 1; i++) rPostSeq[i - lItems] = postSeq[i];
  23. root->left = createTree(lInSeq, lPostSeq, lItems);
  24. root->right = createTree(rInSeq, rPostSeq, rItems);
  25. return root;
  26. }

树的性质

image.png
image.png
image.png

一些树

Threaded Binary Trees

一个满二叉树,n个顶点,有n-1条边,n+1个NULL边,浪费很大。
这里的规则建立起了inorder的Threaded Binary Trees。

  • Rule 1: If Tree->Left is NULL, replace it with a pointer to the inorder predecessor(前辈) of Tree.
  • Rule 2: If Tree->Right is NULL, replace it with a pointer to the inorder successor(后继者) of Tree.
  • Rule 3: There must not be any loose threads. Therefore a threaded binary tree must have a head node of which the left child points to the first node.

    1. typedef struct ThreadedTreeNode *PtrTo ThreadedNode;
    2. typedef struct PtrToThreadedNode ThreadedTree;
    3. typedef struct ThreadedTreeNode {
    4. int LeftThread; /* if it is TRUE, then Left */
    5. ThreadedTree Left; /* is a thread, not a child ptr. */
    6. ElementType Element;
    7. int RightThread; /* if it is TRUE, then Right */
    8. ThreadedTree Right; /* is a thread, not a child ptr. */
    9. }

    image.png

    语法树 | Syntax Tree

    后序遍历:树 | Tree - 图19
    image.png

    二叉搜索树 | Binary Search Tree

    定义

  • 每个节点都有一个值为整数的key,且互不相同。

  • 对于一个顶点,其左子树的所有key小于顶点的key,右子树的key大于顶点的key。
  • 左右子树也是二叉搜索树。
  • 可以为空。

    操作

    Insert
    先检查要插入的元素是否在树中,若不在,再执行插入。
    伪码如下:

    1. SearchTree Insert( ElementType X, SearchTree T ) {
    2. if ( T == NULL ) { /* Create and return a one-node tree */
    3. T = malloc( sizeof( struct TreeNode ) );
    4. if ( T == NULL )
    5. FatalError( "Out of space!!!" );
    6. else {
    7. T->Element = X;
    8. T->Left = T->Right = NULL; }
    9. } /* End creating a one-node tree */
    10. else /* If there is a tree */
    11. if ( X < T->Element )
    12. T->Left = Insert( X, T->Left );
    13. else
    14. if ( X > T->Element ) T->Right = Insert( X, T->Right );
    15. /* Else X is in the tree already; we'll do nothing */
    16. return T; /* Do not forget this line!! */
    17. }

    Delete

  • 删除叶节点:将其parent指向null。

  • 删除度为1的节点:用它的孩子代替它。
  • 删除度为2的节点:用其左子树中最大的元素右子树中最小的元素代替它,再把这个元素从树中删除。这样做的目的是降低树的高度,尽量达到Complete Binary Tree。

image.png image.png image.png

  1. SearchTree Delete( ElementType X, SearchTree T )
  2. { Position TmpCell;
  3. if ( T == NULL ) Error( "Element not found" );
  4. else if ( X < T->Element ) /* Go left */
  5. T->Left = Delete( X, T->Left );
  6. else if ( X > T->Element ) /* Go right */
  7. T->Right = Delete( X, T->Right );
  8. else /* Found element to be deleted */
  9. if ( T->Left && T->Right ) { /* Two children */
  10. /* Replace with smallest in right subtree */
  11. TmpCell = FindMin( T->Right );
  12. T->Element = TmpCell->Element;
  13. T->Right = Delete( T->Element, T->Right ); } /* End if */
  14. else { /* One or zero child */
  15. TmpCell = T;
  16. if ( T->Left == NULL ) /* Also handles 0 child */
  17. T = T->Right;
  18. else if ( T->Right == NULL ) T = T->Left;
  19. free( TmpCell ); } /* End else 1 or 0 child */
  20. return T;
  21. }

Implementation of BST-ADT

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #define MEM 100
  5. struct tree{
  6. int data;
  7. struct tree*left,*right;
  8. }*BST;
  9. int arr[MEM],n;
  10. struct tree*find(int X,struct tree*root) {
  11. if (root == NULL) return NULL;
  12. if (X < root->data) return find(X, root->left);
  13. else if (X > root->data) return find(X, root->right);
  14. else return root;
  15. }
  16. struct tree*findMin(struct tree*root){
  17. if(!root) return NULL;
  18. else{
  19. if(root->left==NULL) return root;
  20. else return findMin(root->left);
  21. }
  22. }
  23. struct tree*findMax(struct tree*root){
  24. if(!root) return NULL;
  25. else{
  26. if(root->right==NULL) return root;
  27. else return findMin(root->right);
  28. }
  29. }
  30. struct tree*insert(int X,struct tree*root) {
  31. if (root==NULL) {
  32. root=(struct tree*) malloc(sizeof (struct tree));
  33. root->data=X;
  34. root->left=root->right=NULL;
  35. }
  36. else{
  37. if(root->data>X) root->left=insert(X,root->left);
  38. else if(root->data<X) root->right=insert(X,root->right);
  39. else ; //do nothing when X is already in tree
  40. }
  41. return root;
  42. }
  43. struct tree*delete(int X,struct tree*root){
  44. struct tree* tmp;
  45. if(root==NULL) {
  46. printf("No this element");
  47. exit(0);
  48. }
  49. else if(X< root->data) root->left= delete(X,root->left);
  50. else if(X> root->data) root->right= delete(X,root->right);
  51. else{
  52. if(root->left&&root->right){ //has two children
  53. tmp= findMax(root->left); //or findMin(root->right)
  54. root->data=tmp->data; //rewrite instead of swap
  55. root->left= delete(root->data,root->left); //delete tmp
  56. }
  57. else { //no child or one
  58. tmp=root;
  59. if(root->left==NULL) root=root->right;
  60. else if(root->right==NULL) root=root->left;
  61. free(tmp);
  62. }
  63. }
  64. return root;
  65. }
  66. void inOrder(struct tree *root){
  67. if(root){
  68. inOrder(root->left);
  69. printf("%d ",root->data);
  70. inOrder(root->right);
  71. }
  72. }
  73. int main() {
  74. scanf("%d", &n);
  75. for (int i = 0; i < n; i++) {
  76. scanf("%d", &arr[i]);
  77. BST= insert(arr[i],BST);
  78. }
  79. inOrder(BST);
  80. printf("\n");
  81. srand(time(0));
  82. BST=delete(arr[rand()%n],BST);
  83. inOrder(BST);
  84. return 0;
  85. }

结合一定条件建立一个完全二叉搜索树

给定一个整数序列,建立完全二叉搜索树(是唯一的)。

  • 将序列按升序排列,也即给出了中序序列,便于后续分割。
  • 找到根节点(可以由完全二叉树性质推得),将树分为左右子树,对左右子树做相同操作。

这里排序我结合了一下后面堆 | Heap的知识,用了小顶堆排序。这里可以看到用数组存树是很方便的。

  1. #pragma warning(disable:4996)
  2. #include <stdio.h>
  3. #include <math.h>
  4. #include <stdlib.h>
  5. #define MEM 10000
  6. #define MIN -1
  7. int n, arr[MEM], heapSize;
  8. int sortedArr[MEM], CBT[MEM];
  9. int leftSubElements(int nodes) {
  10. int height = (int)(log(nodes + 1) / log(2)); //get height
  11. int leaves = nodes - (int)pow(2, height) + 1; //number of leaf nodes
  12. int half = pow(2, height-1); //half nmber of leaf nodes
  13. int leftLeaves = (leaves < half) ? leaves : half;
  14. return (int)pow(2, height - 1) - 1 + leftLeaves;
  15. }
  16. void percolateDown(int pos) {
  17. int X = arr[pos], i, child;
  18. for (i = pos; i * 2 <= heapSize; i = child) {
  19. child = i * 2;
  20. if (child != heapSize && arr[child + 1] < arr[child]) child++;
  21. if (arr[child] < X) arr[i] = arr[child];
  22. else break;
  23. }
  24. arr[i] = X;
  25. }
  26. void createMinHeap() {
  27. for (int i = heapSize / 2; i >= 1; i--) {
  28. percolateDown(i);
  29. }
  30. }
  31. void delete() {
  32. if (heapSize == 0) return;
  33. int min = arr[1];
  34. int last = arr[heapSize--];
  35. arr[1] = last;
  36. percolateDown(1);
  37. }
  38. void minHeapSort() {
  39. for (int i = 1; heapSize != 0; i++) {
  40. sortedArr[i - 1] = arr[1];
  41. delete();
  42. }
  43. }
  44. void createTree(int left, int right, int root) {
  45. int nodes = right - left + 1;
  46. if (nodes == 0) return;
  47. int leftNodes = leftSubElements(nodes);
  48. CBT[root] = sortedArr[left + leftNodes];
  49. createTree(left, left + leftNodes - 1, 2 * root + 1);
  50. createTree(left + leftNodes + 1, right, 2 * root + 1 + 1);
  51. }
  52. int main() {
  53. scanf("%d", &n);
  54. heapSize = n;
  55. arr[0] = MIN;
  56. for (int i = 0; i < n; i++) scanf("%d", &arr[i + 1]);
  57. createMinHeap();
  58. minHeapSort();
  59. createTree(0, n - 1, 0);
  60. for (int i = 0; i < n; i++) {
  61. if (i == 0) printf("%d", CBT[i]);
  62. else printf(" %d", CBT[i]);
  63. }
  64. return 0;
  65. }