zoukankan      html  css  js  c++  java
  • 二叉树算法引发的思考:指针参数传递、引用的陷阱

    最近想熟悉一下基本的数据结构和算法,于是写了一个二叉树程序,功能很简单,只有二叉树的建立和遍历。在这个过程中,却发现了一些平时没有注意到的细节问题,写出来作为总结和大家分享。

    待讨论和遇到的细节问题如下:

    (1)常见的定式思维:指针作为参数传递,是否就不需要再赋值,因为指针指向的值会同步更改,但是,你有考虑过,如果指针变量本身的值被更改了呢?怎么办?

    (2)比较不常用的用法,针对指针变量的引用,你用过吗?

    前提:

    (1)笔者考虑到自己一直混淆使用C和C++,于是,打算这个简单的算法程序采用纯C来写,因此使用的是C编译器。

    (2)关于C和C++的一些区别,笔者打算另外写一些系列文章来总结分析。

    (3)另外,因为是练习写的程序,暂时还没有添加内存释放的方法。

    程序的头部定义以及通用的方法:

    #include <stdio.h>
    #include <malloc.h>
    
    #define TRUE 1
    #define FALSE -1
    
    typedef char ElemType; 
    typedef int BOOL;
    
    typedef struct _BinaryTreeNode
    {
    ElemType elem;
    struct _BinaryTreeNode* left;
    struct _BinaryTreeNode* right;
    }BinareTreeNode, *BiTree;
    
    void PrintNode(ElemType elem)
    {
    printf("%c ", elem);
    }
    
    void PreOrderTraverse(BinareTreeNode* pNode, void(* Visit)(ElemType elem))
    {
    if (NULL != pNode)
    {
    Visit(pNode->elem);
    PreOrderTraverse(pNode->left, PrintNode);
    PreOrderTraverse(pNode->right, PrintNode);
    }
    }

    笔者最初写的程序如下:

    void PreOrderCreateBinaryTree(BinareTreeNode* pNode)
    {
    ElemType elem;
    scanf("%c", &elem);
    if ('#' == elem)
    {
    pNode = NULL;
    }
    else
    {
    pNode = (BinareTreeNode*)malloc(sizeof(BinareTreeNode));
    pNode->elem = elem;
    PreOrderCreateBinaryTree(pNode->left);
    PreOrderCreateBinaryTree(pNode->right);
    }
    }
    
    int main()
    {
    BinareTreeNode* pHeadNode = NULL;
    PreOrderCreateBinaryTree(pHeadNode);
    PreOrderTraverse(pHeadNode, PrintNode);
    return 0;
    }

    你能看出来问题在哪里吗?先思考,不要急着往后看。

    具体问题现象:

    原本以为这样写是没有问题的,实际上,main函数中的pHeadNode的值一直都是NULL,导致PreOrderTraverse时没有任何值输出。
    输入:ABD##E##C## 遍历后输出:无,原因是传入的参数值pHeadNode为NULL。

    具体问题分析:

    为什么会这样呢?

    本文讨论的第一个问题浮出水面:常见的定式思维:指针作为参数传递,是否就不需要再赋值?

    分析:

    虽然是指针传递,main函数中的pHeadNode,是一个指针变量,指针变量的值为空,不存在指向的值。

    PreOrderCreateBinaryTree函数中的形参pNode却是另一个指针变量,刚开始的时候其值由main函数中传入,也是NULL,后来pNode经过malloc被重新赋值。

    问题是,main函数中的pHeadNode有没有被同步改变呢?不是说指针传递,值会一起改变吗?

    其实,这里犯了一个低级错误,就是说使用指针作为函数的参数,指针没有被重新赋值的情况下,指针指向的值一定是会被同步更改的,但是如果指针作为一个变量,本身的值发生更改,那么参数源是不会发生改变的。

    举个例子:

    1)指针指向的值会被同步更改
    void FuncChangeObj(int* pInt)
    {
    (*pInt)++;
    }
    (2)指针本身的变量值被更改,此后,此函数内的指针与源指针将指向不同的值,也不会发生同步更改
    void FuncNoChangeObj(int* pInt)
    {
    pInt = (int*)malloc(sizeof(int));
    *pInt = 10;
    }

    上例二叉树程序先序遍历不成功,就是因为先序创建二叉树的时候,并没有将二叉树的根节点返回,导致pHeadNode一直为NULL。

    那么,是不是说,上述程序其实创建二叉树成功,只是没有返回树的根节点呢?不然,因为也是同样的原因,导致这个二叉树并没有创建成功,各个树节点之间并无关联,全是孤立的节点。

    具体问题解决之道:

    如何修改呢?
    给出答案如下:
    方法一:函数返回指针,对源指针进行赋值

    BinareTreeNode* PreOrderCreateBinaryTree(BinareTreeNode* pNode)
    {
    ElemType elem;
    scanf("%c", &elem);
    if ('#' == elem)
    {
    pNode = NULL;
    }
    else
    {
    pNode = (BinareTreeNode*)malloc(sizeof(BinareTreeNode));
    pNode->elem = elem;
    pNode->left = PreOrderCreateBinaryTree(pNode->left);
    pNode->right = PreOrderCreateBinaryTree(pNode->right);
    }
    return pNode;
    }
    
    int main()
    {
    BinareTreeNode* pHeadNode = NULL;
    pHeadNode = PreOrderCreateBinaryTree(pHeadNode);
    PreOrderTraverse(pHeadNode, PrintNode);
    return 0;
    }

    方法二:使用指针的引用进行传值,即对指针变量本身进行引用,而不是指针变量值传递
    注:本方法即是对第二个问题的阐释,但是要要注意,此方法在C语言编译器中是无法编译成功过的,因为C语言中并不存在引用的概念。

    void PreOrderCreateBiTree2(BiTree& T)
    {
    ElemType elem;
    scanf("%c", &elem);
    if ('#' == elem)
    {
    T = NULL;
    }
    else
    {
    T = (BinareTreeNode *)malloc(sizeof(BinareTreeNode));
    T->elem = elem;
    PreOrderCreateBiTree2(T->left);
    PreOrderCreateBiTree2(T->right);
    }
    }
    
    int main()
    {
    BiTree T = NULL;
    PreOrderCreateBiTree2(T);
    PreOrderTraverse(T, PrintNode);
    return 0;
    }
  • 相关阅读:
    BZOJ 1191 HNOI2006 超级英雄hero
    BZOJ 2442 Usaco2011 Open 修建草坪
    BZOJ 1812 IOI 2005 riv
    OJ 1159 holiday
    BZOJ 1491 NOI 2007 社交网络
    NOIP2014 D1 T3
    BZOJ 2423 HAOI 2010 最长公共子序列
    LCA模板
    NOIP 2015 D1T2信息传递
    数据结构
  • 原文地址:https://www.cnblogs.com/yylwuwei/p/3144250.html
Copyright © 2011-2022 走看看