二级指针创建二叉树节点与一级指针创建二叉树节点

Posted xuan01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二级指针创建二叉树节点与一级指针创建二叉树节点相关的知识,希望对你有一定的参考价值。

 1、c++中的struct结构体变量定义 可以直接 “类型名 变量名”,c中只能 “struct 类型名 变量名” ,可以通过typedef 达到相同的效果;

   struct _x1 ...x1; 是定义了类_x1和_x1的对象实例x1,

  typedef struct _x2 ... x2;  定义了类_x2和_x2的类别名x2 ;

  typedef struct int num;int age;aaa,bbb,ccc;相当于三个类型别名;

2、二级指针:p置空,p的值 (nullptr) 其实就是0;但是p本身是一个变量,仍然具有地址,二级指针指向p;

#include <bits/stdc++.h>
using namespace std;
int main()

    int* p = nullptr;
    int**p1 = nullptr;
    p1 = &p;
    cout << p << endl;
    cout << p1 << endl;
    system("pause");
    return 0;


/*
0
0x61ff08
*/

反例:调用函数并没有改变p值;

#include <bits/stdc++.h>
using namespace std;

void first__ptr(int *ptr)
    ptr = new int(100);
    cout << ptr << endl;
    delete ptr;


int main()
    int* p = nullptr;
    cout << p << endl;
    first__ptr(p);
    cout << p << endl;
    system("pause");
    return 0;

/*
0
0xd318b0
0
*/

修改:通过传递二级指针,操作*ptr达到修改p的值;

#include <bits/stdc++.h>
using namespace std;

void first__ptr(int *ptr)
    ptr = new int(100);
    cout << ptr << endl;
    delete ptr;

void second_ptr(int **ptr)
    *ptr = new int(100); 
    cout << *ptr << endl;
    delete ptr;


int main()
    int* p = nullptr;
    int** p1 = &p;
    cout << p << endl;
    second_ptr(p1);
    cout << p << endl;
    system("pause");
    return 0;

/*
0
0x1e18b0
0x1e18b0
*/

3、创建二叉树节点时使用二级指针(参考二叉树创建为什么用二级指针 - Pearl_zju - 博客园 (cnblogs.com) )

首先是二叉树创建需要使用指针代表节点的缘故,达到链表的效果;也就是每个节点自身就是个一级指针;

个人总结:要使用一级指针的话:递归函数形参是一级指针,但是得有返回值,也是一级指针,函数中操作的是指针本身,返回值也是指针本身;Node* createNode(Node* node) ; 

     要使用二级指针的话:递归函数形参是二级指针,但是不需要返回值; 函数中操作的是*node1,即操作一级指针变量的值,void createNode(Node ** node1);

因为一级指针传递进去的是值传递,不影响调用者的值;但是可以通过函数返回值赋值给调用者,达到改变原值的效果;二级指针是一级指针变量的地址,那么操作*node1,也就是在操作一级指针;

这儿可以理解为:将节点和二级指针都降一级; 节点自身相当于一个 int a变量,二级指针相当于一级指针 int*p = &a,那么要想在另一个函数中改变a的值,传参的时候就需要传递变量a的地址,操作*p就相当于改变a的值,函数也不需要返回值就能改变原值了;

代码实现

#include <bits/stdc++.h>
using namespace std;

typedef struct Node
    int data;
    Node* left, *right;
Node,*Tree;

/*二级指针*/
void Create(Tree* T)
    int num;
    /*CTRL + Z 结束*/
    if(cin >> num)
        *T = new Node();
        (*T)->data = num;
        Create(&((*T)->left));
        Create(&((*T)->right));
    

/*一级指针*/
Tree create1(Tree T)
    int num;
    if(cin >> num)
        T = new Node();
        T->data = num;
        T->left = create1(T);
        T->right = create1(T);
    else
        T = nullptr;
    
    return T;

void preorder(Tree T)
    if(T == nullptr)
        return;
    
    cout << T->data << "\\t";
    preorder(T->left);
    preorder(T->right);


int main()
    Tree T;
    cout << "put in binary tree:" << endl;
    //Create(&T);
    T = create1(T);
    preorder(T);

    cout << endl;
    system("pause");
    return 0;

/*
put in binary tree:
7 2 10  1 3 8 11^Z
7       2       10      1       3       8       11
*/

 

线索化二叉树详解

线索化二叉树详解

