c++图与树算法题

图与树

图结构:节点、边
分类:有向、无向
特殊的图:二叉树、普通树、堆
树的定义
  树是由一个集合以及在该集合上定义的一种关系构成的,集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构,在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点。

数据结构中有很多树的结构,其中包括二叉树、二叉搜索树、2-3树、红黑树等等,本文着重介绍二叉树。

树的基本术语
节点的度:一个节点含有的子树的个数称为该节点的度;
叶节点或终端节点:度为0的节点称为叶节点;
非终端节点或分支节点:度不为0的节点;
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
兄弟节点:具有相同父节点的节点互称为兄弟节点;
树的度:一棵树中,最大的节点的度称为树的度;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次;
堂兄弟节点:双亲在同一层的节点互为堂兄弟;
节点的祖先:从根到该节点所经分支上的所有节点;
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
森林:由m(m>=0)棵互不相交的树的集合称为森林;

二叉树
  二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构。

二叉树的定义:二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。

二叉树的性质
二叉树的第i层至多有 2 i − 1 2^i-1 2i1 个结点;
深度为k 的二叉树至多有 2 k − 1 2^{k-1} 2k1 个结点;
对任何一棵二叉树TT ,如果其终端结点数为n0n0 ,度为2的结点数为n2n2 ,则n0=n2+1n0=n2+1 。

完全二叉树:只在最后一层右边缺少若干节点;
对于完全二叉树,其编号为i的节点,左孩子为2i,右孩子为2i+1,双亲为i/2

二叉树框架

在这里插入图片描述
遍历约定:先左后又
DLR:先序遍历 ABDHIEJCFG
LDR:中序遍历 HDIBJEAFCG
LRD:后序遍历 HIDJEBFGCA

template <class DataType>//任何类型的数据
struct BiNode
{
   
    DataType data; //节点数据
    BiNode<DataType> * lchild,*rchild;//左孩子右孩子
};

template <class DataType>
class BiTree
{
   
public:
    BiTree(){
   root = Create(root);}
    ~BiTree(){
   Release(root);}
    void PreOrder(){
   PreOrder(root);}    //前序遍历
    void InOrder(){
   InOrder(root);}      //中序遍历
    void PostOrder(){
   PostOrder(root);}  //后序遍历
private:
    BiNode<DataType> * root;
    BiNode<DataType> * Create(BiNode<DataType> *bt);
    void Release(BiNode<DataType> *bt);
    void PreOrder(BiNode<DataType> *bt);
    void InOrder(BiNode<DataType> *bt);
    void PostOrder(BiNode<DataType> *bt);
};

创造

template <class DataType>
BiNode<DataType> *BiTree<DataType>::Create(BiNode<DataType> *bt)
{
   
    DataType ch;
    cin>>ch;
    if(ch == '#') bt = NULL;
    else{
   
        bt = new BiNode<DataType>;
        bt->data = ch;
        bt->lchild = Create(bt->lchild);//递归创建,先输入的是跟节点,之后输入左孩子,当不输入左孩子时输入#再输入其右节点
        bt->rchild = Create(bt->rchild);
    }
    return bt;
}

释放

template <class DataType>
void BiTree<DataType>::Release(BiNode<DataType> *bt)
{
   
    if(bt != NULL){
   
        Release(bt->lchild);//递归释放
        Release(bt->rchild);
        delete bt;
    }
}

前序遍历

template <class DataType>
void BiTree<DataType>::PreOrder(BiNode<DataType> *bt)
{
   
    if(bt == NULL) return;
    else{
   
        cout<<bt->data;
        PreOrder(bt->lchild); 
        PreOrder(bt->rchild);
    }
}

中序遍历

template <class DataType>
void BiTree<DataType>::InOrder(BiNode<DataType> *bt)
{
   
    if(bt == NULL) return;
    else{
   
        InOrder(bt->lchild);
        cout<<bt->data;
        InOrder(bt->rchild);
    }
}

后序遍历

template <class DataType>
void BiTree<DataType>::PostOrder(BiNode<DataType> *bt)
{
   
    if(bt == NULL) return;
    else{
   
        PostOrder(bt->lchild);
        PostOrder(bt->rchild);
        cout<<bt->data;
    }
}

