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;
    }
  • 相关阅读:
    Google Chart API 阮一峰的网络日志
    PHP随机函数【上】
    php实现socket推送技术
    javascript变量作用域
    如何使用jqplot描绘一个简单的线形图?
    培训小记
    Google自己的浏览器GoogleChrome
    这大半年的回顾
    一个高手的SQL求工作天数的函数
    关于TSQL中数据库重命名
  • 原文地址:https://www.cnblogs.com/yylwuwei/p/3144250.html
Copyright © 2011-2022 走看看