二叉樹算法引起的思考:指針參數傳遞、引用的陷阱

最近想熟悉一下基本的數據結構和算法,因而寫了一個二叉樹程序,功能很簡單,只有二叉樹的創建和遍歷。在這個過程當中,卻發現了一些平時沒有注意到的細節問題,寫出來做爲總結和你們分享。算法

 

待討論和遇到的細節問題以下:數據結構

(1)常見的定式思惟:指針做爲參數傳遞,是否就不須要再賦值,由於指針指向的值會同步更改,可是,你有考慮過,若是指針變量自己的值被更改了呢?怎麼辦?數據結構和算法

(2)比較不經常使用的用法,針對指針變量的引用,你用過嗎?函數

 

前提:spa

(1)筆者考慮到本身一直混淆使用C和C++,因而,打算這個簡單的算法程序採用純C來寫,所以使用的是C編譯器。指針

(2)關於C和C++的一些區別,筆者打算另外寫一些系列文章來總結分析。code

(3)另外,由於是練習寫的程序,暫時尚未添加內存釋放的方法。blog

程序的頭部定義以及通用的方法:內存

#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;
}
相關文章
相關標籤/搜索