【数据结构】树笔记
创始人
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)

相关内容

热门资讯

汉中汉台交警:快递物流送货忙 ... 为进一步提升外卖、快递行业从业人员交通安全意识、文明出行意识及安全防护水平,4月16日上午,汉中汉台...
浙江鼎力:2024年净利润同比... 转自:财联社【浙江鼎力:2024年净利润同比下降12.76% 拟每10股派10元】财联社4月17日电...
中央提级巡视第二天,昆明2人落... 中央巡视组提级巡视,进驻昆明消息公布第二天,昆明2人落马。据云南昆明市纪委、玉溪市监委4月17日消息...
2025年长江干线首个超大型新... 中新网南京4月17日电 (记者 朱晓颖)16日,在江苏海事部门海巡艇接力维护下,长江干线今年首个超大...
川恒股份一季度营收14.93亿... 4月17日,川恒股份发布2025年一季报。报告显示,公司一季度营业收入为14.93亿元,同比增长43...
2025甘肃文旅(西安)推介会...   中国甘肃网4月17日讯(西北角·中国甘肃网记者 程健)如意甘肃誉中外,诗画陕西耀古今。4月17日...
彤程新材2024年营收32.7... 4月17日,彤程新材发布2024年年报。报告显示,公司2024年营业收入为32.70亿元,同比增长1...
时隔3个月再次到访北京,黄仁勋... 财联社4月17日讯,4月17日,应中国贸促会邀请,英伟达公司首席执行官黄仁勋抵达北京。三个月前,黄仁...
华阳股份今日大宗交易折价成交3... 4月17日,华阳股份大宗交易成交30万股,成交额177万元,占当日总成交额的1.95%,成交价5.9...
杭州银行:5.04亿股限售股将...   4月17日金融一线消息,杭州银行今日发布公告称,本次非公开发行的限售股将于2025年4月23日上...
实探第五届消博会:智能+数字 ... 近年来,国家密集出台一系列的产业支持政策,将带动健康行业快速发展、推动全民健身与全民健康深度融合,引...
诺禾致源今日大宗交易折价成交1... 4月17日,诺禾致源大宗交易成交18.51万股,成交额251.37万元,占当日总成交额的3.54%,...
今年第一季度刑事案件同比下降6... 转自:北京日报客户端4月17日,最高人民法院通报了今年第一季度司法审判工作主要数据,其中,受理刑事案...
上海凯鑫:公司可能在2024年... 2025年4月17日,上海凯鑫(300899.SZ)公告称,预计2024年度归属于上市公司股东的扣除...
游客服务中心如何成为地标吸引? 转自:蚌埠新闻网游客服务中心,作为游客与目的地联系的“第一印象区”,在文旅融合、科技引领趋势下,游客...
科兴制药引进产品阿达木单抗获首... ‌‌科兴制药(688136)与‌浙江博锐生物制药有限公司(以下简称“博锐生物”)‌合作出海的‌阿达木...
海程邦达:拟以4000万元至8... 转自:财联社【海程邦达:拟以4000万元至8000万元回购股份】财联社4月17日电,海程邦达(603...
鸿远电子4月17日现1笔大宗交...   炒股就看金麒麟分析师研报,权威,专业,及时,全面,助您挖掘潜力主题机会!   4月17日,鸿远...
鸿远电子今日大宗交易平价成交2... 4月17日,鸿远电子大宗交易成交24.64万股,成交额1362.35万元,占当日总成交额的2.36%...
我国充分发挥中医药优势保障运动... 转自:新华网  新华社北京4月17日电(记者 田晓航)记者从国家中医药管理局17日举行的新闻发布会获...