22计算机408考研—数据结构—二叉树链式存储

158 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

手把手教学考研大纲范围内树定义,遍历,Huffman,并查集 22考研大纲数据结构要求的是C/C++,笔者以前使用的都是Java,对于C++还很欠缺, 如有什么建议或者不足欢迎大佬评论区或者私信指出 初心是用最简单的语言描述数据结构

Talk is cheap. Show me the code. 理论到处都有,代码加例题自己练习才能真的学会

二叉树链式存储

#include <valarray>
#include "iostream"
#include "queue"
#include "stack"

using namespace std;

typedef struct TreeNode {
    int data;
    struct TreeNode *leftchild, *rightchild;
} TreeNode, *Tree;

    //插入结点
bool treeInsert(Tree &tree, int data) {
    if (tree == NULL) {     //如果根结点为空,把插入的值插入到根结点
        tree = new TreeNode;    //添加结点,设置左子结点和右子结点为空
        tree->data = data;
        tree->leftchild = NULL;
        tree->rightchild = NULL;
        cout << "根节为点空, 插入成功\n";
        return true;
    }
    Tree temp = tree;
    while (temp) {
        cout << "输入 1 选择左子结点,";
        cout << "输入 2 选择右子结点,";
        cout << "输入 3 返回上一结点,输入 其他 退出插入操作\n";
        int selected;
        cin >> selected;
        if (selected == 1) {
            if (temp->leftchild == NULL) {  //如果左子结点为空,将插入的值放到左子结点
                temp->leftchild = new TreeNode;
                temp->leftchild->data = data;
                temp->leftchild->leftchild = NULL;
                temp->leftchild->rightchild = NULL;
                cout << "插入成功\n";
                return true;
            } else {    //如果左子结点不为空,找到左子结点,继续循环
                temp = temp->leftchild;
            }
        } else if (selected == 2) {
            if (temp->rightchild == NULL) { //右子结点为空,将值插入到右子结点
                temp->rightchild = new TreeNode;
                temp->rightchild->data = data;
                temp->rightchild->leftchild = NULL;
                temp->rightchild->rightchild = NULL;
                cout << "插入成功\n";
                return true;
            } else {    //右子结点不为空,指向右子结点
                temp = temp->rightchild;
            }
        } else {    //输入其他,自己退出插入操作
            return false;
        }
    }
}

    //删除子树
bool treeDelete(Tree &tree) {
    if (tree == NULL) {     //第一次进来tree为根结点
        cout << "根节点为空,无法删除\n";
        return false;
    }
    Tree temp = tree;
    Tree fathertemp;    //记录一下父结点,删除时,把删除结点的父结点的左或者右结点附空
    while (true) {
        if (temp->leftchild != NULL) {  //删除左子结点或者右子结点的时候判断一下是否为空
            cout << "输入 1 选择左子结点,";
        }
        if (temp->rightchild != NULL) {
            cout << "输入 2 选择右子结点,";
        }
        cout << "输入 3 删除此结点,输入 其他 退出删除操作\n";
        int selected;
        cin >> selected;
        if (selected == 1 && temp->leftchild != NULL) { //选择左子结点或者右子结点的时候需要判断左子结点或者右子结点是否为空
            fathertemp = temp;  //转向左子结点或者右子结点的时候,记录一下父结点
            temp = temp->leftchild;
        } else if (selected == 2 && temp->leftchild != NULL) {
            fathertemp = temp;
            temp = temp->rightchild;
        } else if (selected == 3) {
            if (fathertemp->leftchild == temp) {    //删除结点如果是父结点的左结点,就把父结点的左结点附空
                fathertemp->leftchild = NULL;
            }
            if (fathertemp->rightchild == temp) {   //右结点同理
                fathertemp->rightchild = NULL;
            }
            Tree t = temp;  //保存一下待删除结点,把待删除结点delete,释放空间
            temp = NULL;//直接指向NULL为什么不能删除????????????

            delete t;
            return true;
        } else {
            return false;   //输入其他,退出删除操作
        }

    }
}

    //层序遍历(非递归)
void treeFloorPrint(Tree tree) {
    cout << "层序遍历:";
    queue<Tree> q;  //使用队列存结点,同一层的在一块挨着,循环同一层的时候,末尾添加的是下一层的左右子结点
    q.push(tree);   //根结点插入队列
    while (!q.empty()) {    //结点队列不对空,一直循环
        Tree temp = q.front();  //保存队列头结点
        q.pop();
        cout << temp->data << " ";  //输出头结点
        if (temp->leftchild != NULL) {  //左子结点或者右子结点存在,把子结点放入队列
            q.push(temp->leftchild);
        }
        if (temp->rightchild != NULL) {
            q.push(temp->rightchild);
        }
    }
    cout << "\n";
}

    //前序遍历(递归)
void treePreRecursionPrint(Tree tree) { //先输出根结点,在找左结点,在找右结点
    if (tree != NULL) {
        cout << tree->data << " ";
        treePreRecursionPrint(tree->leftchild);
        treePreRecursionPrint(tree->rightchild);
    }
}

    //前序遍历(非递归)
