二叉搜索树
二叉搜索树的建立是通过递归方式来建立的,和普通的二叉树的区别是加上了约束, 它的左子树的所有元素都要比根节点要小, 而右子树的所有元素都要根节点要大,左右子树也符合这个条件。他的遍历方式和普通二叉树的遍历没有什么区别。
下面是关于二叉搜索树的添加节点和前序遍历,中序遍历, 后续遍历和层级遍历
1 //递归创建添加新节点 2 void insertNode(PNode *root, ItemType data) 3 { 4 if(*root == NULL) 5 { 6 *root = (PNode)malloc(sizeof(Node)); 7 (*root)->data = data; 8 (*root)->lchild = (*root)->rchild = NULL; 9 } 10 else 11 { 12 if(data < (*root)->data) 13 insertNode(&(*root)->lchild, data); 14 else 15 insertNode(&(*root)->rchild, data); 16 } 17 } 18 //递归前序遍历 19 void preOrder(PNode root) 20 { 21 if(root == NULL) 22 return; 23 printf("%d ", root->data); 24 preOrder(root->lchild); 25 preOrder(root->rchild); 26 } 27 //递归中序遍历 28 void inOrder(PNode root) 29 { 30 if(root == NULL) 31 return; 32 inOrder(root->lchild); 33 printf("%d ", root->data); 34 inOrder(root->rchild); 35 } 36 //递归后续遍历 37 void postOrder(PNode root) 38 { 39 if(root == NULL) 40 return; 41 postOrder(root->lchild); 42 postOrder(root->rchild); 43 printf("%d ", root->data); 44 } 45 46 47 //层级遍历 48 void levelOrder(PNode root) 49 { 50 queue<PNode> q; 51 PNode p = root; 52 if(p != NULL) 53 q.push(p); 54 while(!q.empty()) 55 { 56 p = q.front(); 57 printf("%d ", p->data); 58 if(p->lchild != NULL) 59 q.push(p->lchild); 60 if(p->rchild != NULL) 61 q.push(p->rchild); 62 q.pop(); 63 } 64 }
以上这些操作除了插入有点特殊之外和普通的二叉树没有什么区别,下面主要来说二叉树的非递归遍历方法, 其中包括前序,中序和后续。其中前序和中序差不多,比较好理解点。
前序主要就是打印出节点的值, 再将左子树入栈,出栈时再栈顶的遍历右子树。下面是代码的实现
1 void preOrder_NonRecursion(PNode root) 2 { 3 stack<PNode> s; 4 PNode p = root; 5 while(p != NULL || !s.empty()) 6 { 7 if(p != NULL)//如果存在,继续找他的左孩子 8 { 9 printf("%d ", p->data); 10 s.push(p); 11 p = p->lchild; 12 } 13 else//没有的话,将p指向栈顶的元素,出栈操作,继续找右孩子 14 { 15 p = s.top(); 16 s.pop(); 17 p = p->rchild; 18 } 19 } 20 }
非递归的中序遍历和前序差不多,就改变输出的值,因为前序遍历是先根再左再右, 而中序是左根右, 所以只是输出顺序不同了而已
1 //非递归中序遍历 2 void inOrder_NonRecursion(PNode root) 3 { 4 stack<PNode> s; 5 PNode p = root; 6 while(p != NULL || !s.empty()) 7 { 8 if(p != NULL)//左孩子不为空就进栈,继续它的左孩子 9 { 10 s.push(p); 11 p = p->lchild; 12 } 13 else//输出栈顶的元素,然后出栈,找它右兄弟(程序中是先找父亲,然后找父亲的右孩子) 14 { 15 p = s.top(); 16 printf("%d ", p->data); 17 s.pop(); 18 p = p->rchild; 19 } 20 } 21 }
非递归的后续遍历稍微比前序和中序复杂了一点,但也不是很复杂,因为后续遍历是先左后右再根的, 所以根是最后的,所以加了一个布尔变量来标记他的右子树遍历了没有,其它的和前序和中序没什么大的区别, bool变量为true的时候说明他的右子树还没有遍历。false是已经遍历过了
1 void postOrder_NonRecursion(PNode root) 2 { 3 stack<pair<PNode, bool> > s;//用pair使s 的类型为PNode和bool的复合类型 4 PNode p = root; 5 while(p != NULL || !s.empty()) 6 { 7 if(p != NULL)//如果当前节点不为空的话,入栈, 继续找它的左孩子 8 { 9 s.push(make_pair(p, false));//初始的时候没有遍历他的右子树 10 p = p->lchild; 11 } 12 else//否则去栈顶 13 { 14 if(s.top().second == false)//如果它的右子树没有没遍历 15 { 16 s.top().second = true;//标记已经遍历过 17 p = s.top().first->rchild;//遍历右子树 18 } 19 else//遍历过了,直接输出, 因为此时节点为当前树的根节点 20 { 21 printf("%d ", s.top().first->data); 22 s.pop(); 23 } 24 } 25 } 26 }
还有就是求树的高度, 一般是用递归来实现, 递归比价好理解, 代码也比较简洁, 但是有时候也需要非递归方式来求,下面是递归方式求的, 比较简单
1 //递归求树的深度 2 int getDepth(PNode root) 3 { 4 int d1, d2; 5 if(root == NULL) 6 return 0; 7 d1 = getDepth(root->lchild); 8 d2 = getDepth(root->rchild); 9 return (d1 > d2 ? d1 : d2) + 1; 10 }
非递归的方式求书的高度的思想是:一层一层的遍历, 当遍历一层的时候, 层数加一, 但是怎么确定这层是否遍历完呢, 这时就需要三个变量来合作记录了,一个last_level_number来记录以上层一共有多少个入队的, in_queue_number 来记录所有入队的了有几个, 还有一个是visit_number来记录当前已经出队了几个元素了, 也就是有多少个节点已经扩展了子节点, 也就是广搜的那个扩展, 主要还是利用层级遍历的思想, 需要一个队列
1 //非递归求数的深度 2 int getDepth_NonRecursion(PNode root) 3 { 4 if(root == NULL) 5 return 0; 6 queue<PNode> q; 7 PNode p = root; 8 q.push(p); 9 int last_level_number = 1;//用来标记一共有多少个曾经入过队 10 int visit_number = 0;//记录已经扩展了的节点 11 int in_queue_number = 1;//所有入队的有所少个 12 int height = 0; 13 while (!q.empty()) 14 { 15 visit_number++;//出队一次加一个, 也就是扩展一次, 加一次 16 p = q.front(); 17 q.pop(); 18 //分别扩展它的左孩子和右孩子 19 if (p->lchild != NULL) 20 { 21 q.push(p->lchild); 22 in_queue_number++; 23 } 24 if(p->rchild != NULL) 25 { 26 q.push(p->rchild); 27 in_queue_number++; 28 } 29 //如果到一层的结尾 30 if(visit_number == last_level_number) 31 { 32 height++; 33 last_level_number = in_queue_number;//更新last_level_number 34 } 35 } 36 return height; 37 }
还有就是求二叉搜索树中任意给定两个节点的最近的共同祖先, 这里是二叉搜索树比较简单, 如果不是二叉搜索树就比较麻烦了,暂时只说二叉搜索树的, 非二叉搜索树的后面补上。
主要思想是:利用二叉搜索树的性质, 任意的左孩子都比根节点小, 任意的右节点都比根节点大, 所以,直接拿着两个点和根节点进行比价, 如果都比根节点大的话, 就说明共同祖先肯定在根节点的右边, 如果都小的话, 就说明都在根节点的左边, 如果一小一大那就好办了, 直接就是根节点了, 继续找左子树或者右子树。下面是代码的实现'
//求两节点的最近的共同祖先 void getCommonAncestor(PNode root, ItemType n1, ItemType n2) { if(root == NULL) return; while((n1 < root->data && n2 < root->data) || (n1 > root->data && n2 > root->data) ) { if(n1 < root->data) root = root->lchild; else root = root->rchild; } printf("latest common ancestor: %d\n", root->data); }
求二叉树的镜像,二叉树的镜像的意思就是将他的各个左右节点都反过来, 听着很麻烦, 其实递归很简单的, 只需要先把他的左右子树交换过来, 然后再继续递归他的左右子树就行了
代码如下:
1 //求二叉树的镜像 2 void mirror(PNode root) 3 { 4 if(root != NULL) 5 { 6 //交换它的左右子树 7 PNode p = root->lchild; 8 root->lchild = root->rchild; 9 root->rchild = p; 10 //递归它的左右子树 11 mirror(root->lchild); 12 mirror(root->rchild); 13 } 14 }
下面是所有的实现的完整代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #include <stack> 5 #include <queue> 6 7 using namespace std; 8 9 typedef int ItemType; 10 typedef struct Node{ 11 ItemType data; 12 struct Node *lchild, *rchild; 13 }Node, *PNode; 14 //递归创建添加新节点 15 void insertNode(PNode *root, ItemType data) 16 { 17 if(*root == NULL) 18 { 19 *root = (PNode)malloc(sizeof(Node)); 20 (*root)->data = data; 21 (*root)->lchild = (*root)->rchild = NULL; 22 } 23 else 24 { 25 if(data < (*root)->data) 26 insertNode(&(*root)->lchild, data); 27 else 28 insertNode(&(*root)->rchild, data); 29 } 30 } 31 //递归前序遍历 32 void preOrder(PNode root) 33 { 34 if(root == NULL) 35 return; 36 printf("%d ", root->data); 37 preOrder(root->lchild); 38 preOrder(root->rchild); 39 } 40 //递归中序遍历 41 void inOrder(PNode root) 42 { 43 if(root == NULL) 44 return; 45 inOrder(root->lchild); 46 printf("%d ", root->data); 47 inOrder(root->rchild); 48 } 49 //递归后续遍历 50 void postOrder(PNode root) 51 { 52 if(root == NULL) 53 return; 54 postOrder(root->lchild); 55 postOrder(root->rchild); 56 printf("%d ", root->data); 57 } 58 //非递归前序遍历 59 void preOrder_NonRecursion(PNode root) 60 { 61 stack<PNode> s; 62 PNode p = root; 63 while(p != NULL || !s.empty()) 64 { 65 if(p != NULL)//如果存在,继续找他的左孩子 66 { 67 printf("%d ", p->data); 68 s.push(p); 69 p = p->lchild; 70 } 71 else//没有的话,将p指向栈顶的元素,出栈操作,继续找右孩子 72 { 73 p = s.top(); 74 s.pop(); 75 p = p->rchild; 76 } 77 } 78 } 79 //非递归中序遍历 80 void inOrder_NonRecursion(PNode root) 81 { 82 stack<PNode> s; 83 PNode p = root; 84 while(p != NULL || !s.empty()) 85 { 86 if(p != NULL)//左孩子不为空就进栈,继续它的左孩子 87 { 88 s.push(p); 89 p = p->lchild; 90 } 91 else//输出栈顶的元素,然后出栈,找它右兄弟(程序中是先找父亲,然后找父亲的右孩子) 92 { 93 p = s.top(); 94 printf("%d ", p->data); 95 s.pop(); 96 p = p->rchild; 97 } 98 } 99 } 100 //非递归后续遍历 101 void postOrder_NonRecursion(PNode root) 102 { 103 stack<pair<PNode, bool> > s;//用pair使s 的类型为PNode和bool的复合类型 104 PNode p = root; 105 while(p != NULL || !s.empty()) 106 { 107 if(p != NULL)//如果当前节点不为空的话,入栈, 继续找它的左孩子 108 { 109 s.push(make_pair(p, false));//初始的时候没有遍历他的右子树 110 p = p->lchild; 111 } 112 else//否则去栈顶 113 { 114 if(s.top().second == false)//如果它的右子树没有没遍历 115 { 116 s.top().second = true;//标记已经遍历过 117 p = s.top().first->rchild;//遍历右子树 118 } 119 else//遍历过了,直接输出, 因为此时节点为当前树的根节点 120 { 121 printf("%d ", s.top().first->data); 122 s.pop(); 123 } 124 } 125 } 126 } 127 //层级遍历 128 void levelOrder(PNode root) 129 { 130 queue<PNode> q; 131 PNode p = root; 132 if(p != NULL) 133 q.push(p); 134 while(!q.empty()) 135 { 136 p = q.front(); 137 printf("%d ", p->data); 138 if(p->lchild != NULL) 139 q.push(p->lchild); 140 if(p->rchild != NULL) 141 q.push(p->rchild); 142 q.pop(); 143 } 144 } 145 //求一层的所有节点 146 void specifyLevelNode(PNode root, int h)//h是指定的高度 147 { 148 if(root == NULL) 149 return; 150 queue<PNode> q; 151 PNode p = root; 152 q.push(p); 153 int last_level_number = 1;//用来标记以上所有层多少个 154 int visit_number = 0; 155 int in_queue_number = 1;//入队的多少个 156 int height = 0; 157 while (!q.empty()) 158 { 159 visit_number++; 160 p = q.front(); 161 q.pop(); 162 163 if (p->lchild != NULL) 164 { 165 q.push(p->lchild); 166 in_queue_number++; 167 if (h - 2 == height)/*因为这个高度执行到后面才++, 所以h要减一, 168 还有就是当前是p->lchild而不是p,所以在减一*/ 169 printf("%d ", p->lchild->data); 170 } 171 if(p->rchild != NULL) 172 { 173 q.push(p->rchild); 174 in_queue_number++; 175 if (h - 2== height)//同上 176 printf("%d ", p->rchild->data); 177 } 178 179 if(visit_number == last_level_number) 180 { 181 height++; 182 last_level_number = in_queue_number; 183 } 184 } 185 } 186 //递归求树的深度 187 int getDepth(PNode root) 188 { 189 int d1, d2; 190 if(root == NULL) 191 return 0; 192 d1 = getDepth(root->lchild); 193 d2 = getDepth(root->rchild); 194 return (d1 > d2 ? d1 : d2) + 1; 195 } 196 //非递归求数的深度 197 int getDepth_NonRecursion(PNode root) 198 { 199 if(root == NULL) 200 return 0; 201 queue<PNode> q; 202 PNode p = root; 203 q.push(p); 204 int last_level_number = 1;//用来标记一共有多少个曾经入过队 205 int visit_number = 0;//记录已经扩展了的节点 206 int in_queue_number = 1;//所有入队的有所少个 207 int height = 0; 208 while (!q.empty()) 209 { 210 visit_number++;//出队一次加一个, 也就是扩展一次, 加一次 211 p = q.front(); 212 q.pop(); 213 //分别扩展它的左孩子和右孩子 214 if (p->lchild != NULL) 215 { 216 q.push(p->lchild); 217 in_queue_number++; 218 } 219 if(p->rchild != NULL) 220 { 221 q.push(p->rchild); 222 in_queue_number++; 223 } 224 //如果到一层的结尾 225 if(visit_number == last_level_number) 226 { 227 height++; 228 last_level_number = in_queue_number;//更新last_level_number 229 } 230 } 231 return height; 232 } 233 //求二叉树中相距最远的两个节点之间的距离 234 int getMaxDistance(PNode root) 235 { 236 if(root == NULL) 237 return 0; 238 return getDepth(root->lchild) + getDepth(root->rchild); 239 } 240 //求两节点的最近的共同祖先 241 void getCommonAncestor(PNode root, ItemType n1, ItemType n2) 242 { 243 if(root == NULL) 244 return; 245 while((n1 < root->data && n2 < root->data) || (n1 > root->data && n2 > root->data) ) 246 { 247 if(n1 < root->data) 248 root = root->lchild; 249 else 250 root = root->rchild; 251 } 252 printf("latest common ancestor: %d\n", root->data); 253 } 254 255 //求路径和为指定值的所有路径 256 257 int main() 258 { 259 260 PNode root = NULL; 261 int test[8] = {8, 5, 16, 1, 4, 9, 24, 7}; 262 for(int i = 0; i < 8; i++) 263 { 264 insertNode(&root, test[i]); 265 } 266 printf("Previous Order Traverse:\n"); 267 preOrder(root); 268 printf("\nIn Order Traverse:\n"); 269 inOrder(root); 270 printf("\nPost Order Traverse:\n"); 271 postOrder(root); 272 printf("\nNon-Recursion Previous Order Traverse:\n"); 273 preOrder_NonRecursion(root); 274 printf("\nNon-Recursion In Order Traverse:\n"); 275 inOrder_NonRecursion(root); 276 printf("\nNon-Recursion Post Order Traverse:\n"); 277 postOrder_NonRecursion(root); 278 printf("\nLevel Order Traverse:\n"); 279 levelOrder(root); 280 printf("\nThe depth of tree: %d\n", getDepth(root)); 281 printf("The depth of tree(Non-Recursion): %d\n", getDepth_NonRecursion(root)); 282 specifyLevelNode(root, 3);//求第三层的所有节点 283 printf("\nThe max distance: %d\n", getMaxDistance(root)); 284 getCommonAncestor(root, 7, 4);//测试数据,得出7和4的最近祖先 285 286 return 0; 287 }