关于对象和对象指针,涉及指向链表首结点和二叉树根结点的头指针——传指针还是传指针的指针?

C++对象和对象指针很少被讲到,但数据结构中(如链表、二叉树)函数调用时,经常涉及到对象指针,甚至是对象指针的指针。

以链表的建立为例,当涉及到要修改链表中的结点时,那么函数参数就必须是对象指针或对象指针的指针。

例1、ListNode *Head = NULL;
    CreateList(Head);//创建链表
    PrintList(Head);//打印链表

       运行一下,结果是什么都没打印!为什么呢?CreateList的参数明明是Head,指针型啊。查阅相关资料,CreateList的参数应该是指针的指针CreateList(ListNode **node),或者是指针应用,如CreateList(ListNode *&node), 那么调用时改为CreateList(&Head)即可,运行结果输出“0到9”。不禁感叹,对象就是牛啊,修改时还需要指针的指针!!但是,这样理解对吗?凭什么对象就需要指针的指针才能修改?!且看例2.

例2、(接例1)       

change(Head);
PrintList(Head);

change函数如下:(说明:该函数仅为测试,因为知道里面有5,所以没有做边界测试以及非功能测试)

void change(ListNode *Head)
{
while(Head->value != 5)
{ Head = Head->next; } 
Head->value = 55;
}

运行结果:0 1 2 3 4 55 6 7 8 9.

居然真的改了,不需要指针的指针啊!看来对象和变量类似,要想在调用函数中修改,只需将参数设为对象指针即可。那为什么例1就不可以呢?且看例3两个函数申明:

例3、

void CreateListNodeReturnNULL(ListNode** pHead) ①

ListNode* CreateListNodeReturnPointer(ListNode* pHead) ②

{
printf("Invoke CreateListNodeReturnPointer()\n");
ListNode* pPre = (ListNode* )malloc(sizeof(ListNode));
pHead = pPre;//记录了头结点的指针,将作为返回值复制给链表头结点,实现对链表头结点进行修改
if(pPre == NULL)
{
printf("memory cannot be anocated\n");
exit(0);
}
pPre->value = 0;
pPre->next = NULL;

for(int i = 1; i<10; i++)
{
ListNode* pNode = (ListNode *)malloc(sizeof(ListNode));
pNode->value = i;
pNode->next=NULL;
pPre->next = pNode;
pPre = pNode; 

pPre->next = NULL;
return pHead;//头结点的指针,将作为返回值复制给链表头结点,实现对链表头结点进行修改
}

 

调用函数①不用说,已经成功运行。在函数②中传过来的是Head指针值的一个副本函数调用都是传副本,区别在于有的是值的副本、而有的是地址即指针的副本所以在函数②中,对于pHead值的修改,不会影响主函数中Head的值(见例1,此时是修改指针变量,即那个指针副本),但是会影响其所指向的对象的内容(见例2)。

本文总结如下:

1、函数调用都是传副本,区别在于有的是值的副本、而有的是地址即指针的副本;

2、对象和变量类似,对象指针和变量指针类似;

3、①对于头结点,如果涉及其所在内存地址修改(删除头结点)则需要修改指向其的头指针,形参需要指向指针的指针(例3,函数①)或者在主函数中通   过函数返回值(返回指针)对头指针重新复制(例3,函数②),以修改头指针;

     ②若只修该内容,形参只要是对象指针即可,是可以直接修改的,不需要涉及指向其的指针);

     ③对于链表中非头结点,形参只要是对象指针即可(例2),可以直接修改的(包括删除),通过头指针可以或者改结点的地址。

In a word, 想要修改变量就要获取该变量的地址。