void treePrePrint(Tree tree) {
    cout << "前序遍历:";
    stack<Tree> nodes;  //用栈的原因:后进先出,后面进来的结点是下面的,先弹出这个结点,接着找右子结点
    while (tree != NULL || !nodes.empty()) {    //当前结点不为空,或者结点栈不为空,一直循环,说明还有未输出的结点
        while (tree != NULL) {  //只要结点不为空,一直找左子结点
            nodes.push(tree);
            cout << tree->data << " ";  //输出当前结点,前序遍历,先输出根结点
            tree = tree->leftchild; //不断找左子结点
        }
            //上面循环结束,说明tree结点没有左子树
        if (!nodes.empty()) {   //只要结点队列不为空,就弹出一个结点
            tree = nodes.top(); //弹出没有左子树的结点
            nodes.pop();
            tree = tree->rightchild;    //转到这个树的右子结点,继续循环
        }
    }
    cout << "\n";
}

    //中序遍历(递归)
void treeMidRecursionPrint(Tree tree) { //先输出左结点,在输出根结点,输出右结点
    if (tree != NULL) {
        treeMidRecursionPrint(tree->leftchild);
        cout << tree->data << " ";
        treeMidRecursionPrint(tree->rightchild);
    }
}
    //中序遍历(非递归)
void treeMidPrint(Tree tree) {  //与前序遍历大概相同,不过是先输出左子结点
    cout << "中序遍历:";
    stack<Tree> nodes;
    while (tree != NULL || !nodes.empty()) {
        while (tree != NULL) {
            nodes.push(tree);
            tree = tree->leftchild;
        }
        //当左子结点遍历完后,输出根结点,然后再转到右结点
        if (!nodes.empty()) {
            tree = nodes.top();
            nodes.pop();
            cout << tree->data << " ";
            tree = tree->rightchild;
        }
    }
    cout << "\n";
}

    //后序遍历(递归)
void treePostRecursionPrint(Tree tree) {    //先输出左子结点,在输出右子结点,输出根结点
    if (tree != NULL) {
        treePostRecursionPrint(tree->leftchild);
        treePostRecursionPrint(tree->rightchild);
        cout << tree->data << " ";
    }
}

    //后序遍历(非递归)
void treePostPrint(Tree tree) { //后序遍历与前两种不太一样,需要找一个变量存一下上一个访问的结点
    cout << "后序遍历:";           //因为要把左右结点都循环完才能输出根结点,
    stack<Tree> nodes;
    Tree lastnode;
    while (tree != NULL || !nodes.empty()) {
        while (tree != NULL) {  //左结点不为空就一直找到底,把结点保存到栈里面
            nodes.push(tree);
            tree = tree->leftchild;
        }
        tree = nodes.top(); //此结点左子结点不存在,拿到这个根结点
        if (tree->rightchild == NULL || lastnode == tree->rightchild) { //看右结点是否存在或者是否被访问(如果右结点被访问了,可以输出根结点了)
            cout << tree->data << " ";  //当前根结点被访问了
            nodes.pop();
            lastnode = tree;    //当前根结点被访问后,记录最后一个访问的结点
            tree = NULL;    //把访问过的结点附空
        } else {            //如果右结点存在并且没有被访问的话,转到右结点,重复循环把右结点都访问完,
                            // 当结点访问后,lastnode记录的就是每次最后一个访问的结点
            tree = tree->rightchild;
        }
    }
    cout << "\n";
}

    //返回二叉树的深度(递归)
int Depth (Tree tree) {
    if (tree == NULL) {
        return 0;
    }
    return max(Depth(tree->leftchild), Depth(tree->rightchild)) + 1;    //找左或右结点的最大深度 + 1(加一是加上当前这一层)
                                                            //不断循环这种操作
}

    //格式化输出二叉树(直接看顺序存储的注释就可以,格式化输出就是找二叉树的规律)
void treePrint(Tree tree) { //与顺序打印二叉树基本相似(如果结点为空的时候,要创建一个0结点,给0结点的左右结点都附空)
    cout << "打印二叉树:\n";
    int depth = Depth(tree);
    queue<Tree> q;
    q.push(tree);
    for (int i = 0; i < depth; i++) {
        int space = 0;
        for (int j = 0; j < depth - 1 - i; j++) {
            space = space * 2 + 1;
        }
        for (int j = 0; j < space; j++) {
            cout << " ";
        }
        for (int j = 0; j < pow(2, i); j++) {
            cout << q.front()->data;
            if (q.front()->leftchild != NULL) {
                q.push(q.front()->leftchild);
            } else {
                TreeNode *t = new TreeNode;
                t->data = 0;
                t->leftchild = NULL;
                t->rightchild = NULL;
                q.push(t);
            }
            if (q.front()->rightchild != NULL) {
                q.push(q.front()->rightchild);
            } else {
                TreeNode *t = new TreeNode;
                t->data = 0;
                t->leftchild = NULL;
                t->rightchild = NULL;
                q.push(t);
            }
            q.pop();

            for (int k = 0; k < space * 2 + 1; k++) {
                cout << " ";
            }
        }
        cout << "\n";
    }
}

int main() {
    Tree tree = NULL;
    int temp;
    while (true) {
        cout << "输入 1 插入结点,输入 2 删除子树,输入 其他 遍历打印二叉树" << endl;
        cin >> temp;
        if (temp == 1) {
            cout << "请输入插入结点的值" << endl;
            cin >> temp;
            treeInsert(tree, temp);
        }else if (temp == 2) {
            treeDelete(tree);
        }
        else {
            treePrePrint(tree);
//            cout << endl;
//            treePreRecursionPrint(tree);
            treeMidPrint(tree);
//            cout << endl;
//            treeMidRecursionPrint(tree);
            treePostPrint(tree);
//            cout << endl;
//            treePostRecursionPrint(tree);
            treeFloorPrint(tree);
            cout << "树的深度:" << Depth(tree) << endl;
            treePrint(tree);
        }
    }
    return 0;
}