二叉树(经典面试题)
2018-02-01 16:59
267 查看
先要了解二叉树创建过程才能更好的理解经典面试题
列表内容创建二叉树
下图为所需要创建二叉树
2.二叉树的基本操作
3.二叉树的进阶操作
4.二叉树的高阶操作
4.2:判断是否是完全二叉树
完全二叉树定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都必须为满的,第 h 层所有的结点都连续集中在最左边。如下图
列表内容创建二叉树
下图为所需要创建二叉树
1.1:创建树新节点 BTnode *BuyBTNewnode(BTDataType x) { BTnode* node = (BTnode *)malloc(sizeof(BTnode)); node->_data = x; node->_left = NULL; node->_right = NULL; return node; } 1.2:创建一个二叉树 BTnode *CreateBT(BTDataType* bt, size_t *Index, BTDataType invalid) {//Index创建为指针,要用传址方式,不能用传值,因为递归时传参形成临时变量 //导致递归的下一层中的变量发生改变不会使上一层的值改变,使用传址,下标 //Index才能随着递归而改变 BTnode *root = NULL;//创建树的根 if (bt[*Index] != invalid)//遍历数组,遇到的不是非法值时,创建结点 { root = BuyBTNewnode(bt[*Index]);//创建新结点 ++(*Index);//下标增加 root->_left = CreateBT(bt, Index, invalid); ++(*Index); root->_right = CreateBT(bt, Index, invalid); } return root; }
2.二叉树的基本操作
2.1:遍历二叉树(递归算法) void BTreePrevOrder(BTnode* root)//前序遍历 { if (root == NULL) { return; } printf("%d ", root->_data); BTreePrevOrder(root->_left); BTreePrevOrder(root->_right); } void BTreeInOrder(BTnode* root)//中序遍历 { if (root == NULL) { return; } BTreeInOrder(root->_left); printf("%d ", root->_data); BTreeInOrder(root->_right); } v 4000 oid BTreePostOrder(BTnode* root)//后序遍历 { if (root == NULL) { return; } BTreePostOrder(root->_left); BTreePostOrder(root->_right); printf("%d ", root->_data); } ////从上面可以看到,这三种遍历主要区别就是输出位置的不同
2.2:遍历二叉树(循环算法) void BTreePrevOrderNonR(BTnode* root)//前序遍历(非递归) {//把栈作为媒介来更好的遍历 Stack s;//创建一个栈,并初始化 StackInit(&s); BTnode* top = NULL; BTnode* cur = root; while (StackEmpty(&s) || cur) { while (cur != NULL)//先把左子树全部入栈 { printf("%d ", cur->_data);//打印当前的左子树,即根 StackPush(&s, cur);//入栈 cur = cur->_left; } top = StackTop(&s);//把左子树全部入栈后,取栈顶元素即 //将二叉树中的3,给top保存 StackPop(&s);//栈顶出栈:出3 cur = top->_right;//指向3的右子树,因为左子树已经访问过了 }//循环往复遍历 } void BTreeInOrderNonR(BTnode* root)//中序遍历(非递归) {//中序遍历和前序遍历的差别就是输出的时机不同 Stack s; StackInit(&s); BTnode* top = NULL; BTnode* cur = root; while (StackEmpty(&s) || cur) { while (cur != NULL)//先把左子树全部入栈,然后一次访问右子树 { StackPush(&s, cur); cur = cur->_left; } top = StackTop(&s);//打印当前栈顶 printf("%d ", top->_data); StackPop(&s); cur = top->_right; } } void BTreePostOrderNonR(BTnode* root)//后序遍历(非递归) {//后序与前中序不同的是,要有一个标记节点,用来记录右子树是否访问过 //只有右子树为空或者右子树访问过才能访问根, Stack s; StackInit(&s); BTnode *top = NULL; BTnode *prev = NULL;//标记节点 BTnode *cur = root; while (StackEmpty(&s) || cur) { while (cur) { StackPush(&s, cur); cur = cur->_left; } top = StackTop(&s); if (top->_right == NULL || top->_right == prev)//判断右子树为空或者是否访问过 { printf("%d ", top->_data); prev = top;//记录右子树已经访问过 StackPop(&s);//出栈,为访问上一个节点做准备 } else { //右子树没有访问过,开始访问右子树,循环继续(相当于子问题) cur = top->_right; } } }
3.二叉树的进阶操作
3.1:求树的节点 size_t BTreeSize(BTnode* root) { if (root == NULL)//递归结束条件 { return 0; }//本题思路是:左右子树节点相加,并且返回时加1 ,1为根节点,采取递归方式 return BTreeSize(root->_left) + BTreeSize(root->_right) + 1;//每递归一次就加1 } 3.2:求树的叶子节点的个数 size_t BTreeLeafSize(BTnode* root) { if (root == NULL) { return 0; } //在树的节点的基础上增加求叶子节点的限制条件,只有当左右子树为空时 //才能说明该节点为叶子结点,即返回1 if (root->_left == NULL && root->_right == NULL) { return 1; } return BTreeLeafSize(root->_left) + BTreeLeafSize(root->_right);//依然是左子树叶子结点加右子树叶子结点 } 3.3:求第k层的节点数 size_t BTreeKLevelSize(BTnode* root, size_t k) { if (root == NULL)//每递归一层k-1,直到递归到1时,就是第k层 { return 0; } if (1 == k) { return 1; } return BTreeKLevelSize(root->_left, k - 1) + BTreeKLevelSize(root->_right, k - 1); } 3.4:求树的深度 size_t BTreeDepth(BTnode* root) { if (root == NULL) { return 0; }//判断每一个根节点的左右子树的深度,比较其大小,返回大的 //即该根节点的的深度,记得加1 size_t leftDepth = BTreeDepth(root->_left) + 1; size_t righDepth = BTreeDepth(root->_right) + 1; return leftDepth > righDepth ? leftDepth : righDepth; } 3.5:查找树中的数,找到了返回该节点 BTnode* BTreeFind(BTnode* root, BTDataType x)(遍历一遍查找) { if (root == NULL) { return 0; } if (x == root->_data) { return root; }//左右子树分别查找 BTnode *retLeft = BTreeFind(root->_left, x); if (retLeft)//如果找到了,就向上返回,防止在递归,浪费时间 { return retLeft; } BTnode *retRight = BTreeFind(root->_right, x); if (retRight) { return retRight; } return NULL; }
4.二叉树的高阶操作
4.1:层序遍历二叉树(即一层一层遍历,遍历结果为:1 2 5 3 4 6 7 ) void BTreeLevelOrder(BTnode* root) {//层序遍历利用队列为媒介,进行遍历(队列有先进先出性质) Queue q;//创建队列并初始化 QueueInit(&q); if (root);//给队列先插入根节点,保证队列不为空,进入循环 { QueuePush(&q, root); } while (QueueEmpty(&q)) { BTnode *front = QueueFront(&q);//返回对头 printf("%d ", front->_data);//打印当前值 QueuePop(&q);//出队列 if (front->_left != NULL)//当对头节点左子树不为空时,再入队列 { QueuePush(&q, front->_left); } if (front->_right != NULL)//当对头节点右子树不为空时,再入队列 { QueuePush(&q, front->_right); } } printf("\n"); }
4.2:判断是否是完全二叉树
完全二叉树定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都必须为满的,第 h 层所有的结点都连续集中在最左边。如下图
int IsCompleteBTreeOne(BTnode* root) {//利用层序遍历来判断是否为完全二叉树 Queue q; QueueInit(&q); if (root);//给队列先插入根节点,保证队列不为空,进入循环 { QueuePush(&q, root); } while (QueueEmpty(&q)) { BTnode *front = QueueFront(&q);//front保存对头 QueuePop(&q);//出头节点 if (front == NULL)//判断front是否为空,为空了终止循环 { break; } QueuePush(&q, front->_left);//继续入左右子树 QueuePush(&q, front->_right); } while (QueueEmpty(&q))//当front为空时,判断队列中是否全部为空 //只有全部为空说明这是完全二叉树,否则不是 { BTnode *front = QueueFront(&q); if (front != NULL)//当队列中还有不为NULL的 //表示该二叉树不是完全二叉树 { return 0; } QueuePop(&q);//对头为空,出队列,还需判断 } return 1; }
4.3:判断是否是完全二叉树 (flag方式,提高效率) int IsCompleteBTreeThree(BTnode* root)// flag的方式判断 (用到队列) { Queue q; size_t flag = 1; QueueInit(&q); if (root);//给队列先插入根节点,保证队列不为空,进入循环 { QueuePush(&q, root); } while (QueueEmpty(&q)) { BTnode* front = QueueFront(&q); QueuePop(&q); if (front->_left != NULL)//当左子树不为空时,入栈 { if (0 == flag)//上次判断左子树或者右子树已经为空 //现在左子树不为空,则为不完全二叉树 { return 0; } QueuePush(&q, root->_left); } else//当左子树为空时,只flag { flag = 0; } if (front->_right != NULL)//同左 { if (0 == flag) { return 0; } QueuePush(&q, root->_right); } else { flag = 0; } } return 1; }
4.4:求二叉树中两个节点的最低公共祖先节点 BTnode* BTFindNode(BTnode* root, BTnode *node)//查找树中的节点(遍历一遍查找) { if (root == NULL || node == NULL) { return 0; } if (node == root) { return root; } BTnode *retLeft = BTFindNode(root->_left, node); if (retLeft)//如果找到了,就向上返回,防止在递归,浪费时间 { return retLeft; } BTnode *retRight = BTFindNode(root->_right, node); if (retRight) { return retRight; } return NULL; } //求二叉树中两个节点的最低公共祖先节点 BTnode * GetLastCommonParent(BTnode * root, BTnode * node1, BTnode * node2)//递归解决 { if (BTFindNode(root->_left, node1))//先在左子树查找node1,找到了进入if语句,再到右子树查找 { if (BTFindNode(root->_right, node2)) { return root;//找到返回节点 } else { GetLastCommonParent(root->_left, node1, node2);//没找到递归继续在下层找 } } else { if (BTFindNode(root->_left, node2))//左子树没有node1,就在左子树查找node2 { if (BTFindNode(root->_right, node1)) { return root; } else { GetLastCommonParent(root->_right, node1, node2);//没找到递归继续在下层找 } } } }
相关文章推荐
- /*****/二叉树经典面试题
- 二叉树经典面试题
- 二叉树经典面试题4~判断一棵树是否是完全二叉树
- [经典面试题]二叉树专题
- 二叉树经典面试题1~求二叉树的镜像
- 二叉树经典面试题4~判断一棵树是否是完全二叉树
- 二叉树经典面试题3~树中两个结点的最低公共祖先
- 经典面试题——二叉树
- 二叉树经典面试题5~由前序遍历和中序遍历还原二叉树
- 经典必背---二叉树经典笔试面试题
- 经典算法面试题(二):用递归法把二叉树的叶子结点按从左到右的顺序连成一个单链表
- 二叉树经典面试题解析一:用非递归实现二叉树的前序遍历,中序遍历,后序遍历
- 【数据结构】二叉树经典面试题解析二:求取二叉树的深度
- 经典面试题(六)重构二叉树
- 二叉树的经典面试题总结
- 算法之乐:一个算法解决3道经典二叉树面试题(深度、长度、直径)
- 经典二叉树面试题
- <编程之美>经典面试题:求二叉树节点的最大距离(我的解法,最容易理解的版本?)
- 二叉树经典面试题6~二叉树中和为某一值的路径
- 【经典面试题二】二叉树的递归与非递归遍历(前序、中序、后序)