[速记]关于指针,引用和递归和解递归——C++
在写基于二叉排序树的查找时,分为三个过程
1.二叉排序树的插入
2.二叉排序树的建立
3.基于二叉排序树的查找
其中第三部可以递归方式实现,也可以用while循环解递归,于是我想也解解第一步的递归,看看行不行,结果给了我当头一棒,解递归失败!
最后我分析了一下原因:
首先看一下,原来递归的实现方式
typedef struct _TreeNode
{
struct _TreeNode *leftNode;
struct _TreeNode *rightNode;
TypeData data;
}TreeNode,*TreeRoot;
//返回插入位置的结点
TreeNode* Insert_Tree(TreeRoot &root,TypeData key)
{
if (!root)
{
TreeNode *node=new TreeNode;
node->data=key;
node->leftNode=nullptr;
node->rightNode=nullptr;
root=node;
return root;
}
else if (root->data==key)
{
return root;
}
else if (root->data<key)
{
return (Insert_Tree(root->rightNode,key));
}
else if (root->data>key)
{
return(Insert_Tree(root->leftNode,key));
} }
然后,我自己实现了一下解递归的实现
//这个方法是错误的
TreeNode* Insert_Tree2(TreeRoot &root,TypeData key)
{
TreeNode *p=root;
while (p)
{
if (p->data==key)
{
return p;
break;
}
else if (p->data>key)
{
p=p->leftNode;
}
else
{
p=p->rightNode;
}
}
if (!p)
{
p=new TreeNode;
p->data=key;
p->leftNode=nullptr;
p->rightNode=nullptr;
return p;
} }
看似,没有什么问题,实际上却犯了一个灰常严重的错误
我们可以看到,两种方式都是以引用的形式传入了根节点,为什么用引用?
因为我们插入的时候肯定会造一个新结点,然后要把它接到原来的树体系当中,第一种递归的方式中,最后造结点的时候
TreeNode *node=new TreeNode;
node->data=key;
node->leftNode=nullptr;
node->rightNode=nullptr;
root=node;
return root; 可以看到,标黑的位置,这个时候root是以引用的方式传入的,这个事实上直接更改了原来树体系中的结点 而在,解递归的实现当中,虽然是以引用的方式传入的参数,但是我们用了一个指针局部变量来代替他,最后的时候
p=new TreeNode;
p->data=key;
p->leftNode=nullptr;
p->rightNode=nullptr;
return p;
他是这样执行的,这里的p怎样得到的呢?
p=上一个p的left,
这样一种方式的后果是什么呢?
它改变了两个指针共同指向的内容,却无法改变另一个指针本身,是不是觉得无法理解? 不要紧,p是根据上一个父节点的left来赋值的,假设上一个父节点叫做f
那么,也就是
p=f->left;
这个时候p和f->left都为nullptr,
一直到这个时候,都没有问题
然后我们一个劲儿地去更改p的内容,
这个时候,没错,我们通过p更改了两者共同指向的内容,可是!
f->left一直都是nullptr
你改的再多,依然与我无关啊! 不理解的,再看一个程序
Node *p=new Node;
p->left=nullptr;
p->right=nullptr;
Node *l=p->left;
Node *q=p->left;
q=new Node;
q->data='q';
q->left=q;
q->right=q;
std::cout<<l->data;
std::cout<<q->data;
最后,总结一下:
1.要注意!修改指针和修改指针共同指向的内容是两回事!
2.凡是用到链式存储,当要改变结点之间的关系,或者增加结点的时候,这个时候最好用对指针的引用(二级指针也行,不过不推荐)。
3.当第二种情况用到递归的时候,解递归一定要谨慎,最好不要解递归!
4.事实上,只要不更改涉及到更改指针本身,一般都不会出现这种问题!此时实际上就是涉及到了更改了指针本身,却没有对原来的内容更新
[速记]关于指针,引用和递归和解递归——C++的更多相关文章
- Reverse Linked List 递归非递归实现
单链表反转--递归非递归实现 Java接口: ListNode reverseList(ListNode head) 非递归的实现 有2种,参考 头结点插入法 就地反转 递归的实现 1) Divide ...
- paip.指针 引用 c++ java的使用总结.
paip.指针 引用 c++ java的使用总结. ///////////////一般一个变量包括下面的信息 a.地址(指针) b.命名(引用,别名) c.变量内容.. 指针是一个变量的地址, ...
- Delphi函数翻译成VC要注意句柄指针传递(传递Handle的时候,必须加上一个指针引用,才能消除编译错误)
Delphi里做了魔法变化,每个变量名称本身就是指针,因为不怎么需要指针语法.我也不知道是不是因为这个原因引起的Delphi与VC对句柄的不同处理. 这是Delphi的强行关机函数,好用,调用方式:W ...
- Java基础知识强化之IO流笔记12:递归之递归解决问题的思想(图解)
1. 使用递归计算5!的结果,递归思想的本质如下: 2. 下面就要使用代码实现这个递归: 递归实现分析: (1)做递归要写一个方法 (2)出口条件 (3)规律 代码实现如下: package com. ...
- 【数据结构】——搜索二叉树的插入,查找和删除(递归&非递归)
一.搜索二叉树的插入,查找,删除 简单说说搜索二叉树概念: 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右 ...
- C语言学习笔记 (007) - 数组指针和通过指针引用数组元素的方法总结
1.数组指针:即指向数组的指针 那么, 如何声明一个数组指针呢? ]; /*括号是必须写的,不然就是指针数组:10是数组的大小*/ 拓展:有指针类型元素的数组称为指针数组. 2.通过指针引用数组元素的 ...
- C++中的二级指针和指针引用函数传参
在函数的使用过程中,我们都明白传值和传引用会使实参的值发生改变.那么能够通过传指针改变指针所指向的地址吗? 在解决这个问题之前,也许我们应该先了解指针非常容易混淆的三个属性: ①.指针变量地址(&am ...
- 引用、数组引用与指针引用、内联函数inline、四种类型转换运算符
一.引用 (1).引用是给一个变量起别名 定义引用的一般格式:类型 &引用名 = 变量名: 例如:int a=1; int &b=a;// b是a的别名,因此a和b是同一个单元 ...
- C++ 指针引用
//指针引用 #include<iostream> using namespace std; struct Teacher{ ]; int age; }; int InitA(Teache ...
随机推荐
- javascript实现键盘自动打字效果
最近在网上看到一个字符逐个出现的打字效果,觉得挺有趣的,想一想基本实现思路就是设置一个定时器逐然后逐个向容器中添加字符,于是就基于jQuery写了一个简单版的. <!DOCTYPE html&g ...
- css3 linear-gradient实现购物车地址选择信封效果
对于css3的渐变前端的童鞋一定不陌生,在一些电商网站会为了美化将地址选择做成信封样式(个人感觉很稀饭~),看了一下它的实现方式,大多数是以图片的形式,持着优化的心态尝试着用css3 linear-g ...
- Android 5.X新特性之RecyclerView基本解析及无限复用
说到RecyclerView,相信大家都不陌生,它是我们经典级ListView的升级版,升级后的RecyclerView展现了极大的灵活性.同时内部直接封装了ViewHolder,不用我们自己定义Vi ...
- 简历生成平台项目开发-STEP3第一次项目例会探讨
时间:2016.7.13周三7点半 地点:图书馆 讨论主题:项目需求和功能分析.第一次任务分配 内容:按照之前的讨论,我们认为简历生成功能,不仅要适应学生求职的需求,更多的是要在格式和内容上满足HR的 ...
- mysql 触发器
触发器(trigger)是SQL server 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发, 比如当 ...
- 不同数据库,查询前n条数据的SQL语句
不同的数据库,支持的SQL语法略有不同,以下是不同数据库查询前n条数据的SQl语句 SQL Server(MSSQL) SELECT TOP n * FROM table_name ORACLE SE ...
- python-open文件处理
python内置函数open()用于打开文件和创建文件对象 语法 open(name[,mode[,bufsize]]) name:文件名 mode:指定文件的打开模式 r:只读 w:写入 a:附加 ...
- MyBatis源码分析-IDEA新建MyBatis源码工程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- Don't repeat yourself
今天工作上要测试不同版本mysql在不同系统的速度,学习了怎么源码安装mysql后,就开始敲命令安装和编译执行测试程序. 这么走了两遍后,想起一句话——当一件事重复做三遍后,要考虑把它工具化,自动化. ...
- 学习python函数笔记之一
1.函数文档字符串,用于存放函数的说明,一般写在第一行 2.函数调用:函数名+参数列表(函数的参数分实际参数和形式参数,str就是形式参数,则'welcome 头 python'就是实际参数) 参数需 ...