说明

  1. 线索化二叉树,由字面意思,就是将二叉树的节点拿线索连接起来
  2. 实质上,也就是将二叉树的叶子节点左右指针域彼此连接一个节点
  3. 二叉树的非叶子节点的左右指针域都各自连接了一个节点,但是叶子节点的左右指针域是空的,因此考虑将叶子节点的左右指针域按照某种遍历次序连接起来
  4. 按照二叉树的遍历方式,有前序中序后续三种遍历方式,因此可以形成三种链式结构
  5. 每个叶子节点前一个节点称为前驱节点,后一个节点称为后继节点,如果当前节点没有前驱或者后继节点,则直接置为空
  6. 以中序线索化二叉树为例,编写中序线索化二叉树的方法
  7. 先判断当前节点是否为空,如果为空,则直接返回
  8. 否则先向左递归线索化二叉树的左子树
  9. 然后再线索化当前节点,定义属性pre保存当前节点的前一个节点,因此当前节点的前一个节点置为pre
  10. 注意当前节点的后一个节点,需要用pre保存当前节点,然后遍历到后一个节点,然后用pre指向
  11. 注意第一个节点和最后一个节点
  12. 中序线索化如下,前序和后续类似

源码及分析

节点类
//创建节点
class HeroNode{
    //编号
    private int no;
    //姓名
    private String name;
    //左子树
    private HeroNode left;
    //右子树
    private HeroNode right;

    //线索化的前驱节点类型,是节点还是树,假定 0 为树 , 1 为节点
    private int leftType;
    //线索化的后继节点类型
    private int rightType;

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    //构造器,左子树和右子树默认为空
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name=\'" + name + \'\\\'\' +
                \'}\';
    }
    //删除节点

    /**
     *
     * @param no 要删除的节点编号
     */
    public void delNode(int no){
        //判断当前节点的左子树是否为空,如果不为空,再判断是否为要删除的节点
        if (this.left != null && this.left.no == no){
            this.left = null;
        }
        //判断当前节点的右子树是否为空,如果不为空,再判断是否为要删除的节点
        if (this.right != null && this.right.no == no){
            this.right = null;
        }
        //否则向左向右递归
        if (this.left != null){
            this.left.delNode(no);
        }
        if (this.right != null){
            this.right.delNode(no);
        }
    }


    //前序中序后序遍历主要区别在于父节点的输出位置不同,
    /**
     * 前序遍历先输出父节点信息,然后判断左子树是否为空,如果不为空,则递归前序遍历
     * 然后再判断右子树是否为空,如果不为空,则递归遍历前序遍历
     */
    //前序遍历
    public void preOrder(){
        //先输入当前节点信息
        System.out.println(this);
        //然后判断当前节点的左子树是否为空
        if (this.left != null){
            this.left.preOrder();
        }
        //再判断当前节点的右子树是否为空
        if (this.right != null){
            this.right.preOrder();
        }

    }
    //中序遍历
    public void infixOrder(){
        //先判断当前节点的左子树是否为空
        if (this.left != null){
            this.left.infixOrder();
        }
        //再输出当前节点的信息
        System.out.println(this);
        //然后再判断当前节点的右子树是否为空
        if (this.right != null){
            this.right.infixOrder();
        }
    }
    //后序遍历
    public void postOrder(){
        //先判断当前节点的左子树是否为空
        if (this.left != null){
            this.left.postOrder();
        }
        //再判断当前节点的右子树是否为空
        if (this.right != null){
            this.right.postOrder();
        }
        //最后输出当前节点的信息
        System.out.println(this);
    }

    //前序查找

    /**
     * 前序遍历查找
     * @param no 要查找的节点编号
     * @return 返回查找的结果
     */
    public HeroNode preOrderSearch(int no){
        //先判断当前节点是不是要查找的节点
        if (this.no == no){
            return this;
        }
        //如果当前节点不是要查找的节点,则判断左子树是否为空,若不为空,则递归前序查找
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.preOrderSearch(no);
        }
        //如果在左子树找到,则直接返回
        if (resNode != null){
            return resNode;
        }
        //如果左子树也没有找到,则判断右子树是否为空,并递归
        if (this.right != null){
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }
    //中序查找

    /**
     * 中序遍历查找
     * @param no 要查找的节点编号
     * @return 返回查找的结果
     */
    public HeroNode infixOrderSearch(int no){
        //先判断当前节点左子树是否为空,如果不为空则递归中序查找
        //定义变量保存查找的结果
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.preOrderSearch(no);
        }
        //如果查找到,则直接返回
        if (resNode != null){
            return resNode;
        }
        //如果没有找到,判断当前节点是不是要查找的节点
        if (this.no == no){
            return this;
        }
        //如果还没有找到,则判断右子树是否为空,不为空则递归中序查找
        if (this.right != null){
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }
    //后序查找

    /**
     * 后续遍历查找
     * @param no 要查找的节点编号
     * @return 返回查找的结果
     */
    public HeroNode postOrderSearch(int no){
        //判断当前节点的左子树是否为空,如果不为空,则递归后续查找
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.postOrderSearch(no);
        }
        if (resNode != null){
            return resNode;
        }
        if (this.right != null){
            resNode = this.right.postOrderSearch(no);
        }
        if (resNode != null){
            return resNode;
        }
        if (this.no == no){
            return this;
        }
        return resNode;
    }
}

