前言
在阐述c++中函数传参之前,先说明一下为何写这篇博客。事实上,c++的传参方式的讨论已经很多,但是在解剑指 Offer 27. 二叉树的镜像(Leetcode 226翻转二叉树)时发现三种传参方式自己并没有完全弄清,在以树指针为对象时,就出现错误。
交换两个变量
网上有三种常见的交换变量的方式,第一种创建临时变量存储其中一个待交换的值,这种方法最为常见。
1 | auto tmp = a; |
第二种不创建临时变量,利用四则运算,代码如下,但是可能会产生数值溢出,不建议使用。
1 | a = a + b; |
第三种同样不创建临时变量,利用位运算,代码如下。这是因为 x ^ y ^ y = x,任意变量与其他变量异或两次结果等于自身。然而,该方法只适用于交换整型或者字符型数据交换,这是因为位运算的运算分量只能是这两者。
1 | a = a ^ b; |
所以,最简单的才是最有用的,第一种方法虽然朴素,但是有效。
问题描述
在翻转二叉树时,其实问题本身解答非常容易,Leetcode也是定级为简单。就是个普通的前序遍历或者后序遍历,前者自顶向下,后者自底向上。但是在交换两个TreeNode*变量时,自己写的交换函数竟不能通过。
1 | /** |
自己的交换代码如下:
1 | void swapNode(TreeNode* left, TreeNode* right){ |
在invertTree函数中使用该函数,交换两个二叉树指针不能成功交换,而用c++自带的swap函数确又可以。
分析问题
函数传参有三种:值传递,引用传参和指针传参。三者的主要区别为:值传参只传递实参的拷贝值,函数中的操作不影响实参,引用传参和指针传参均会影响实参,函数执行完毕相当于实参完成了函数内部的操作。引用传参和指针传参的不用在于,引用传参必须要先初始化实参,而指针传参不必。以交换变量为例,三中传参分别为:
1 | //值传参 |
第一种不能完成实参的交换,后两种可以。这样分析下来,自己写的swapNode(TreeNode* left, TreeNode* right)函数看似也是指针传参呀,但是为何不行呢。这里就出现了偏差,TreeNode* 虽然是指针,但是被交换的两个变量也都是指针,相当于进行了值传参。如果想要真正让交换函数起到作用,应该进行引用传参或者指针传参。
解决方法
引用传参交换两个TreeNode*指针变量表示为:
1 | void swapNode(TreeNode*& left, TreeNode*& right){ |
如果有TreeNode* a和TreeNode* b,直接调用swapNode(a, b)即可。
指针传参交换两个TreeNode*指针变量表示为:
1 | void swapNode(TreeNode** left, TreeNode** right){ |
如果有TreeNode* a和TreeNode* b,直接调用swapNode(&a, &b)即可。
者两种传参方式传递树指针时,可能同时出现了*或者同时出现了&略显怪异,但是事实确实如此。