### 回答1: 如果使用二叉链表作为储存结构,那么每个结点应该包含三个域:左孩子指针、右孩子指针和双亲指针。因此,如果要获取某个结点x的双亲,只需要访问x的双亲指针即可。 假设我们已经定义了二叉树结点的结构体如下: ``` struct BinaryTreeNode { int value; // 结点的值 BinaryTreeNode* left; // 左孩子指针 BinaryTreeNode* right; // 右孩子指针 BinaryTreeNode* parent; // 双亲指针 }; ``` 那么获取结点x的双亲代码如下: ``` BinaryTreeNode* getParent(BinaryTreeNode* x) { if (x == nullptr || x->parent == nullptr) { // 如果x为空或x没有双亲,返回空指针 return nullptr; } else { // 返回x的双亲指针 return x->parent; } } ``` 调用该函数,结点x,即可获取x的双亲。例如,假设我们已经构建好了一棵二叉树,其中结点A是节点,结点B和结点C是它的左右孩子,结点D是结点B的左孩子,结点E是结点C的右孩子。如果我们要获取结点D的双亲,可以这样调用函数: ``` BinaryTreeNode* root = ...; // 节点A BinaryTreeNode* d = ...; // 结点D BinaryTreeNode* parent = getParent(d); if (parent != nullptr) { cout << "结点D的双亲是:" << parent->value << endl; } else { cout << "结点D没有双亲或者输入的结点为空" << endl; } ``` ### 回答2: 二叉链表是一种常用的二叉树存储结构。在二叉链表中,每个结点除了包含数据元素外,还包含了指向其左孩子和右孩子的指针。而结点的双亲可以通过在结点结构中添加一个指向结点指针来表示。 具体实现时,可以在结点结构中添加一个指针变量parent,用于指向结点的父结点。当需要访问结点x的双亲时,可以通过访问x的parent指针来获取。 例如,下面是一个二叉链表结点的定义: ```c typedef struct Node { int data; // 结点中存储的数据元素 struct Node* left; // 指向左孩子的指针 struct Node* right; // 指向右孩子的指针 struct Node* parent; // 指向结点指针 } Node; ``` 假设有一个指向二叉树结点指针root,要访问结点x的双亲,可以使用以下代码: ```c Node* getParent(Node* root, Node* x) { if (root == NULL || root == x) { return NULL; // 结点或目标结点为空时,返回NULL } if (root->left == x || root->right == x) { return root; // 如果目标结点是当前结点的左孩子或右孩子,则当前结点就是目标结点的双亲 } Node* parent = getParent(root->left, x); if (parent != NULL) { return parent; // 如果在左子树中找到双亲,直接返回 } return getParent(root->right, x); // 否则在右子树中找双亲 } ``` 以上代码使用递归方式实现了获取结点x的双亲的功能。当结点为空或结点就是目标结点x时,返回NULL。在每个结点中,分别判断左孩子和右孩子是否为目标结点x,如果是,则返回当前结点;如果不是,则递归地在左子树和右子树中继续寻找。这样,就可以通过二叉链表来实现获取二叉树中任意结点双亲的功能。 ### 回答3: 在二叉链表中,每个结点都有一个指针指向其双亲结点。假设我们有一个二叉树的二叉链表储存结构如下: ```c typedef struct BiTNode { ElemType data; // 结点数据 struct BiTNode *lchild, *rchild; // 左子结点和右子结点指针 struct BiTNode *parent; // 双亲结点指针 } BiTNode, *BiTree; ``` 为了找到结点x的双亲,我们可以进行如下操作: 1. 判断二叉树是否为空,如果为空则返回空。 2. 从结点开始,从上至下按层遍历二叉树。 3. 在遍历的过程中,判断当前结点的左子结点和右子结点是否为x,如果是则返回该结点作为x的双亲。 4. 如果当前结点的左子结点和右子结点不是x,则将当前结点入队,并依次遍历其左子结点和右子结点。 5. 重复步骤3和4,直到找到结点x的双亲或者遍历完整个二叉树。 要实现上述操作,我们可以使用队列作为辅助数据结构,即在操作过程中将结点依次入队,然后依次出队。 以下是一种可能的C语言实现: ```c BiTNode* FindParent(BiTree root, BiTNode* x) { if (root == NULL || x == NULL) { return NULL; } queue<BiTNode*> q; // 辅助队列 q.push(root); // 结点入队 while (!q.empty()) { BiTNode *curNode = q.front(); // 取出队列头结点 q.pop(); // 出队 // 判断左子结点或右子结点是否为x,是则返回当前结点 if (curNode->lchild == x || curNode->rchild == x) { return curNode; } // 将左子结点和右子结点入队 if (curNode->lchild) { q.push(curNode->lchild); } if (curNode->rchild) { q.push(curNode->rchild); } } return NULL; // 遍历完整个二叉树都没找到x的双亲,则返回NULL } ``` 通过以上实现,我们可以找到任意一个二叉树结点x的双亲结点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值