CPP[算法与数据结构]Day015 有序二叉树的增删改查,先序遍历,中序遍历,后序遍历

目录

0x00 二叉树

0x01 有序二叉树:

0x02补充:二叉树的遍历:

0X03 升(降)序二叉树的增删改查


0x00 二叉树

二叉树:每个节点有且只有两个孩子(设计数据结构的时候,每个节点只设计两个指针:左节点和右节点)

二叉树中的叶子节点:两个孩子都为NULL的节点。

0x01 有序二叉树:

升序二叉树:左节点 < 根节点 < 右节点,更准确的说,升序二叉树,按照“左中右”中序遍历的结果一定是从小到大的。其实中序遍历本质上也是递归遍历。

降序二叉树:左节点 > 根节点 > 右节点 ,按照"左中右"中序遍历的结果一定是大到小

0x02补充:二叉树的遍历:

根据根的位置分为三种:

先序遍历:根左右

中序遍历:左根右

后序遍历:左右根

0X03 升(降)序二叉树的增删改查

增:

问题:如何将一个数据插入一个升序二叉树或者一个升序二叉树的子树呢?

可以采用分治的思想递归插入。即首先确定最简单的情况(基线条件):要插入的子树(树)没有节点,那么直接插入的数据作为子树(树)的根节点。然后确定递归条件,即想办法缩小问题的规模,如果插入的数据小于根节点,那么就将该数据插入根节点的左子树(缩小了问题的规模),如果插入的数据比根节点大,那么将该数据插入根节点的右子树。

遍历:

遍历仍然使用分治思想,最简单的情形,即基线条件:树没有节点,直接返回。递归条件(缩小问题规模):遍历左子树,打印根的值,遍历右子树

查:因为二叉树是有序的,这里二分查找这种高效率利器就能大显身手了!

删:注意我们删除了有序二叉树的某个节点后,仍然要保证有序二叉树有序!

  1. 删除根节点:
    1. 有右孩子:
      1. 让根节点的左孩子成为右孩子的最左孩子。
      2. 让根节点的右孩子成为根。
      3. 释放根节点内存
    2. 没有右孩子:
      1. 让左孩子成为根
      2. 释放根节点内存
  2. 删除的节点不是根节点
    1. 找到要删除的节点的父节点
    2. 如果要删除的节点有右孩子
      1. 要删除的节点的左孩子成为要删除的节点的右孩子的最左孩子
      2. 要删除的节点如果是其父的左孩子
        1. 要删除的节点的 右孩子 成为 要删除的节点的父节点的左孩子
      3. 要删删除的节点如果是其父的右孩子
        1. 要删除的节点的 右孩子 成为 要删除的节点的父节点的 右孩子
      4. 释放要删除的节点的内存
  3.  

3.要删除的节点如果没有右孩子:

  1. 如果要删除的节点是其父的左孩子:
    1. 要删除的节点的左孩子成为要删除的节点的父节点的左孩子
  2. 如果要删除的节点是其父的右孩子:
    1. 要删除的节点的左孩子成为要删除的节点的父节点的右孩子
  3. 释放要删除的节点的内存。

代码有点小bug,以后闲了重写

#include <iostream>
using namespace std;

#include "00-Tree.h"

int main(){
	Tree<int> tree;
	tree.insert(6);
	tree.insert(4);
	tree.insert(7);
	tree.insert(2);
	tree.insert(5);
	tree.insert(8);
	tree.insert(1);
	tree.insert(3);
	cout<<"before:"<<endl;
	tree.travel();
	tree.search(6);
	tree.delNode(6);
	cout<<"after:"<<endl;
	tree.travel();


	return 0;
}
#pragma once
/*有序二叉树*/

