【数据结构】树笔记
创始人
2024-05-29 02:54:37
0
  1. 基础知识

树,有父节点和子节点。

二叉树分为:满二叉树,完全二叉树,完美二叉树

满二叉树:在当前树中,父节点不存在缺失左儿子或右儿子的现象。

完全二叉树:基于满二叉树,当前树中从上往下,从左至右数结点,不存在跳过空结点的现象

完美二叉树:基于完全二叉树,根节点左右子树等高

  1. 二叉树

1. 创建二叉树

树的开始处是根节点,结点可能有两个子节点。

#include
using namespace std;
template
struct p{t data;p *left_p;p*right_p;p(t num){left_p=NULL;right_p=NULL;data=num;}
};

然后尝试设置一个初始化树的函数。

template
class tree{
private:p *father;
public:tree(t n){father=new p(n);}~tree(){}

树初始化好了,出现了new结点,所以将来还要记得把树销毁掉,即析构函数。

2. 添加新结点

首先思考需要怎样才能添加一个确定的(或者说符合用户心意的)结点。

新结点的值是必定的,还需要指定新结点的父节点,并指定是要插入父节点的左子树还是右子树。

不能让用户自己创建一个结点指定,用户的输入必须简单。这里我们假设这棵树中无重复值,所以用户需要输入的树中目标结点的值。

这里涉及到一个问题,如何找到这个目标结点。于是我们先写查找结点的函数。

3. 查找结点

遍历树:先看当前结点的左子树,看完且没有找到后再看右子树->典型的递归函数

template
class tree{
private:p *father;p* refind(t n,p*r){if(r==NULL){return NULL;}if(r->data==n){return r;}p*temp_p=refind(n,r->left_p);if(temp_p==NULL){temp_p=refind(n,r->right_p);}return temp_p;}
public:tree(t n){father=new p(n);}~tree(){}p* findn(t n){return refind(n,father);}
};

回过头来说插入结点。

找到之后,首先看,如果我们要插入的是左结点,那看找到的这个结点它左结点满没满,没满,插入,满了,插入失败,插入右结点同理

int insert(t n,int flag,t num){p*temp_p=refind(n,father);if(temp_p==NULL){return 0;}if(flag==0){if(temp_p->left_p==NULL){p*new_p=new p(num);temp_p->left_p=new_p;return 1;}return 0;}else{if(temp_p->right_p==NULL){p*new_p=new p(num);temp_p->right_p=new_p;return 1;}return 0;}}

4. 遍历二叉树

已经写完了查找,遍历是一样的。

template
class tree{
private:p *father;void repre(p*temp_p){if(temp_p==NULL ){return;}cout<data<<" ";repre(temp_p->left_p);repre(temp_p->right_p);}
public:tree(t n){father=new p(n);}~tree(){}void pre(){repre(father);cout<

以上方法是前序遍历。前序遍历就是:输出顺序为:父节点,左子树,右子树

后面还有中序遍历,后序遍历。

中序遍历输出顺序为:左子树,父节点,右子树

后序遍历就是:左子树,右子树,根节点。

很明显这两个和前序遍历写法一样,除了要移动一下输出的位置。

#include
using namespace std;
template
struct p{t data;p *left_p;p*right_p;p(t num){left_p=NULL;right_p=NULL;data=num;}
};
template
class tree{
private:p *father;void repre(p*temp_p,int n){for(int i=0;idata<left_p==NULL and temp_p->right_p==NULL){return;}repre(temp_p->left_p,n+2);repre(temp_p->right_p,n+2);}void remi(p*temp_p){//中序递归if(temp_p==NULL){return ;}remi(temp_p->left_p);cout<data<<" ";remi(temp_p->right_p);}void relast(p*temp_p){//后序递归if(temp_p==NULL){return;}relast(temp_p->left_p);relast(temp_p->right_p);cout<data<<" ";}p* refind(t n,p*r){if(r==NULL){return NULL;}if(r->data==n){return r;}p*temp_p=refind(n,r->left_p);if(temp_p==NULL){temp_p=refind(n,r->right_p);}return temp_p;}
public:tree(t n){father=new p(n);}~tree(){}void preprint(){//前序repre(father,0);}void midprint(){//中序remi(father);cout<* findn(t n){return refind(n,father);}int insert(t n,int flag,t num){p*temp_p=refind(n,father);if(temp_p==NULL){return 0;}if(flag==0){if(temp_p->left_p==NULL){p*new_p=new p(num);temp_p->left_p=new_p;return 1;}return 0;}else{if(temp_p->right_p==NULL){p*new_p=new p(num);temp_p->right_p=new_p;return 1;}return 0;}}
};
int main(){treet(11);t.insert(11,0,22);t.insert(11,1,33);t.insert(22,0,44);t.insert(33,0,55);t.preprint();t.midprint();t.lastprint();
}

虽然上述都是用递归实现的,但如果树过大,系统的栈空间已满,递归将失败。所以考虑用迭代实现。

递归其实也是用栈实现的,所以说,我们完全可以用栈来代替递归过程

前序:(翻译一下递归的实现方式就好了)

void repre(){stack*>s;s.push(father);while(!s.empty()){p *temp_p=s.top();s.pop();cout<data<<" ";if(temp_p->right_p!=NULL){s.push(temp_p->right_p);}if(temp_p->left_p!=NULL){s.push(temp_p->left_p);}}}

中序:一个数要访问两次才能输出,所以再拿一个栈存。不管右结点有没有,都把它加入。

翻译一下当时做递归中序时的方法:只有空的时候才返回,说迭代的语言就是,只有空的时候才弹栈。

(这样才能越级输出根节点)

void remid(){stack*>s;//存放第一次遍历的结点stack*>s1;//存放第二次遍历的结点s.push(father);while(!s.empty()){p*temp_p=s.top();s.pop();if(temp_p==NULL){if(!s1.empty()){cout<data<<" ";s1.pop();}}else if (temp_p->left_p==NULL and temp_p->right_p==NULL) {cout<data<<" ";cout<data<<" ";s1.pop();}else{s1.push(temp_p);s.push(temp_p->right_p);s.push(temp_p->left_p);}}

后序遍历的迭代方法和中序遍历是一样的,关键点是处理什么时候彻底弹栈(即弹出s1栈)。本来想设置两个栈的,后来意识到其实我们是没有办法区分左右子树的。也就是,弹主栈的操作遇到两次,s1就要弹出一个。可以自己画图试着理解。

void relast(){stack*>s;stack*>s1;int cont=0;s.push(father);while(!s.empty()){p*temp_p=s.top();s.pop();if(temp_p==NULL){cont++;if(cont==2){if(!s1.empty()){cout<data<<" ";s1.pop();cont=1;}}}else{s1.push(temp_p);s.push(temp_p->right_p);s.push(temp_p->left_p);}}cout<

下次来补层序遍历。。。受不了了,敲不动了。

  1. 二叉查找树(BST)

这棵树左节点小于根节点,右节点大于根节点。

所以看起来,找的时候只要向一边找就行了,每次舍弃掉一半的点,算法课算过,效率是O(lgn)。

但是如果树长得很丑,比如全部只有右子树(退化成链表),这样就不存在舍弃点,所以效率就退化回O(n)

相关内容

热门资讯

Python|位运算|数组|动... 目录 1、只出现一次的数字(位运算,数组) 示例 选项代...
张岱的人物生平 张岱的人物生平张岱(414年-484年),字景山,吴郡吴县(今江苏苏州)人。南朝齐大臣。祖父张敞,东...
西游西后传演员女人物 西游西后传演员女人物西游西后传演员女人物 孙悟空 六小龄童 唐僧 徐少华 ...
名人故事中贾岛作诗内容简介 名人故事中贾岛作诗内容简介有一次,贾岛骑驴闯了官道.他正琢磨着一句诗,名叫《题李凝幽居》全诗如下:闲...
和男朋友一起优秀的文案? 和男朋友一起优秀的文案?1.希望是惟一所有的人都共同享有的好处;一无所有的人,仍拥有希望。2.生活,...
戴玉手镯的好处 戴玉手镯好还是... 戴玉手镯的好处 戴玉手镯好还是碧玺好 女人戴玉?戴玉好还是碧玺好点佩戴手镯,以和田玉手镯为佳!相嫌滑...
依然什么意思? 依然什么意思?依然(汉语词语)依然,汉语词汇。拼音:yī    rán基本解释:副词,指照往常、依旧...
高尔基的散文诗 高尔基的散文诗《海燕》、《大学》、《母亲》、《童年》这些都是比较出名的一些代表作。
心在飞扬作者简介 心在飞扬作者简介心在飞扬作者简介如下。根据相关公开资料查询,心在飞扬是一位优秀的小说作者,他的小说作...
卡什坦卡的故事赏析? 卡什坦卡的故事赏析?讲了一只小狗的故事, 我也是近来才读到这篇小说. 作家对动物的拟人描写真是惟妙...
林绍涛为简艾拿绿豆糕是哪一集 林绍涛为简艾拿绿豆糕是哪一集第三十二集。 贾宽认为是阎帅间接导致刘映霞住了院,第二天上班,他按捺不...
小爱同学是女生吗小安同学什么意... 小爱同学是女生吗小安同学什么意思 小爱同学,小安同学说你是女生。小安是男的。
内分泌失调导致脸上长斑,怎么调... 内分泌失调导致脸上长斑,怎么调理内分泌失调导致脸上长斑,怎么调理先调理内分泌,去看中医吧,另外用好的...
《魔幻仙境》刺客,骑士人物属性... 《魔幻仙境》刺客,骑士人物属性加点魔幻仙境骑士2功1体质
很喜欢她,该怎么办? 很喜欢她,该怎么办?太冷静了!! 太理智了!爱情是需要冲劲的~不要考虑着考虑那~否则缘...
言情小说作家 言情小说作家我比较喜欢匪我思存的,很虐,很悲,还有梅子黄时雨,笙离,叶萱,还有安宁的《温暖的玄》 小...
两个以名人的名字命名的风景名胜... 两个以名人的名字命名的风景名胜?快太白楼,李白。尚志公园,赵尚志。
幼儿教育的代表人物及其著作 幼儿教育的代表人物及其著作卡尔威特的《卡尔威特的教育》,小卡尔威特,他儿子成了天才后写的《小卡尔威特...
海贼王中为什么说路飞打凯多靠霸... 海贼王中为什么说路飞打凯多靠霸气升级?凯多是靠霸气升级吗?因为之前刚到时确实打不过人家因为路飞的实力...
运气不好拜财神有用吗运气不好拜... 运气不好拜财神有用吗运气不好拜财神有没有用1、运气不好拜财神有用。2、拜财神上香前先点蜡烛,照亮人神...