zoukankan      html  css  js  c++  java
  • 数据结构之二叉树————C++实现

    这篇博客重点介绍数据结构的C++实现,不太设计理论细节,如果想了解细节,可以去参照严蔚敏老师的数据结构一书。

    一般的树都可以转换为二叉树,且二叉树的存储结构及操作都较为简单,因此先介绍下树中的二叉树类型。

    二叉树是n个结点的有限集,它或者是空集(n=0),或者有一个根结点及最多两棵互不相交的,分别称作这个根的左子树和右子树的二叉树组成。

    二叉树有五种基本形态:(1)空集(2)根的左右子树都为空(3)根的右子树为空(4)根的左子树为空(5)根的左右子树皆不为空。

    二叉树的逻辑结构:

    二叉树与无序树不同,二叉树中,每个结点最多只能有两棵子树,并且无左右之分。另外,二叉树与度数为2的有序树不同,在有序树中,虽然一个极点的孩子之间是有左右次序的,但若该结点只有一个孩子,就无须区分其左右次序;而二叉树中,即使是一个孩子也有左右之分。所以,二叉树不是树的特殊情形。

    二叉树的特殊性质(在此不证明):

    (1)二叉树第i层上的结点数目最多为2i-1(i>=1);

     (2)深度为k的二叉树至多有2k-1个结点(k>=1);

     (3)在任意一颗二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

     完全二叉树:若二叉树的高度为h,除第h层外,其他各层(1-h-1)的结点数都达到最大个数,并且最下一层上的结点都集中在该层最左边的若干位置上,则此二叉树被称为完全二叉树。

     满二叉树:一颗深度为k且有2k-1个结点的二叉树称为满二叉树。

     还有很多性质,在这不在多讨论。可以参考其他书籍。

    下面介绍下二叉树的实现:

    顺序存储结构实现的主要思想:将一般二叉树填上一些空结点,使之成为“完全二叉树”,并且按完全二叉树形式给结点编号。其中的空结点便是顺序存储过程中浪费的空间。

    ///////////////////////////////////////////////////////////////////
    ///SeqBinaryTree.h 二叉树(采用顺序存储结构)数据结构C++类定义(基类)
    ///////////////////////////////////////////////////////////////////
    
    #ifndef SEQBINARYTREE_H
    #define SEQBINARYTREE_H
    #include<iostream>
    #include "SeqBinaryTree.h"
    using namespace std;
    template<typename ElemType>
    class SeqBinaryTree
    {
    public:
        // 把一棵顺序存储的二叉树置空
        void clear();
        // 取最后一个节点的顺序存储空间的下标
        int getFinalIndex();
        //返回二叉树的首地址
        ElemType* getInitialAddress();
        //取下标为i的结点(即第i+1个结点)
        ElemType getNode(int i);
        //设置下标为i的结点(即第i+1个结点)的值
        void setNode(int i, ElemType value);
        //设置最后一个结点的下标
        void setFinalIndex(int i);
        //重载赋值运算符的定义
        SeqBinaryTree<ElemType> operater = (SeqBinaryTree<ElemType> rightT);
        void read(istream& in);
        void display(ostream& out);
        //******下面为系统自动调用的构造函数和析构函数声明********
        
        //构造函数
        SeqBinaryTree();
        //析构函数
        ~SeqBinaryTree();
        //拷贝初始化构造函数
        SeqBinaryTree(const SeqBinaryTree<ElemType>& seqT);
    protected:
        ElemType* initialAddress;
        int finalIndex;
    };
    
    ///二叉树的C++类实现
    
    //功能:把一棵顺序存储的树置空
    template<typename ElemType>
    void SeqBinaryTree<ElemType>::clear()
    {
        if (initialAddress)
        {
            delete[] initialAddress;
            initialAddress = NULL;
            finalIndex = -1;
        }
    }
    //取最后一个结点在顺序存储空间的下标
    template <typename ElemType>
    int SeqBinaryTreee<ElemType>::getFinalIndex()
    {
        return finalIndex;
    }
    //返回二叉树的首地址
    template <typename ElemType>
    ElemType* SeqBinaryTree<ElemType>::getInitialAddress()
    {
        retuen initialAddress;
    }
    //取下标为i的结点(即第i+1个点)
    template <typename ElemType>
    ElemType SeqBinaryTree<ElemType>::getNode(int i)
    {
        if (i < 0 || i>finalIndex)
        {
            cerr << OVERFLOW;
            exit(1);
        }
        return initialAddress[i];
    }
    //重载赋值运算符的定义
    template<typename ElemType>
    SeqBinaryTree<ElemType> SeqBinaryTree<ElemType>::operater = (SeqBinaryTree<ElemType> rightT)
    {
        if (this != &rightT)
        {
            finalIndex = rightT.finalIndex;
    
            if (finalIndex != -1)
            {
                initialAddress = new ElemType[finalIndex + 1];
                assert(initialAddress != 0);
    
                for (int i = 0; i <= finalIndex; i++)
                {
                    initialAddress[i] = rightT.initialAddress[i];
                }
            }
        }
    
        return *this;
    }
    
    //设置下标为i的结点(即第i+1个结点)的值
    template<typename ElemType>
    void SeqBinaryTree<ElemType>::setNode(int i, ElemType value)
    {
        initialAddress[i] = value;
        //如果i大于最后一个结点在顺序存储空间的下标
        if (i>finalIndex)
        {
            finalIndex = i;
        }
    }
    //设置最后一个结点在顺序存储空间的下标
    template <typename ElemType>
    void SeqBinaryTree<ElemType>::setFinalIndex(int i)
    {
        finalIndex = i;
    }
    //***********************构造函数,析构函数,拷贝初始化函数*************
    
    //构造函数
    template<typename ElemType>
    SeqBinaryTree<ElemType>::SeqBinaryTree()
    {
        initialAddress = NULL;
        finalIndex = -1;
    }
    //析构函数
    template<typename ElemType>
    SeqBinaryTree<ElemType>::~SeqBinaryTree()
    {
        clear();
    }
    
    //拷贝初始化函数
    template<typename ElemType>
    SeqBinaryTree<ElemType>::SeqBinaryTree(const ElemType& seqT)
    {
        initialAddress = NULL;//设置当前树的顺序存储空间的首地址为空
        finalIndex = seqT.finalIndex;
        if (finaIndex != -1)
        {
            initialAddress = new ElemType[finalIndex + 1];
            assert(initialAddress != 0);
    
            for (int i = 0; i < = finalIndex; i++)
            {
                initialAddress[i] = seqT.initialAddress[i];
            }
        }
    }
    //输入顺序存储二叉树
    template<typename ElemType>
    void SeqBinaryTree<ElemType>::read(istream& in)
    {    
        int i = finalIndex;
        while (in>> initialAddress.[i])
        {
            i++;
        }
        finalIndex = i;
    }
    //重载输入运算符
    template<typename ElemType>
    istream& operator >> (istream& in, SeqBinaryTree<ElemType>& T)
    {
        T.read(in);
        return in;
    }
    //输出顺序存储二叉树
    template<typename ElemType>
    void SeqBinaryTree<ElemType>::display(ostream& out)
    {
        int i = 0;
        while (i!=finalIndex)
        {
            out << initialAddress[i];
            i++;
        }
    }
    //重载输出运算符
    template<typename ElemType>
    ostream& operator << (ostream& out, SeqBinaryTree<ElemType>& T)
    {
        T.diplay(out);
        return out;
    }
    #endif

     如果二叉树不是满二叉树、完全二叉树,或是二叉树经常执行插入和删除等操作时,都应考虑用链式存储结构存放二叉树。

    二叉树链式存储结构的实现:

    用这种方式存储二叉树时,每个结点除了存储结点本身的数据data外,还应存储一个左指针lchild和一个右指针rchild,分别指向左孩子和右孩子。如果在操作中需要查找结点的双亲,可以再加上指向双亲结点的指针parent。

    在这给出的结构中不包括双亲结点。

    #include <iostream>
    #include<assert.h>
    #include "SqQueue.h"//用到顺序存储结构实现的队列(前一篇博客已经给出)
    #include "SeqBinaryTree.h"//用到顺序存储结构实现二叉树(上边已经给出)
    #include "SqStack.h"//用到顺序存储结构实现的栈(以后将会给出)
    using namespace std;
    #define LH 1 //左高
    #define EH 0 //等高
    #define RH -1 //右高
    
    /////////////////////////////////////////////////////////
    //二叉树(采用二叉链表存储)结点的数据结构C++类声明
    template<typename ElemType>
    class LinkBinaryTree
    {
    public:
        //二叉树(采用二叉链表存储)结点的数据结构C++定义
        class Node
        {
        public:
            Node() :lchild(NULL), rchild(NULL){};//Node的构造函数,左右指针初始化为空
    
            ElemType data;  //结点的数据域
            class Node* lchild;//结点的左指针
            class Node* rchild;//结点的右指针
        };
        typedef Node* NodePointer;
    
    public:
        //把二叉树置空
        void clear();
        //求二叉树的叶子数
        int countLeaf();
        //求二叉树的结点数
        int countNode();
        //递归求二叉树的深度
        int depth();
        //显示二叉树的顺序存储结构
        void displaySeqTree();
        //交换二叉树中所有结点的左右子树
        void exchangeRchild();
        //取根指针
        NodePointer getRoot();
        //中序递归遍历二叉树
        void  inOrderTraverse();
        //判断是否为空二叉树
        bool isEmpty();
        //按层次顺序遍历二叉树
        void layOrderTraverse();
        //二叉树的二叉链表存储转换为顺序存储结构
        void linkToSequential();
        //非递归中序遍历二叉树
        void noRecursionInOrderTraverse();
        //后序递归遍历二叉树
        void postOrderTraverse();
        //前序递归遍历二叉树
        void preOrderTraverse();
        //随机生成一颗二叉树
        void randomCreate();
        //二叉树的顺序存储转换为二叉链表存储结构
        void sequentialToLink(SeqBinariTree<ElemType> T);
        
    //为实现公有操作定义的辅助函数
    private:
        //拷贝初始化构造函数的辅助函数
        void LinkBinaryTree_aux(NodePointer& p, NodePointer other);
        //求二叉树叶子数的辅助函数
        int countLeaf_aux(NodePointer p);
        //求二叉树结点数的辅助函数
        int countNode_aux(NodePointer p);
        //回收二叉树结点存储空间的辅助函数
        void deleteNode_aux(NodePointer p);
        //递归求二叉树深度的辅助函数
        int depth_aux(NodePointer p);
        //交换二叉树中所有结点左右子树的辅助函数
        void exchangeLRchild_aux(NodePointer p);
        //中序递归遍历二叉树的辅助函数
        void inOrderTraverse_aux(NodePointer p);
        //二叉树的二叉链表存储转换为顺序存储结构的辅助函数
        void linkToSequential_aux(SeqBinaryTree<ElemType>& tempT, NodePointer p, int i);
        //后续递归遍历二叉树的辅助函数
        void postOrderTraverse_aux(NodePointer p);
        //前序递归遍历二叉树的辅助函数
        void preOrderTraverse_aux(NodePointer p);
        //二叉树的顺序存储结构转换为二叉链表存储结构的辅助函数
        void sequentialToLink_aux(int i, NodePointer& sybroot);
    
    /////////////////////////下面为系统自动调用的构造函数、析构函数及输入输出函数的声明
    
    public:
        //二叉树(采用二叉链表存储)构造函数
        LinkBinaryTree();
        //二叉树(采用二叉链表存储)析构函数
        virtual ~LinkBinaryTree();
        //二叉树(采用二叉链表存储)拷贝初始化构造函数
        LinkBinaryTree(const LinkBinaryTree<ElemType>& otherT);
        //输入二叉树(采用二叉链表存储)
        void read(istream& in);
        //输出二叉树(采用二叉链表存储)
        void display(ostream& out);
    protected:
        NodePointer root;//二叉树的根指针(采用二叉链表存储)
        SeqBinaryTree<ElemType> seqT;//二叉树对应的顺序存储树
    };
    
    
     ///////二叉树(采用二叉链表存储)数据结构C++实现   
    
    //功能:把二叉树置空
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::clear()
    {
        seqT.clear();
        deleteNode_aux(root);
        root = NULL;
    }
    
    
    //回收二叉树结点存储空间的辅助函数
    template<typename ElemType>
    void LinkBinaryTree<ElemType>::deleteNode_aux(NodePointer p)
    {    
        //按后续遍历方式逐一回收没一个结点
        if (p)
        {
            deleteNode_aux(p->lchild);
            deleteNode_aux(p->rchild);
            delete p;
        }
    }
    //求二叉树的叶子数
    template<typename ElemType>
    int LinkBinaryTree<ElemType>::countLeaf()
    {
        return countLeaf_aux(root);
    }
    
    //求二叉树叶子数的辅助函数
    template <typename ElemType>
    int LinkBinaryTree<ElemType>::countLeaf_aux(NodePointer p)
    {
        int num;//预存放最终的叶子结点数
        static int i = 0;//存放叶子结点累计数,初始化为0
        if (p)
        {   //如果指针p不为空且所指结点的左右子树指针均为空,则叶子结点累计数加1.
            if (!p->lchild && !p->rchild)
            {
                ++i;
            }
            countLeaf_aux(p->lchild);
            countLeaf_aux(p->rchild);
        }
        //如果此时递归过程指针p指向了根节点,说明已经累计完所有叶子几点数。(涉及到递归的知识。)
        if (p == root)
        {
            num = i;//记录叶子结点数
            i = 0;//叶子结点累计数清0,以便再次调用此函数求叶子结点数。
        }
        return num;
    }
    
    //求二叉树的结点数
    template <typename ElemType>
    int LinkBinaryTree<ElemType>::countNode()
    {
        return countNode_aux(root);
    }
    
    //求二叉树结点数的辅助函数
    template<typename ElemType>
    int LinkBinaryTree<ElemType>::countNode_aux(NodePointer p)
    {
        int num;//预存放最终结点个数
        static int i = 0;//存放结点累计数,初始化为0
        if (p)//如果p不为空
        {
            i++;
            countNode_aux(p->lchild);
            countNode_aux(p->rchild);
        }
        //同上
        if (p==root)
        {
            num = i;
            i = 0;
        }
        return num;
    }
    
    //递归求二叉树的深度
    template<typename ElemType>
    int LinkBinaryTree<ElemType>::depth()
    {
        return depth_aux(root);
    }
    
    //递归求二叉树深度的辅助函数
    template <typename ElemType>
    int LinkBinaryTree<ElemType>::depth_aux(NodePointer p)
    {
         int lDep =0;//预存放左子树的深度
         int rDep=0;//预存放右子树的深度
        if (!p)
        {
            return 0;
        }
        else
        {
            lDep = depth_aux(p->lchild);
            rDep = depth_aux(p->rchild);
            return (lDep > rDep ? lDep : rDep) + 1;
        }
    
    }
    //交换二叉树中所有结点的左右子树
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::exchangeRchild()
    {
        exchangeLRchild_aux(root);
        //重新生成二叉树对应的顺序存储结构
        linkToSequential();
    }
    
    //交换二叉树中所有结点左右子树的辅助函数
    template <typename ElemType>
    void LinkBianryTree<ElemType>::exchangeLRchild_aux(NodePointer p)
    {
        NodePointer s;//预存放当前结点左孩子的指针
        if (p)
        {
            exchangeLRchild_aux(p->lchild);
            exchangeLRchild_aux(p->rchild);
            s = p->lchild;
            p->lchild = p->rchild;
            p->rchild = s;
        }
    }
    
    //二叉树的二叉链表存储转换为顺序存储结构
    
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::linkToSequential()
    {
        int max_total;//预存放具有同样深度的满二叉树的结点数
        SeqBinaryTree<ElemType> tempT;//临时顺序存储结构的二叉树tempT
        if (!root)
        {
            seqT.clear();
            return;
        }
        //计算具有相同深度的满二叉树的结点数
        max_total = 1;
        for (int d = 1; d <= depth(); ++d)
        {
            max_total *= 2;
        }
        max_total - = 1;
        //根据对应的满二叉树的结点数,申请max_total个结点
        tempT.initialAddress = new Node[max_total];
        tempT.finalIndex = max_total - 1;
        linkToSequential_aux(tempT, root, 0);
        seqT = tempT;
    }
    
    //二叉树的二叉链表存储 转换为顺序存储结构的辅助函数
    //各节点对应的顺序存储结构位置为0
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::linkToSequential_aux(SeqBinaryTree<ElemType>& tempT, NodePointer p, int i)
    {
        //用指针p所指结点的数据域设置为二叉树顺序存储空间下标为i的值
        tempT.setNode(i, p->data);
        //用指针p所指结点的左指针和2*i+1下标值自递归
        //把左子树的二叉链表存储转换为顺序存储结构
        if (p->lchild != NULL)
        {
            linkToSequential_aux(tempT, p->lchild, 2 * i + 1);
        }
        //用指针p所指结点的左指针和2*i+2下标值自递归
        //把右子树的二叉链表存储转换为顺序存储结构
        if (p->rchild != NULL)
        {
            linkToSequential_aux(tempT, p->rchild, 2 * i + 2);
        }
    }
    //取根指针
    template<typename ElemType>
    LinkBinaryTree<ElemType>::NodePointer LinkBinaryTree<ElemType>::getRoot()
    {
        return root;
    }
    //中序递归遍历二叉树
    template<typename ElemType>
    void LinkBinaryTree<ElemType>::inOrderTraverse()
    {
        inOrderTraverse_aux(root);
    }
    //中序递归遍历二叉树的辅助函数
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::inOrderTraverse_aux(NodePointer p)
    {
        if (p)
        {
            inOrderTraverse_aux(p->lchild);
            cout << p->data;
            inOrderTraverse_aux(p->rchild);
        }
    }
    
    //判断是否为空二叉树
    template <typename ElemType>
    bool LinkBinaryTree<ElemType>::isEmpty()
    {
        return root ? false : true;
    }
    //按层次顺序遍历二叉树
    template<typename ElemType>
    void LinkBinaryTree<ElemType>::layOrderTraverse()
    {
        NodePointer p;//预指向当前遍历节点的指针
        SeQueue<NodePointer>Q;//预存放待遍历结点的指针队列
        if (root!=NULL)
        {
            Q.enQueue(p);
        }
        while (!Q.isEmpty())
        {
            Q.deQueuue(p);
            cout << p->data;
            if (p->lchild)
            {
                Q.enQueue(p->lchild);
            }
            if (p->rchild)
            {
                Q.enQueue(p->rchild);
            }
        }
    }
    
    
    //非递归中序遍历二叉树
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::noRecursionInOrderTraverse()
    {
        NodePointer p = root;//指向当前结点的指针,初始化为根指针
        SqStack<ElemType> S;
        while (p||!S.isEmpty())
        {
            if (p)
            {
                S.push(p);
                p = p->lchild;
            }
            else
            {
                S.pop(p);
                cout << p->data;
                p = p->rchild;
            }
    
        }
    }
    
    //后序递归遍历二叉树
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::postOrderTraverse()
    {
        postOrderTraverse_aux(root);
    }
    //后序遍历二叉树的辅助函数
    template<typename ElemType>
    void LinkBinaryTree<ElemType>::postOrderTraverse_aux(NodePointer p)
    {
        if (p)
        {
            postOrderTraverse_aux(p->lchild);
            postOrderTraverse_aux(p->rchild);
            cout << p->data;
        }
    }
    
    //前序遍历二叉树
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::preOrderTraverse()
    {
        preOrderTraverse_aux(root);
    }
    
    //前序遍历二叉树的辅助函数
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::preOrderTraverse_aux(NodePointer p)
    {
        if (p)
        {
            cout << p->data;
            preOrderTraverse_aux(p->lchild);
            preOrderTraverse_aux(p->rchild);
        }
    }
    
    //二叉树的顺序存储转换为二叉链表存储结构
    template <typename ElemType>
    void LinkBinaryTree<ElemType>::sequentialToLink(SeqBinaryTree<ElemType> T)
    {
        seqT = T;
        sequentialToLink_aux(0, root);
    }
    
    //二叉树的顺序存储转换为二叉链表存储结构的辅助函数
    template <typename ElemType>
    void LinkBinaryTree<ElemtType>::sequentialToLink(int i, NodePointer p)
    {
        int n = seqT.getFinalIndex();//得到顺序存储二叉树最后一个结点的下标
    
        if (n==-1)//如果顺序存储二叉树最后一个结点的下标为-1,则指针p设置空
        {
            p = NULL;
            return;
        }
        //如果顺序存储二叉树最后一个结点的下标不为-1,则申请一个二叉链表
        p = new LinkBinaryTree<ElemType>::Node;
        assert(p != 0);
    
        //把顺序存储二叉树下标为i的结点的数据域赋值给新结点的数据域
        p->data = seqT.getNode(i);
       //如果顺序存储二叉树下标为i的结点无左子树,则新结点的左指针为空,否则,
        //用下标为2*i+1和新结点的左指针自递归,把下标为i的结点的左子树转换为
        //二叉链表存储结构
        if (2 * i + 1>n || seqT.getNode(2 * i + 1) == ' ')
        {
            p->lchild = NULL;
        }
        else
        {
            sequetialToLink_aux(2 * i + 1, p->lchild);
        }
        //如果顺序存储二叉树下标为i的结点无右子树,则新结点的左指针为空,否则,
        //用下标为2*i+2和新结点的左指针自递归,把下标为i的结点的右子树转换为
        //二叉链表存储结构
        if (2*i+2>n||seqT.getNode(2*i+2)==' ')
        {
            p->rchild = NULL;
        }
        else
        {
            sequentialToLink_aux(2 * i + 2, p->rchild);
        }
    }
    
    //************下面为系统自动调用的构造函数、析构函数及输入输出函数的实现***********
    //二叉树(采用二叉链表存储)构造函数
    template<typename ElemType>
    LinkBinaryTree<ElemType>::LinkBinaryTree()
    {
        root = NULL;
        seqT.clear();
    }
    
    //二叉树拷贝初始化函数
    template<typename ElemType>
    LinkBinaryTree<ElemType>::LinkBinaryTree(const LinkBinaryTree<ElemType>& otherT)
    {
        if (!otherT.root)
        {
            root = NULL;
            seqT.clear();
        }
        else
        {
            LinkBinaryTree_aux(root, otherT.root);
            linkToSequential();
        }
    }
    
    //拷贝初始化构造函数的辅助函数
    template<typename ElemType>
    void LinkBinaryTree<ElemType>::LinkBinaryTree_aux(NodePointer& p, NodePointer otherP)
    {
        if (!otherP)
        {
            p = NULL;
            return;
        }
        p = new Node;
        assert(p != 0);
        p->data = otherP->data;
        if (!otherP->lchild)
        {
            p->lchild = NULL;
        }
        else
        {
            LinkBinaryTree_aux(p->lchild, otherP->lchild);
        }
        if (!otherP->rchild)
        {
            p->rchild = NULL;
        }
        else
        {
            LinkBinaryTree_aux(p->rchild, otherP->rchild);
        }
    }
    
    //二叉树析构函数
    template<typename ElemType>
    LinkBinaryTree<ElemType>::~LinkBinaryTree()
    {
        clear();
    }
    
    //输入二叉树(采用二叉链表存储)
    template<typename ElemType>
    void LinkBinaryTree<ElemType>::read(istream& in)
    {
        cout << "存储方式创建一颗二叉树" << endl;
        seqT.read();
        sequentialToLink_aux(0, root);
    }
    
    //重载输入运算符的定义
    template<typename ElemType>
    istream& operator>>(istream& in, LinkBinaryTree<ElemType>& bT)
    {
        bT.read();
        return in;
    }
    
    //输出二叉树
    template<typename ElemType>
    void LinkBinaryTree<ElemType>::display(ostream& out)
    {
        out << seqT.initialAddress;
    }
    
    //重载输出运算符
    
    template<typename ElemType>
    ostream& operator<<(ostream& out, LinkBinaryTree<ElemType>& bT)
    {
        bT.display(out);
        return out;
    }

    水平有限,难免有错误之处,请指正。

  • 相关阅读:
    Linux考试题附答案
    MariaDB数据库主从复制实现步骤
    LinuxCentos系统安装Mariadb过程记录
    LinuxCentos系统安装Nginx过程记录
    VMware虚拟机不能联网的解决办法
    Linux centos下设置定时备份任务
    如何修改本地hosts文件?
    mysql用户授权以及权限收回
    Ubuntu系统下完全卸载和安装Mysql
    C++之类和对象的使用(一)
  • 原文地址:https://www.cnblogs.com/ITxiansheng/p/3664915.html
Copyright © 2011-2022 走看看