数据结构之二叉树 ----- Day21
一. 知识点整理
本文的图片均来自程杰编著的《大话数据结构》一书
1. 树的基础概念
1.1 树的定义
- 定义
树是n( n >= 0)个结点的有限集合
n = 0时称之为空树
- 结点的分类:结点的度、叶节点
- 结点之间的关系,具体可以参照图片
- 结点的层次和深度(也称高度)
1.2 二叉树的概念
- 定义
二叉树是n个结点的有限集合,该集合或者为空集,或者又一个根节点和两棵互不相交的、分别称之为根节点的左子树和右子树的二叉树组成。
- 二叉树的五种形态
1.3 特殊的二叉树
- 斜树,如图
- 满二叉树,如下图
- 完全二叉树:对一棵具有n个节点的二叉树按层序编号i(1 <= i <= n)的结点与同深度的满二叉树中编号为i的结点在二叉树中位置完全相同。(如下图)
- 完全二叉树的特点
(1)叶子结点只能出现再最下两层。
(2)最下层的叶子一定 集中在左部连续位置
(3)倒数二层,若有叶子结点,一定都在右部连续位置
(4)如果结点度为1,则该节点只有左孩子,既不存在只有右子树的情况
(5)同样节点数的二叉树,完全二叉树的深度最小。
2. 二叉树的操作
2.1 创建树和销毁树
- 树的结构体声明
typedef struct BitNode
{
int data;
int timeIn; // 后面进行非递归遍历需要记录访问的次数
struct BitNode *left, *right;
}Node, *pNode, **ppNode;
- 创建树和树的结点,以及将树根与结点联系起来
pNode CreateTree(int *array, int len)
{
pNode * Nodearray = (pNode *)malloc(sizeof(pNode) * len);
for (int i = 0; i < len; i++)
{
pNode node = CreateNode(array[i]);
Nodearray[i] = node;
}
for (int i = 0; i < len/2; i++)
{
pNode root = Nodearray[i];
pNode left = NULL;
pNode right = NULL;
if (2*i + 1 < len)
{
left = Nodearray[i*2 + 1];
}
if (2*i + 2 < len)
{
right = Nodearray[i*2 + 2];
}
LinkNode(root, left, right);
}
pNode root = Nodearray[0];
free(Nodearray);
Nodearray = NULL;
return root;
}
pNode CreateNode(int data)
{
pNode node = (pNode)malloc(sizeof(Node));
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
void LinkNode(pNode root, pNode left, pNode right)
{
root->left = left;
root->right = right;
}
- 删除树和树的结点
void deleteTree(ppNode root)
{
//back_order1(*root, deleteNode);
if ((*root) != NULL)
{
deleteTree(&((*root)->left));
deleteTree(&((*root)->right));
deleteNode(root);
}
return ;
}
void deleteNode(ppNode node) //此处需要将二级指针传入进去,为了防止地址被破坏
{
(*node)->left = NULL;
(*node)->right = NULL;
(*node)->data = 0;
free(*node);
(*node) = NULL; //核心句
}
2.2 二叉树的遍历
2.2.1 用递归的方法进行
- 层序遍历【层序遍历需要通过队列来实现,这里只写出核心代码】
void LevelTra(pNode root)
{
if (root == NULL)
{
return ;
}
int result;
while (DelQue(&queue, &result) == 1)
{
printf("%d ", result);
}
if (root->left != NULL)
{
pNode temp = root->left;
Enque(&queue, temp->data);
}
if (root->right != NULL)
{
pNode temp = root->right;
Enque(&queue, temp->data);
}
LevelTra(root->left);
LevelTra(root->right);
}
- 前序遍历
- 原理图:
void pre_order(pNode root)
{
if (root == NULL)
{
return ;
}
printf("%d ", root->data);
pre_order(root->left);
pre_order(root->right);
}
- 中序遍历
void mid_order(pNode root)
{
if (root == NULL)
{
return ;
}
mid_order(root->left);
printf("%d ", root->data);
mid_order(root->right);
}
- 后序遍历
void back_order(pNode root)
{
if (root == NULL)
{
return ;
}
back_order(root->left);
back_order(root->right);
printf("%d ", root->data);
}
2.2.2 非递归实现遍历【需要用到栈的知识,这里只列出核心代码】
- 前序遍历
void pre_order_commom(pNode root)
{
pNode head = root;
while (head != NULL || s.top != 0)
{
while (head != NULL)
{
printf("%d ", head->data);
pushStack(head);
head = head->left;
}
if (s.top != 0)
{
head = popStack();
head = head->right;
}
}
printf("\n");
}
- 中序遍历
void mid_order_commom(pNode root)
{
pNode head = root;
while (head != NULL || s.top != 0)
{
while (head != NULL)
{
pushStack(head);
head = head->left;
}
if (s.top != 0)
{
head = popStack();
printf("%d ", head->data);
head = head->right;
}
}
printf("\n");
}
- 后序遍历
void back_order_commom(pNode root)
{
pNode head = root;
while (head != NULL || s.top != 0)
{
while (head != NULL)
{
head->timeIn = 1;
pushStack(head);
head = head->left;
}
if (s.top != 0)
{
head = popStack();
if (head->timeIn == 1)
{
head->timeIn++;
pushStack(head);
head = head->right;
}
else if (head->timeIn == 2)
{
printf("%d ", head->data);
head = NULL;
}
}
}
printf("\n");
}
- 程序的运行结果
2.3 二叉树通过值进行查找结点
pNode findNode(pNode root, int data)
{
if (root == NULL)
{
return NULL;
}
if (root->data == data)
{
return root;
}
pNode head = findNode(root->left, data);
if (head != NULL)
{
return head;
}
return findNode(root->right, data);
}
2.4 进行结点的插入
void insertNode(pNode root, int data)
{
pNode parent;
pNode head = root;
pNode nodeInsert = (pNode)malloc(sizeof(Node));
nodeInsert->data = data;
nodeInsert->left = nodeInsert->right = NULL;
while (head != NULL)
{
parent = head;
if (data < head->data)
{
head = head->left;
}
else
{
head = head->right;
}
}
if (data < parent->data)
{
parent->left = nodeInsert;
}
else
{
parent->right = nodeInsert;
}
}
2.5 二叉树结点的替换和交换
void replaceNode(ppNode root, pNode old, pNode new)
{
if (*root == old)
{
new->left = old->left;
new->right = old->right;
old->right = old->left = NULL;
*root = new;
}
else
{
pNode parent = searchParent(*root, old);
if (parent == NULL)
{
return ;
}
if (parent->left == old)
{
parent->left = new;
}
else
{
parent->right = new;
}
new->left = old->left;
new->right = old->right;
old->right = old->left = NULL;
}
return ;
}
void exchangeNode(ppNode root, pNode node1, pNode node2)
{
pNode temp = CreateNode(1024);
replaceNode(root, node1, temp);
replaceNode(root, node2, node1);
replaceNode(root, temp, node2);
}
二. 疑难知识点整理
1. 为什么在对树进行清除的时候要使用二级指针ppNode, 而不是pNode?
因为在传入pNode时,在函数里面会自动形成一个形参从而来对应你要删除的结点,但是在函数调用完成后,传入函数结点的地址还是原来的并没有发生改变,只是将其内容进行清除。然而通过ppNode可以在最后的ppNode node = NULL 完成这一操作,避免了上面所说的错误。
2. 怎样通过C语言打印树形结构?
这个不会,后面会了补充
三. C++实现二叉树相关
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <stack>
#include <climits>
#include <queue>
#include <algorithm>
struct TreeNode
{
int _val;
TreeNode* leftChild;
TreeNode* rightChild;
TreeNode(int val):_val(val),leftChild(nullptr),rightChild(nullptr){}
};
//用来记录平衡树的深度和是否为平衡树
struct BalanceRev
{
bool _isBalance;
unsigned int _depth;
BalanceRev(bool isBalance, unsigned int depth):_isBalance(isBalance),_depth(depth){}
};
struct FullTreeRev
{
unsigned int _nodes;
unsigned int _depth;
FullTreeRev(unsigned int nodes, unsigned int depth):_nodes(nodes),_depth(depth){}
};
class BinaryTree
{
public:
static int preValue;
public:
bool insertTreeNode(TreeNode* root, TreeNode* node);
TreeNode* deleteTreeNode(TreeNode* root, int val);
void preOrder(TreeNode* root);
void inOrder(TreeNode* root);
void postOrder(TreeNode* root);
void widthOrder(TreeNode* root);
void preOrderRec(TreeNode* root);
void inOrderRec(TreeNode* root);
void postOrderRec(TreeNode* root);
TreeNode createTreeNode(int val);
int getBinaryTreeWidth(TreeNode* root);
//是否是搜索树
bool isBSTree(TreeNode* root);
//是否是完全二叉树
bool isPerfectBT(TreeNode* root);
//是否是满二叉树
bool isFullTree(TreeNode* root);
//是否是平衡二叉树
bool isBalanceTree(TreeNode* root);
//两个树节点最短路径相交的节点
TreeNode getLowestCrossNode(TreeNode* root, TreeNode* head1, TreeNode* head2);
TreeNode* getLowestCrossNode1(TreeNode* root, TreeNode* head1, TreeNode* head2);
private:
int findLeftMaxNum(TreeNode* root);
int getBinaryTreeDepth(TreeNode* root);
BalanceRev processBLT(TreeNode* root);
FullTreeRev preocessFullTree(TreeNode* root);
void setMapNodeParent(TreeNode* node, std::map<TreeNode*, TreeNode*>& fatherMap);
};
int BinaryTree::preValue = INT_MIN;
TreeNode BinaryTree::createTreeNode(int val)
{
TreeNode newNode(val);
return newNode;
}
bool BinaryTree::insertTreeNode(TreeNode* root, TreeNode* node)
{
if (node == nullptr)
{
return false;
}
//若根节点不存在,则将新节点做为根节点
TreeNode* tmp = nullptr;
TreeNode* parent = nullptr;
if (root == nullptr)
{
root = node;
}
else
{
tmp = root;
}
while (tmp != nullptr)
{
//现将tmp节点保存至parent节点
parent = tmp;
if (tmp->_val > node->_val) //若当前节点比要插入的节点值大,则选择左边的树
{
tmp =tmp->leftChild;
}
else
{
tmp =tmp->rightChild;
}
}
//此时parent保存的是要插入点的父节点
if (node->_val > parent->_val)
{
parent->rightChild = node;
}
else
{
parent->leftChild = node;
}
return true;
}
TreeNode* BinaryTree::deleteTreeNode(TreeNode* root, int val)
{
}
void BinaryTree::preOrder(TreeNode* root)
{
TreeNode* tmp = root;
if (tmp == nullptr){
return;
}
std::cout << tmp->_val << "->";
preOrder(tmp->leftChild);
preOrder(tmp->rightChild);
}
void BinaryTree::inOrder(TreeNode* root)
{
TreeNode* tmp = root;
if (tmp == nullptr){
return;
}
inOrder(tmp->leftChild);
std::cout << tmp->_val << "->";
inOrder(tmp->rightChild);
}
void BinaryTree::postOrder(TreeNode* root)
{
TreeNode* tmp = root;
if (tmp == nullptr){
return;
}
postOrder(tmp->leftChild);
postOrder(tmp->rightChild);
std::cout << tmp->_val << "->";
}
void BinaryTree::preOrderRec(TreeNode* root)
{
if (root == nullptr)
{
return;
}
std::stack<TreeNode*> preStack;
preStack.push(root);
while(!preStack.empty())
{
TreeNode* tmp = preStack.top();
std::cout << tmp->_val << "->";
preStack.pop();
if (tmp->rightChild != nullptr)
preStack.push(tmp->rightChild);
if (tmp->leftChild != nullptr)
preStack.push(tmp->leftChild);
}
}
void BinaryTree::inOrderRec(TreeNode* root)
{
//八整颗树的左边全部押到栈里面去
if (root == nullptr)
{
return;
}
std::stack<TreeNode*> postStackMain;
TreeNode* phead = root;
while (!postStackMain.empty() || phead != nullptr)
{
if (phead != nullptr)
{
postStackMain.push(phead);
phead = phead->leftChild;
}
else
{
phead = postStackMain.top();
std::cout << phead->_val << "->";
postStackMain.pop();
phead = phead->rightChild;
}
}
//将左边界将整个树划分,先左再头再右
}
void BinaryTree::postOrderRec(TreeNode* root)
{
if (root == nullptr)
{
return;
}
std::stack<TreeNode*> inStackMain, inStackSub;
inStackMain.push(root);
while (!inStackMain.empty())
{
TreeNode* tmp = inStackMain.top();
inStackMain.pop();
inStackSub.push(tmp);
if (tmp->leftChild != nullptr)
inStackMain.push(tmp->leftChild);
if (tmp->rightChild != nullptr)
inStackMain.push(tmp->rightChild);
}
while(!inStackSub.empty())
{
TreeNode* tmp = inStackSub.top();
inStackSub.pop();
std::cout << tmp->_val << "->";
}
}
int BinaryTree::getBinaryTreeWidth(TreeNode* root)
{
int tree_width = INT_MIN;
if (root == nullptr)
{
return tree_width;
}
//宽度遍历用队列
std::queue<TreeNode*> Queue;
std::map<TreeNode*, int> treeMap; //创建一个map用来储存节点以及当前节点所在层数
int curLevel = 1; // 表示当前的层数
int curLevelNodes = 0;
int max_num = INT_MIN;
//把头节点插入到队列里面
Queue.push(root);
treeMap.insert(std::make_pair(root, curLevel));
while (!Queue.empty())
{
TreeNode* tmp = Queue.front();
Queue.pop();
auto iter = treeMap.find(tmp);
int curNodeLevel = INT_MIN;
if (iter != treeMap.end())
curNodeLevel = iter->second;
std::cout << "curNodeLevel = " << curNodeLevel << std::endl;
//用当前节点和当前层进行比较
if (curNodeLevel == curLevel) //如果相等,则说明还在这一层
{
curLevelNodes++;
}
else
{
curLevel++;
tree_width = std::max(tree_width, curLevelNodes);
curLevelNodes = 1;
}
if (tmp->leftChild != nullptr)
{
treeMap.insert(std::make_pair(tmp->leftChild, curLevel+1));
Queue.push(tmp->leftChild);
}
if (tmp->rightChild != nullptr)
{
treeMap.insert(std::make_pair(tmp->rightChild, curLevel+1));
Queue.push(tmp->rightChild);
}
}
return tree_width;
}
//层序遍历
void BinaryTree::widthOrder(TreeNode* root)
{
if (root == nullptr)
{
return;
}
std::queue<TreeNode*> Queue;
Queue.push(root);
while(!Queue.empty())
{
TreeNode* tmp = Queue.front();
std::cout << tmp->_val << "->";
Queue.pop();
if (tmp->leftChild != nullptr)
Queue.push(tmp->leftChild);
if (tmp->rightChild != nullptr)
Queue.push(tmp->rightChild);
}
}
bool BinaryTree::isBSTree(TreeNode* root)
{
if (root == nullptr)
{
return true;
}
bool isLeftBST = isBSTree(root->leftChild);
if (isLeftBST == false)
{
return false;
}
if (root->_val >= preValue)
{
preValue = root->_val;
}
else
{
return false;
}
return isBSTree(root->rightChild);
}
//1.若一个节点有右节点无左节点,则不是
//2.若父节点有一个左孩子但是无右孩子,则下面遇到的每一个节点都必须是叶子节点,否则不是
bool BinaryTree::isPerfectBT(TreeNode* root)
{
if (root == nullptr)
{
return false;
}
std::queue<TreeNode*> Queue;
Queue.push(root);
bool isMeet = false;
while (!Queue.empty())
{
TreeNode* tmp = Queue.front();
if ((isMeet && (tmp->leftChild != nullptr || tmp->rightChild != nullptr))
|| (tmp->rightChild != nullptr && tmp->leftChild == nullptr))
{
return false;
}
if (tmp->leftChild != nullptr )
{
Queue.push(tmp->leftChild);
}
if (tmp->rightChild != nullptr)
{
Queue.push(tmp->rightChild);
}
if (tmp->leftChild == nullptr || tmp->rightChild == nullptr)
{
isMeet = true;
}
}
return true;
}
int BinaryTree::getBinaryTreeDepth(TreeNode* root)
{
if (root == nullptr)
{
return 0;
}
int leftDepth = getBinaryTreeDepth(root->leftChild)+1;
int rightDepth = getBinaryTreeWidth(root->rightChild)+1;
return std::max(leftDepth, rightDepth);
}
BalanceRev BinaryTree::processBLT(TreeNode* root)
{
BalanceRev rev(false, INT_MIN);
if (root == nullptr)
{
rev._isBalance = true;
rev._depth = 0;
return rev;
}
BalanceRev leftData = processBLT(root->leftChild);
BalanceRev rightData = processBLT(root->rightChild);
//开始判断平衡树的满足条件
rev._depth = std::max(leftData._depth, rightData._depth)+1;
rev._isBalance = leftData._isBalance && rightData._isBalance && (std::fabs(leftData._depth-rightData._depth) < 2);
return rev;
}
FullTreeRev BinaryTree::preocessFullTree(TreeNode* root)
{
if (root == nullptr)
{
return FullTreeRev(0, 0);
}
FullTreeRev leftData = preocessFullTree(root->leftChild);
FullTreeRev rightData = preocessFullTree(root->rightChild);
unsigned int nodes = leftData._nodes + rightData._nodes + 1;
unsigned int depth = std::max(leftData._depth, rightData._depth);
return FullTreeRev(nodes, depth);
}
bool BinaryTree::isFullTree(TreeNode* root)
{
FullTreeRev ret = preocessFullTree(root);
return ret._nodes == ( 1 << ret._depth)-1;
}
//是否是平衡二叉树
// X的左子树必须是平衡树且X的右树也是平衡的
//并且左树与右树的高度不超过1
bool BinaryTree::isBalanceTree(TreeNode* root)
{
return this->processBLT(root)._isBalance;
}
void BinaryTree::setMapNodeParent(TreeNode* node, std::map<TreeNode*, TreeNode*>& fatherMap)
{
if (node == nullptr)
{
return ;
}
fatherMap.insert(std::make_pair(node->leftChild, node));
fatherMap.insert(std::make_pair(node->rightChild, node));
setMapNodeParent(node->leftChild, fatherMap);
setMapNodeParent(node->rightChild,fatherMap);
}
TreeNode BinaryTree::getLowestCrossNode(TreeNode* root, TreeNode* head1, TreeNode* head2)
{
if (root == nullptr)
{
return NULL;
}
std::map<TreeNode*, TreeNode*> fatherMap;
//根节点把自己放进去
fatherMap.insert(std::make_pair(root, root));
//递归遍历整棵树,将节点与其父节点建立联系
setMapNodeParent(root, fatherMap);
//创建一个set表用来记录某一个节点到根节点的所有节点
std::set<TreeNode*> _head1Set;
TreeNode* cur1 = head1;
TreeNode* cmp = nullptr;
auto iter = fatherMap.find(cur1);
if (iter != fatherMap.end())
cmp = iter->second;
//如果当前节点的父节点不是自己,则说明还没有到根节点
while (cur1 != cmp)
{
_head1Set.insert(cmp);
cur1 = cmp;
iter = fatherMap.find(cur1);
if (iter != fatherMap.end())
cmp = iter->second;
}
TreeNode* cur2 = head2;
iter = fatherMap.find(cur2);
if (iter != fatherMap.end())
cmp = iter->second;
while (cur2 != cmp)
{
auto resulter = _head1Set.find(cmp);
if (resulter != _head1Set.end())
return TreeNode(cmp->_val);
cur2 = cmp;
iter = fatherMap.find(cur2);
if (iter != fatherMap.end())
cmp = iter->second;
}
return TreeNode(root->_val);
}
TreeNode* BinaryTree::getLowestCrossNode1(TreeNode* root, TreeNode* head1, TreeNode* head2)
{
//如果当前节点为空,或者当前节点为head1 or head2,那么都返回当前节点
if (root == nullptr || root == head1 || root == head2)
{
return root;
}
TreeNode* left = getLowestCrossNode1(root->leftChild, head1, head2);
TreeNode* right = getLowestCrossNode1(root->rightChild, head1, head2);
if (left != nullptr && right != nullptr)
{
return root;
}
return left!=nullptr?left:right;
}
int main(int argc, const char** argv)
{
BinaryTree testTree;
TreeNode node1[] = {19,7,25,5,11,15,21,61 ,4};
for (unsigned int i = 1; i < sizeof(node1)/sizeof(node1[0]); ++i)
{
testTree.insertTreeNode(&node1[0], &node1[i]);
}
int width_size = testTree.getBinaryTreeWidth(&node1[0]);
std::cout << "width_size = " << width_size << std::endl;
std::cout << std::endl;
return 0;
}