二叉树与回溯算法

作者: Vophan | 来源:发表于2019-01-27 19:25 被阅读20次

前情提要:
在上次KNN中我们用到了KD树的搭建以及回溯算法,尤其是回溯算法给我搞得要死要活的,所以今天停了一下手里的工作,来重新学些一次二叉树的搭建以及深度优先搜索的三种遍历方式。

二叉树是什么

计算机科学中,二叉树(英语:Binary tree)是每个节点最多只有两个分支(即不存在分支度大于2的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒。

这是来自wiki的解释,我们来划一下重点:
1、最多只有两个
2、具有左右顺序,不能颠倒

搭建二叉树

写到这里,我又停了几分钟,因为遍历的时候有前序,中序,后序,那么搭建有没有呢?我仔细想了一下,我觉得有,因为之所以在遍历的时候,会有这些情况,是因为在遍历的时候,我们会有一个取值的操作,正是这个取值的操作的顺序决定了三种不同的遍历方式,相似的,搭建的时候也有一个赋值的操作,所以我们应该也可以用三种方式搭建(这是深度优先搜索的范畴,广度优先搜索不行,因为广度优先搜索依赖于队列长度)。
现在先用前序展示一下:

pNode BinaryTree::PlanetTree(int val, int pos) {
    pNode tree = new node(val);
    if (pos >= depth) {
        return tree;
    } else {
        srand(time(0));
        tree->left = PlanetTree(rand() % 9, pos + 1);
        tree->right = PlanetTree(rand() % 9, pos + 1);
        return tree;
    }
}

写到这里,我还加入一个小插曲:
因为懒得给每个节点赋值,于是我想用c++的随机数来生成,想着就掏出了

#include <random>

等我rand()函数一用,傻眼了。
怎么数都一样呢?

rand()函数是C++标准函数库提供的随机数生成器,生成0-RAND_MAX之间的一个“伪随机”整数,理论上可以产生的最大数值为2^16-1,即32767。
rand()函数不接受参数,默认以1为种子(seed,即起始值),这里的种子在随机数产生的过程中起了很大的作用,甚至可以说是起了决定性的作用。

想必大家现在明白了,就是因为这个种子数都是1,导致了所有的随机数一样,当时我看到这里,整个人都不好了,说好的随机数呢?但是,深入一查,原来是这样(偷笑

#include <time.h>
#include <random>

//仅仅是演示,别问我主方法哪里去了
srand(time(0));
int x = rand();
int y = rand() % (b - a) * a; //生成区间[a, b]之间的随机数,友情提示,a不要为0哦。

然后还有什么呢?
我还想说一下,c++ 的伪随机数是怎么生成的?
电脑是做不到真正随机的,给大家返回的都是伪随机数,想必这个大家都知道。但是,伪随机数是怎么产生的呢?
计算机对一个数(随机种子)进行线性变化,得到一个数,这个数就是伪随机数,但是当随机种子多的时候,这些数就成高斯分布,所以把他视为随机数。


扯远了。
然后是什么呢?

遍历二叉树

遍历二叉树其实有两种方法,
1、广度优先搜索
2、深度优先搜索
我说一下,我对他们两个的理解吧。
凡是DFS可以解决的问题,BFS都可以,而且,DFS因为他的递归思想简单易懂,而处于鄙视链的下游,而且BFS可以自定义队列长度,不用担心递归太深而爆栈(其实DFS也行,就是自己写个栈,然后不调用递归,也就是非递归调用方式)
今天我们主要说一下DFS:

前序遍历

所谓前序遍历,就是遍历的时候,先取根节点(狭义的根节点)的值,然后再取左子树,最后右子树。
反应在代码上就是:

void BinaryTree::PreOrder(pNode root) {
    if (root != NULL) {
        std::cout<<root->val<<std::endl;
        PreOrder(root->left);
        PreOrder(root->right);
    }
}

简单吧,注意取值的位置,也就是输出的位置。

中序遍历

对比前序遍历,我们就可以发现不同的地方,中序遍历就是,先取左节点(最左面节点)的值,然后再去取根节点的值,最后取右节点的值。
代码:

void BinaryTree::InOrder(pNode root) {
    if (root) {
        InOrder(root->left);
        std::cout<<root->val<<std::endl;
        InOrder(root->right);
    }
}

后序遍历

后序遍历就比较有意思了,大家可以先猜一下是什么顺序,再看后面的代码

void BinaryTree::PostOrder(pNode root) {
    if (root) {
        PostOrder(root->left);
        PostOrder(root->right);
        std::cout<<root->val<<std::endl;
    }
}

没错,左右中


联想

在知乎,看到一个回答,说递归爆栈,但是BFS没事的,让我想到了,那种非递归的实现方式,我就好奇,为啥都叫栈,实现方法就不一样呢,后来我发现其实这两种都一样,都是调用栈,只不过为了防止爆栈,自己实现了一个栈。

最后贴上完整代码:

//
// Created by vophan on 19-1-27.
//

#ifndef BINARYTREE_BINARYTREE_H
#define BINARYTREE_BINARYTREE_H

#include <random>
#include <time.h>

typedef struct Node{
    int val;
    struct Node* left;
    struct Node* right;
    Node(int val):val(val) {}
}node,*pNode;

class BinaryTree{
public:
    BinaryTree(int depth);
    int depth;
    pNode root;
    pNode PlanetTree(int val, int pos);
    void PreOrder(pNode root);
    void InOrder(pNode root);
    void PostOrder(pNode root);
};

BinaryTree::BinaryTree(int depth):depth(depth) {
    root = PlanetTree(1,0);
}
pNode BinaryTree::PlanetTree(int val, int pos) {
    pNode tree = new node(val);
    if (pos >= depth) {
        return tree;
    } else {
        srand(time(0));
        tree->left = PlanetTree(rand() % 9, pos + 1);
        tree->right = PlanetTree(rand() % 9, pos + 1);
        return tree;
    }

}

void BinaryTree::PreOrder(pNode root) {
    if (root != NULL) {
        std::cout<<root->val<<std::endl;
        PreOrder(root->left);
        PreOrder(root->right);
    }
}

void BinaryTree::InOrder(pNode root) {
    if (root) {
        InOrder(root->left);
        std::cout<<root->val<<std::endl;
        InOrder(root->right);
    }
}

void BinaryTree::PostOrder(pNode root) {
    if (root) {
        PostOrder(root->left);
        PostOrder(root->right);
        std::cout<<root->val<<std::endl;
    }

}

#endif //BINARYTREE_BINARYTREE_H

相关文章

  • Algorithm小白入门 -- 二叉树

    二叉树二叉树构造二叉树寻找重复子树 1. 二叉树 基本二叉树节点如下: 很多经典算法,比如回溯、动态规划、分治算法...

  • 递归2-回溯与递归

    I. 回溯算法基础 回溯与递归的区别和联系  很多人不理解回溯与递归到底是什么关系。其实很简单,回溯算法是一种算法...

  • 回溯算法

    回溯算法 回溯算法介绍   回溯算法并不是非常复杂的算法, 很多人接触过但是未必知道这是回溯算法; 典型的回溯算法...

  • 搜索与回溯算法模板及其应用

    本文介绍了搜索与回溯算法模板及其应用,主要包括: 【1】 搜索与回溯算法基本思想【2】模板算法1及其应用(素数环问...

  • 回溯算法:八皇后问题和0-1背包问题

    文章结构 如何理解回溯算法 回溯算法的经典应用 完整源码 图片来源 1. 如何理解回溯算法 1.1 什么是回溯算法...

  • 二叉树与回溯算法

    前情提要:在上次KNN中我们用到了KD树的搭建以及回溯算法,尤其是回溯算法给我搞得要死要活的,所以今天停了一下手里...

  • 高频题

    字符串 数学 数组 二叉树 二分查找 链表 动态规划 贪心算法 堆 广度优先搜索 深度优先 哈希表 回溯算法 设计

  • Algorithm进阶计划 -- 回溯算法

    滑动窗口算法回溯算法框架回溯算法运用 1. 回溯算法框架 回溯算法,是类似枚举的搜索尝试过程,主要是在搜索尝试过程...

  • 分支限界

    类似【回溯算法】,也是一种在问题的解空间树上搜索问题解的算法。但一般情况下,【分支限界】与【回溯算法】的求解目标不...

  • 回溯算法总结

    回溯法学习总结 回溯算法也是算法导论中常用的算法,回溯算法类似于暴力求解算法,经常用在求可能解的问题。下面我将从三...

网友评论

    本文标题:二叉树与回溯算法

    本文链接:https://www.haomeiwen.com/subject/rafnjqtx.html