template <class T>
class Tree{
	//定义内部类:节点类型
	//原因:创建内部类更好操作一些,也可以不创建内部类
	struct Node{
		T pBuff;
		Node* pLeft;
		Node* pRight;	
	};
	Node* pRoot;//树类型只有一个元素:根节点指针。
public:
	//1.构造函数
	Tree(){pRoot = NULL;}
	//2.析构函数:
	//为什么要写析构函数,因为我们希望当Tree类对象死亡的,它动态内存分配开的相关内存也能被释放。
	//释放思路:从二叉树的叶子开始逐层往上释放动态内存分配开的内存空间。
	~Tree(){if(pRoot) _clear(); pRoot = NULL;}
	//3.创建节点
	Node* CreateNode(const T& data){ 
		Node* pNew = new Node;
		pNew->pBuff = data;
		pNew->pLeft = pNew->pRight = NULL;
		return pNew;
	}
	//4.增
	void insert(const T& data);
	//5.中序遍历
	void travel();
	//6.查:
	void search(const T& data);
	//7.删除
	bool delNode(const T& data);
private:
	void _insert(Node** ppRoot,const T& data);
	void _travel(Node* pRoot);
	Node* _search(Node* pRoot,const T& data);
	void _clear(){}


};

//1.增
template <class T>
void Tree<T>::insert(const T& data){
	_insert(&pRoot,data);
}

template <class T>
//向一颗树ppRoot升序插入数据data,注意想要通过函数修改一个指针的指向,必须传入该指针的二级指针
void Tree<T>::_insert(Node** ppRoot,const T& data){
	Node* pNew =CreateNode(data);
	//最简单的情形:基线条件
	if(*ppRoot == NULL){
		*ppRoot = pNew;
		return;
	}
	//递归条件:
	//如果比根小,那么就用同样的算法插入到左子树
	if((*ppRoot)->pBuff > data){//升序二叉树,左<根<右,成为左孩子
		_insert(&((*ppRoot)->pLeft),data);
	}else{
		_insert(&((*ppRoot)->pRight),data);
	}
}

//2.中序遍历
template <class T>
void Tree<T>::_travel(Node* pRoot){
	if(pRoot == NULL) return;
	else{
		_travel(pRoot->pLeft);
		cout<<pRoot->pBuff;
		_travel(pRoot->pRight);
	}
	return;
}
template <class T>
void Tree<T>::travel(){
	_travel(pRoot);
	cout<<endl;
}
//3.查
template <class T>
void Tree<T>::search(const T& data){
	Node* ret = _search(pRoot,data);
	if(ret){
		cout<<"not find!"<<endl;
		return;
	}
	cout<<"not find!";
	return;
}

template <class T>
typename Tree<T>::Node* Tree<T>::_search(Node* pRoot,const T& data){
	if(pRoot == NULL) return NULL;
	if(pRoot->pBuff == data )return pRoot;	
	else{
		if(pRoot->pBuff > data) return _search(pRoot->pLeft,data);
		else return _search(pRoot->pRight,data);
	}
}
//4.删除
template <class T>
bool Tree<T>::delNode(const T& data){
	Node* pTemp = NULL;
	Node* pDelete = pRoot;
	if(pRoot->pBuff == data){//要删除的节点是根节点
		if(pRoot->pRight){//有右孩子
			pTemp = pRoot->pRight;
			while(pTemp->pLeft){
				pTemp = pTemp->pLeft;
			}
			pTemp->pLeft = pRoot->pRight;

			pRoot = pRoot -> pLeft;

		}else{//没有右孩子
			pRoot = pRoot->pLeft;
		
		}
		delete pDelete;
		return true;
	}
	Node* pDelParent = NULL;
	bool isFind = false;
	while(pDelete){
		if(pDelete->pBuff == data){
			isFind  = true;
			break;
		}
		pDelParent = pDelete;
		if(pDelete->pBuff > data){
			pDelete = pDelete->pLeft;
		}else{
			pDelete = pDelete->pRight;
		}
	}
	if(isFind = false) return false;
	if(pDelete->pRight){
		Node* pTemp = pDelete->pRight;
		while(pTemp->pLeft){
			pTemp = pTemp->pLeft;
		}
		pTemp->pLeft = pDelete->pLeft;
		if(pDelete == pDelParent->pLeft){
			pDelParent->pLeft = pDelete->pRight;
		}else{
			pDelParent->pRight = pDelete->pRight;
		}
		delete pDelete;
	}else{
		if(pDelete == pDelParent->pLeft){
			pDelParent->pLeft = pDelete->pLeft;
		}else{
			pDelParent->pRight = pDelete->pLeft;
		}
		delete pDelete;
		
		
	}
	return true;

}
root@kali:~/桌面/C++/算法/2019-09# 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值