C++树的插入和遍历(关于指针的指针,指针的引用的思考)
题目
写一个树的插入和遍历的算法,插入时按照单词的字典顺序排序(左边放比它“小”的单词,右边放比它“大”的单词),对重复插入的单词进行计数。
程序源码
#include <iostream>
#include <cstring>
#include <sstream> //vs2010中不加入这句话不能使用cin和cout,用gcc编译不用加
using namespace std;
struct Tnode
{
string word;
int count;
Tnode *left;
Tnode *right;
};
//根据word分配一个新的Tnode存储,并返回其引用
Tnode* GenNewTnode(const string&);
//将word按顺序放入树中,如果已存在则对应count加一
//返回插入树中节点的指针
Tnode* InsertWordToTree(Tnode*&,const string&);
//根据左,中,右的顺序遍历数的所有节点
void ShowTree(Tnode*,bool,int = 2);
int main()
{
cout<<"输入各个单词,输入“结束”结束"<<endl;
Tnode* tree = 0;
while(1)
{
string word;
cin>>word;
if(word == "结束")
break;
InsertWordToTree(tree,word);
}
ShowTree(tree,true/*省略第三个参数*/);
}
//根据word分配一个新的Tnode存储,并返回其引用
Tnode* GenNewTnode(const string& word)
{
Tnode* tN = new Tnode;
tN->word = word;
tN->count = 1;
tN->left=(tN->right=0);
return tN;
}
//将word按顺序放入树中,如果已存在则对应count加一
//返回插入树中节点的指针
Tnode* InsertWordToTree(Tnode*& root,const string& word)
{
Tnode* tN = root;
if(root == 0) //空树
{
root = GenNewTnode(word); //产生第一个根节点
return root;
}
while (1)
{
int oder = word.compare(tN->word);
if(oder == 0) //单词存在
{
tN->count++;
return tN;
}
Tnode*& nextTN = (oder<0)?(tN->left):(tN->right); //迭代搜索
if(nextTN == 0)
{
nextTN = GenNewTnode(word); //没找到,产生新的节点
return tN;
}
tN = nextTN;
}
}
//根据左,中,右的顺序遍历数的所有节点
//indent:是否需要输出空格
//spaces:节点之间插入多少空格
void ShowTree(Tnode* root,bool indent,int spaces)
{
if(root)
{
ShowTree(root->left,indent,spaces+2);
if(indent)
{
for(int i = 0;i < spaces;i++)
cout<<" ";
}
cout<<root->word<<"("<<root->count<<")"<<endl;
ShowTree(root->right,indent,spaces+2);
}
}
思路解析,问题总结和思考
整体思路是分别需要实现三个函数:分配新的节点存储空间、将单词放入节点、显示树。
分配新的节点空间:根据给定的单词将其放入存储中,返回存储的指针即可。
显示树:二叉树的遍历是计算机二级考试的常客,比较简单。这里采用左->中->右的递归搜索算法(中序遍历LDR)。
Ps:补充一点计算机二级知识,中->左->右为前序遍历(DLR),左->中->右为中序遍历(LDR),左->右->中为后序遍历(LRD)。已知前序和后序遍历不能唯一的确定这颗二叉树。但已知前中或后中遍历都可以唯一确定二叉树。
将单词放入节点:这个函数中使用到了指针的引用,以前没有使用过指针的引用,在犯了一些错误之后才对指针的引用才有所体会。犯了两个低级的错误:
- 定义了一个局部指针变量指向需要添加节点的位置,然后使用GenNewTnode()分配存储空间。由于局部变量在返回的时候会释放掉所以并不会产生新的节点,需要使用指针的引用指向需要添加节点的位置(上面代码中的nextTN)。
- 为了简化程序想将局部变量tN定义为指针引用进行迭代,但是忽略了引用类型一旦在初始化时给出了定义就不能再指向其他位置了,这一点和指针不一样。
其实将指针看成普通的变量之后,再去理解指针的引用就容易许多了。
注意不能给局部指针分配存储,因为返回时指针就会释放。但是可以给加去引运算的指针的指针分配空间(*tN=new
,tN为指针的指针)。所以上面的InsertWordToTree函数可以使用指针的指针进行改写。
Tnode* InsertWordToTree(Tnode*& root,const string& word)
{
Tnode** tN = &root;
if(root == 0) //空树
{
root = GenNewTnode(word); //产生第一个根节点
return root;
}
while (1)
{
int oder = word.compare((*tN)->word);
if(oder == 0) //单词存在
{
(*tN)->count++;
return *tN;
}
tN = (oder<0)?(&((*tN)->left)):(&((*tN)->right)); //迭代搜索
if(*tN == 0)
{
*tN = GenNewTnode(word); //没找到,产生新的节点
return *tN;
}
}
}
注意tN = (oder<0)?(&((*tN)->left)):(&((*tN)->right)); //迭代搜索
和*tN = GenNewTnode(word); //没找到,产生新的节点
这两句话的表达,要取左右两边节点地址的地址给tN,这样才能保证分配空间的首地址放到了左右指针中。
当然我们也可以即不使用指针的引用也不使用指针的指针,使用普通的指针也能完成上面任务但是代码要稍微复杂一些,如下:
Tnode* InsertWordToTree(Tnode*& root,const string& word)
{
Tnode* tN = root;
if(root == 0) //空树
{
root = GenNewTnode(word); //产生第一个根节点
return root;
}
while (1)
{
int oder = word.compare(tN->word);
if(oder == 0) //单词存在
{
tN->count++;
return tN;
}
Tnode* temp = (oder<0)?(tN->left):(tN->right); //迭代搜索
if(temp == 0)
{
if(oder<0)
{
tN->left = GenNewTnode(word); //没找到,产生新的节点
return tN->left;
}
else
{
tN->right = GenNewTnode(word);
return tN->right;
}
}
tN = temp;
}
}
注意上面的temp变量虽然和tN->left都指向同一个地址,但是由于没使用引用,这两个指针的地址并不相同。不能给temp分配空间,而是给tN->left分配空间,即tN->left指向分配空间的首地址。
ps:虽然叙述起来比较啰嗦,但其实搞明白后指针的指针,指针的引用没有那么难,可能是我表达能力有限吧┑( ̄Д  ̄)┍。
C++树的插入和遍历(关于指针的指针,指针的引用的思考)的更多相关文章
- 萌新笔记——C++里创建 Trie字典树(中文词典)(一)(插入、遍历)
萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...
- C++里创建 Trie字典树(中文词典)(一)(插入、遍历)
萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...
- 数据结构系列之2-3-4树的插入、查找、删除和遍历完整版源代码实现与分析(dart语言实现)
本文属于原创,转载请注明来源. 在上一篇博文中,详细介绍了2-3树的操作(具体地址:https://www.cnblogs.com/outerspace/p/10861488.html),那么对于更多 ...
- 数据结构系列之2-3树的插入、查找、删除和遍历完整版代码实现(dart语言实现)
弄懂了二叉树以后,再来看2-3树.网上.书上看了一堆文章和讲解,大部分是概念,很少有代码实现,尤其是删除操作的代码实现.当然,因为2-3树的特性,插入和删除都是比较复杂的,因此经过思考,独创了删除时分 ...
- B+树的插入、删除(附源代码)
B+ Tree Index B+树的插入 B+树的删除 完整测试代码 Basic B+树和B树类似(有关B树:http://www.cnblogs.com/YuNanlong/p/6354029.ht ...
- B树和B+树的插入、删除图文详解
简介:本文主要介绍了B树和B+树的插入.删除操作.写这篇博客的目的是发现没有相关博客以举例的方式详细介绍B+树的相关操作,由于自身对某些细节也感到很迷惑,通过查阅相关资料,对B+树的操作有所顿悟,写下 ...
- B树和B+树的插入、删除图文详解(good)
B树和B+树的插入.删除图文详解 1. B树 1. B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数 ...
- java:数据结构(四)二叉查找树以及树的三种遍历
@TOC 二叉树模型 二叉树是树的一种应用,一个节点可以有两个孩子:左孩子,右孩子,并且除了根节点以外每个节点都有一个父节点.当然这种简单的二叉树不能解决让树保持平衡状态,例如你一直往树的左边添加元素 ...
- MySQL B+树 的插入与删除
一.MySQL Index 的插入 有如下B+树,其高度为2,每页可存放4条记录,扇出为5.所有记录都在叶子节点上, 并且是顺序存放,如果用户从最左边的叶子节点开始顺序遍历,可以得到所有简直的顺序 排 ...
随机推荐
- C++ 网络编程 总结
第一次用C++写程序,对C++ 只是菜鸟级别的,倒是对C#很熟悉.两者有很大的相似性.但也有不同. 首先写了一个网络通讯用的小的MFC程序.发现 (1)MFC写界面真的好麻烦呀. 用C#写的tab ...
- swift和OC中frame的小差异
//1.0 OC中 CGRect .CGPoint.CGSize 的结构如下: struct CGRect { CGPoint origin; CGSize size; }; struct CGPoi ...
- Kubernetes+Prometheus+Grafana部署笔记
一.基础概念 1.1 基础概念 Kubernetes(通常写成“k8s”)Kubernetes是Google开源的容器集群管理系统.其设计目标是在主机集群之间提供一个能够自动化部署.可拓展.应用容器可 ...
- FTP原理和虚拟用户映射登录-2019.2.8
FTP主动模式和被动模式 FTP(File Transfer Protocol)是文件传输协议的简称.正如其名所示:FTP的主要作用,就是让用户连接上一个远程计算机(这些计算机上运行着FTP服务器程序 ...
- Applese走迷宫-bfs
链接:https://ac.nowcoder.com/acm/contest/330/C来源:牛客网 题目描述 精通程序设计的 Applese 双写了一个游戏. 在这个游戏中,它被困在了一个 n×mn ...
- 我的第一篇博客之js的XXXX年XX月XX日 星期[日一-六] [上下]午 XX时:XX分
<!DOCTYPE html> <html> <head> <title>test</title> ...
- 第三章,DNA序列的进化演变
31.前言 3.1.两个序列间的核苷酸差异 来自同一祖先序列的两条后裔序列,之间的核苷酸的差异随着时间的增加而变大.简单的计量方法,p距离 3.2.核苷酸代替数的估计 3.3.Jukes和Cantor ...
- metasploit framework(一):基本使用
它位于/usr/share/metasploit-framework 进入到modules目录,有六大模块 exploits:系统漏洞利用的流程,对系统漏洞注入一些特定的代码,使其覆盖程序执行寄存器, ...
- Integer 原码解读
有一天,突然发现,阅读原码可以发现很多有趣的东西.在Java中,我们知道很多东西都是封装好的,拿来即用,当你有一天去研究它拿来的东西是如何具体操作的,将会是非常有趣的事情. 在上一篇研究HashMap ...
- Jenkins 传递自定义的参数
1.同一个job之间,不同的shell之间传递参数 注意:如果是job参数化构建自定义的参数,可以在job的不同shellj间引用,但是不能改变他的值供后面的shell使用 job在执行时会针对所 ...