转载整合两个博客。
叉树:
二叉树的每个节点至多有两个子树。如这个二叉树,其中1,2有两个子树,3只有左子树,5有右子树,4,6,7没有子树。
二叉树有两种存储方式:
第一种,数组表示。用数组存储方式就是用一组连续的存储单元存储二叉树的数据元素。
两颗树分别用数组表示为:
用下标就可以直接找到那个节点,也可以找这个节点的左孩子2n+1,右孩子2n+2.找父节点(n-1)/2
但是数组也有缺陷,比如第二个就会浪费存储空间。
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。完全二叉树就适合用数组表示。
满二叉树就是除了叶结点外每一个结点都有左右子叶且叶结点都处在最底层的二叉树。也适合用数组表示。
完全二叉树不是满二叉树,满二叉树就是完全二叉树。
第二种,链表存储表示。其中又有二叉链表结构和三叉链表结构。
左图就是二叉链表结构,右图是三叉链表结构。
二叉链表里有leftchild指针,rightchild指针和数据。三叉链表里不仅有leftchild指针,rightchild指针和数据还有parent指针,能找到父节点。
二叉的遍历方式有四种:
第一种:前序遍历。先访问根节点,再访问左子树,最后访问右子树。
第二种:中序遍历。先访问左子树,再访问根节点,最后访问右子树。
第三种:后序遍历。先访问左子树,再访问右子树,最后访问根节点。
第四种:层序遍历。一层层节点依次遍历。
这颗二叉树的前序遍历:1-2-4-5-7-3-6
中序遍历:4-2-5-7-1-6-3
后序遍历:4-7-5-2-6-3-1
层序遍历:1-2-3-4-5-6-7
树的构造:
typedef struct BiNode{ int data;//数据域 BiNode *lchild, *rchild;//左右孩子指针 } BiNode, *BiTree;
树的遍历:
void preorder(BiNode *root){ if (root != NULL) { //访问根节点 cout << "先序遍历" << root->data; preorder(root->lchild); preorder(root->rchild); }// end of if }
//关键在于何时访问的语句的位置 void preorder(BiTree root){ //初始化栈 stack<BiTree> nodes; BiNode *p = root; while (p != NULL || !nodes.empty()) { while (p != NULL) { //根左右的顺序遍历 cout << p->data; //进栈 nodes.push(p); //继续移动 p = p->lchild; } //p == null if (!nodes.empty()) { //对 p 重新指向 p = nodes.top(); //出栈 nodes.pop(); //转到右子树 p = p->rchild; } } }
void inOrder(BiNode *root){ if (root != NULL) { inOrder(root->lchild); cout << "中序遍历" << root->data; inOrder(root->rchild); }// end of if } //非递归的中序遍历二叉树 void inOrder(BiTree root){ //非递归中序遍历(左跟右) stack<BiTree> nodes;//初始化栈 //指示指针 BiNode *p = root; //遍历二叉树的循环语句 while (p != NULL || !nodes.empty()) { while (p != NULL) { //不为空就入栈 nodes.push(p); //一直向做走,直到为 kong p = p->lchild; } // 需要判断空否,因为需要出栈操作 if (!nodes.empty()) { //令 p 重新指向 栈顶结点 p = nodes.top(); //访问根节点(栈顶结点) cout << p->data << " "; //使用完毕,弹出 nodes.pop(); //向右遍历 p = p->rchild; } }// end of while }
//递归后续遍历二叉树 void lastOrder(BiTree root){ if (root != NULL) { lastOrder(root->lchild); lastOrder(root->rchild); cout << root->data; } } void postOrder3(BiTree root) //非递归后序遍历 { stack<BiTree> nodes; //当前结点 BiNode *cur; //前一次访问的结点 BiNode *pre = NULL; //根节点入栈 nodes.push(root); //依次遍历左右子树 while(!nodes.empty()) { cur = nodes.top(); //判断 cur 结点的左右孩子子树的情况 if((cur->lchild == NULL && cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild))) { //如果当前结点没有孩子结点或者孩子节点都已被访问过 cout << cur->data; //出栈 nodes.pop(); //前一次访问的结点, pre标记已经访问的结点 pre = cur; } else { //左右跟的访问顺序,关键还是访问语句的位置!!!一定是先写右子树,再写左子树,顺序不能错 //如果当前结点的右子树不为空 if(cur->rchild != NULL){ nodes.push(cur->rchild); } //如果当前结点的左子树不为空 if(cur->lchild != NULL){ nodes.push(cur->lchild); } } } }