树与图算法题

左子树叶节点之和 (深度优先+广度优先+递归)

3
/
9 20
/
15 7
比如上面 9 和15是左子树上的子节点, 那么求和得 24.

struct TreeNode {
   
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {
   }
};

我们需要用一个队列来实现广度优先. 每次循环的时候从队首取出一个节点, 然后把它的子节点(也就是左右子节点, 如果有的话)添加到队列中, 直到队列为空. 我们还需要把左右子节点的状态(也就是左边还是右边)记录到节点中, 所以我们用了一个 std::pair 这么一个数据结构.

class Solution {
   
public:
    int sumOfLeftLeaves(TreeNode* root) {
   
        if (root == NULL) return 0;
        queue< pair<TreeNode*, bool> > q;
        q.push(make_pair(root, false));
        auto r = 0;
        while (!q.empty()) {
   
            auto p = q.front();
            q.pop();
            if (p.first->left == NULL && p.first->right == NULL && p.second) {
   
                r += p.first->val;  // sum the value if it comes from left
            }
            if (p.first->left) {
   
                q.push(make_pair(p.first->left, true));                
            }
            if (p.first->right) {
   
                q.push(make_pair(p.first->right, false));
            }
        }
        return r;        
    }
};

深度优先 DFS (DEPTH FIRST SEARCH)
深度优先访问树的方式也很直观, 就是一条路能往左下走就往左下走, 直到叶子节点, 然后再回溯到右节点. 我们可以通过递归的方式 来实现, 当然也可以自己创建和维护堆栈来实现DFS.

class Solution {
   
public:
    void walk(TreeNode* root, bool left) {
   
        if (root == NULL) return;
        // leaf
        if (root->left == NULL && root->right == NULL) {
   
            if (left) {
    // sum only left branches
                sum += root->val;
            }
        }
        if (root->left != NULL) {
    // DFS left sub tree
            walk(root->left, true);
        }
        if (root->right != NULL) {
    // DFS right sub tree
            walk(root->right, false);
        }
    }
    int sumOfLeftLeaves(TreeNode* root) {
   
        if (root) {
   
            walk(root->left, true);
            walk(root->right, false);
        }
        return sum;
    }
private:
    int sum = 0;
};

给定二叉树前、中序遍历,构造二叉树

前序遍历第一个节点是根节点x;
从中序遍历序列中找到根节点x;
中序遍历中的x的左边序列对应等长的前序遍历序列——左子树;
中序遍历中的x的右边序列对应等长的前序遍历序列——右子树;

二叉树的下一个节点

二叉树的下一个节点
给定一颗二叉树和其中一个节点,找到中序遍历的下一个节点,树中有指向左、右两个子树的指针,还有指向父节点的指针
在这里插入图片描述
在这里插入图片描述
关键是怎么就能知道只有这两种情况了,可能只有试了:
8的下一节点在4,因为其是左节点,直接找其父节点,这是最简单的情况;
9的下一节点在2,因为是右节点,而且是左子树,所以找其左子树父节点;
10的下一节点在5;
11的下一节点在12,因为是右节点,而且是右子树,所以找其更大的父节点;
4的节点是9,找其右子节点;
5的节点是11;
6的节点是16,找其右子节点的最左节点;

/*
二叉树的下一个节点
给定一颗二叉树和其中一个节点,找到中序遍历的下一个节点,树中有指向左、右两个子树的指针,还有
指向父节点的指针
*/
 
Struct TreeNode{
   
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode(int x) : val(x), left(NULL), rihgt(NULL){
   }
};
 
 
class Solution{
   
public:
	TreeNode* getNext(TreeNode* node){
   
		if(node == NULL){
   
			return NULL;
		}
		if(node->right != NULL){
              //如果节点的右子节点不为空,如4,5,2,1,6,3,7,13
			TreeNode* next = node->right; //右子节点
			while(next->left != NULL){
    //右子节点的左子节点一直求左子节点,例如6的下一个节点是16
				next = next->left;
			}
			return next;
		}                                   //右子节点为空
		while(node->parent != NULL)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值