合肥工业大学二叉树实验(建议借鉴不推荐照搬)

(1)实验一:采用的是中序遍历算法,数据结构书上有,就不在赘述。

(2)实验二:打印叶子结点和出度为一的结点,张老师上课的时候讲过,PPT上有。改造树的遍历算法,唯一值得注意的是出度为一的判定条件。如下:(pBT->lChild && pBT->rChild == NULL) || (pBT->rChild && pBT->lChild == NULL)。

 

(3)实验三:因为要找到对应结点的父亲结点,所以改造遍历算法的时候,我们要有手段可以访问对应结点的父亲结点。这里我使用的方法是:通过遍历每一个结点,我来比较它的左孩子和右孩子是否和对应要找的值相等,如果相等那么就可以访问它的父亲结点。虽然增加了访问或者说是比较次数,但是保留了父亲结点,个人感觉还是比较好的思想。

 

(4)实验四:改造树的递归遍历算法。因为树的每一次递归刚好就是进入一个更深的层次,可以与树的高度对应起来。所以在递归的时候,我增加了一个变量Height来表示树的高,每进入一次递归让它加一。

 

(5)实验五:首先分析数组存放满二叉树的形式,根结点是i,那么它的左孩子的下标是2*i,它的右孩子的下标是2*i-1。根据这个规律,我们可以得出数组满二叉树转换为二叉链表的二叉树的操作步骤:申请一个空间,把对应结点的值传入其中,然后将它的左孩子和右孩子分别指向空,然后在递归进入右孩子和左孩子的创建,转递的参数刚好是我们得到的结论。

 

(6)实验六:首先分析题目输出二叉树中的每一个叶子结点到根结点的路径。由题目可知,它是从下往上输出路径,所以要用堆栈来保存根节点并输出。然后是改造先序遍历递归算法,递归的终止条件不变是p==NULL,判断如果不是根节点就将其入栈,如果是根节点就将里面的值打印出来(注意不要破环栈)。然后递归该结点的左孩子和右孩子。

目前为止,不进行其他操作打印的应该是前序遍历中所有根节点的直接前驱。要解决如何退栈的问题,对代码进行再修改,根结点不是任何结点的祖先结点,所以在打印完根结点后,要将该叶子结点从栈里面退出。其二对于遍历过的结点,递归回头的时候我们要将其退栈,换句话说,就是左孩子和右孩子都访问完了,准备返回上一级的,我们要将该节点退栈。

 

(7)实验七:实现二叉树的层次遍历。算法实质是按顺序访问队列里面的结点

使用循环,循环的结束条件是队列为空,每一次循环,都将队列的首个元素取出,并且出队,然后打印它的值,最后再将非空的左右孩子入队列。

 

(8)拓展实验1:复制一颗二叉树T到T1,由题目可以得出可以改造二叉树的先序递归算法(只能用先序),递归的终止条件是T的指向为空。归纳递归公式:

创建二叉树 = 创建右子树 + 创建左子树。创建子树的时候要注意将新建结点的左右子树指向NULL。

 

(9)拓展实验2:交换二叉树中每个结点的左右孩子指针的值。根据题意可以采用递归算法实现。递归的终止条件是指针指向不为空。递归的归纳公式是:交换二叉树的左右子树 = 交换左子树的左右子树 + 交换右子树的左右子树。观察形式发现和二叉树的先序遍历递归有点像。交换某个结点的左右子树的算法,和平常数的交换没有俩样。

 

(10)拓展实验3:求2个结点最近的公共祖先。首先,2个结点的公共祖先的判断条件:2个结点在该点的左子树或者右子树上,又或者是1个结点是在拎一个结点的左子树或者右子树上。

(flag_1 && flag_2) || ((pBT->data == data_1 || pBT->data == data_2) && (flag_1 || flag_2))。  由判断条件可以得到,目前的首要目标是判断该节点的左子树或者右子树上是否有要找的俩个结点,并且这个结点是从深到浅的判断。因为要由深及浅,所以要选择后序递归遍历算法,这样访问的结点是从最深的开始。然后如果找到结点返回true没找到返回false。flag_1是判断第一个结点是在该节点的子树上,flag_2是判断第二个结点是否在该节点的子树上面。

return (flag_1) || flag_2 || (pBT->data == data_1 || pBT->data == data_2);

这个代码是没有找到公共祖先的递归函数的返回值:

 

附加附加题三和实验六的部分代码:

bool FindSameAncestor(Node& pBT, ElementType data_1, ElementType data_2, Node& ancestor)
{
    if (pBT == NULL) return false;
    bool flag_1 = FindSameAncestor(pBT->lChild, data_1, data_2, ancestor);
    bool flag_2 = FindSameAncestor(pBT->rChild, data_1, data_2, ancestor);
    if ((flag_1 && flag_2) || ((pBT->data == data_1 || pBT->data == data_2) && (flag_1 || flag_2)))
    {
        //return true;
        ancestor = pBT;
        //return true;
    }
    // 最开始让返回值为true的是 pBT->data == data_1 || pBT->data == data_2 
    // 这个时候上一级的flag_1 和 flag_2 的真值就有可能会被改变,改变的值是true 
    // 因为是采用的后序遍历的算法
    return (flag_1) || flag_2 || (pBT->data == data_1 || pBT->data == data_2);
}
void Ancestor(Node& pBT,ChainStack<char>& s)
{
    static int i = 1;
    if (pBT != NULL)
    {
        s.Push(pBT->data);
        if (pBT->lChild == NULL && pBT->rChild == NULL)
        {
            cout << "" << i << "个从叶子结点到根结点的路径." << endl;
            cout << "它是从根结点" << pBT->data << "开始的" << endl;
            cout << "路径如下:";
            s.Print();
            cout << endl;
            i++;
        }
        else
        {

            Ancestor(pBT->lChild, s);
            Ancestor(pBT->rChild, s);
        }
        /*这个s弹出栈的位置放的非常巧妙*/
        s.Pop();
    }
}

  最后如果有问题可以私信我。

posted @ 2022-06-16 22:13  郭楠代码  阅读(224)  评论(0)    收藏  举报