数据结构之二叉树

一. 知识点整理

本文的图片均来自程杰编著的《大话数据结构》一书

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值