线索化二叉树类
//创建一颗线索化二叉树
class ThreaderBinaryTree{
    //二叉树必有根节点
    private HeroNode root;
    //定义变量指向前驱节点,默认为空
    private HeroNode pre = null;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //编写中序线索化二叉树的方法
    /**
     *
     * @param node node为当前要中序线索化的节点
     */
    public void infixThreadedBinaryTree(HeroNode node){
        //先判断当前节点是否为空
        if (node == null){
            return;
        }
        //如果不为空,先线索化左子树
        infixThreadedBinaryTree(node.getLeft());
        //再线索化当前节点
        //当前节点的前驱节点为pre,后继节点需要在下一个节点连通,因为是单向的

        //设置当前节点的前驱节点,并设置前驱节点类型
        if (node.getLeft() == null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        //设置当前节点的后继节点及其类型
        if (pre != null && pre.getRight() == null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        //让pre指向当前节点
        pre = node;
        //最后再线索化右子树
        infixThreadedBinaryTree(node.getRight());
    }
    //重载线索化的方法
    public void infixThreadedBinaryTree(){
        this.infixThreadedBinaryTree(root);
    }

    //删除节点
    /**
     *
     * @param no 要删除的节点编号
     */
    public void delNode(int no){
        //先判断二叉树是否为空
        if (this.root != null){
            //再判断当前root节点是不是要删除的节点
            if (this.root.getNo() == no){
                root = null;
            }else {
                this.root.delNode(no);
            }
        }else {
            System.out.println("二叉树为空,不能删除...");
        }

    }
    //前序遍历
    public void preOrder(){
        if (this.root != null){
            this.root.preOrder();
        }else {
            System.out.println("二叉树为空...");
        }
    }
    //中序遍历
    public void infixOrder(){
        if (this.root != null){
            this.root.infixOrder();
        }else {
            System.out.println("二叉树为空...");
        }
    }
    //后续遍历
    public void postOrder(){
        if (this.root != null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空...");
        }
    }
    //前序查找
    public HeroNode preOrderSearch(int no){
        if (this.root != null){
            return this.root.preOrderSearch(no);
        }else {
            return null;
        }
    }
    //中序查找
    public HeroNode infixOrderSearch(int no){
        if (this.root != null){
            return this.root.infixOrderSearch(no);
        }else {
            return null;
        }
    }
    //后续查找
    public HeroNode postOrderSearch(int no){
        if (this.root != null){
            return this.root.postOrderSearch(no);
        }else {
            return null;
        }
    }
}
测试类
public static void main(String[] args) {
        ThreaderBinaryTree threaderBinaryTree = new ThreaderBinaryTree();
        HeroNode root = new HeroNode(1,"tom");
        HeroNode node2 = new HeroNode(3,"jack");
        HeroNode node3 = new HeroNode(6,"smith");
        HeroNode node4 = new HeroNode(8,"king");
        HeroNode node5 = new HeroNode(10,"mary");
        HeroNode node6 = new HeroNode(14,"dop");
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        threaderBinaryTree.setRoot(root);

        //进行线索化
        threaderBinaryTree.infixThreadedBinaryTree();

        //测试线索化的结果
        System.out.println("node5的前一个节点 = " + node5.getLeft());
        System.out.println("node5的后一个节点 = " + node5.getRight());

    }

以上是关于二级指针创建二叉树节点与一级指针创建二叉树节点的主要内容,如果未能解决你的问题,请参考以下文章

二叉树与链表

6.2.2-1 指针与引用在二叉树创建的应用

二叉树的创建与遍历

二叉树与二叉搜索树

头指针不包含二叉树中的子节点

c使用二叉链表创建二叉树遇到的一些疑问和思考