由数组创建完全二叉树和二叉树的遍历

12 篇文章 0 订阅

由数组创建完全二叉树


struct TreeNode{
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x):val(x),left(NULL),right(NULL){}
};
int main(){
    vector<int> a({4,2,5,1,3,-1,6,0});
    vector<TreeNode*> tree(a.size(),NULL);
    // 创建二叉树节点
    for(int i=0;i<a.size();++i){
        if(a[i]!=-1){
            tree[i] = new TreeNode(a[i]);
        }
    }
    // 创建二叉树:对所有非终端节点添加左右子树
    for(int i=0;i<=a.size()/2-1;++i){
        tree[i]->left = tree[2*i+1];
        if(2*i+2 < a.size()){
            tree[i]->right = tree[2*i+2];
        }
    }
}

一、前序遍历

//递归前序遍历
void preorder(node*& root) {
    if (root == NULL) {
        return;
    }
    cout << root->val << ' '; //访问根节点
    preorder(root->left);
    preorder(root->right);
}
//非递归前序遍历
//借助栈,先压入根节点,每访问一个节点时出栈,然后先压右孩子,再压左孩子。
void preorder(node* root) {
    stack<node*> st;
    st.push(root);
    while (!st.empty()) {
        node* cur = st.top();
        cout << cur->val << ' ';
        st.pop();
        if (cur->right != NULL) {
            st.push(cur->right);
        }
        if (cur->left != NULL) {
            st.push(cur->left);
        }
    }
}

二、中序遍历

//递归中序遍历
void inorder(node*& root) {
    if (root == NULL) {
        return;
    }
    inorder(root->left);
    cout << root->val << ' '; //访问根节点
    inorder(root->right);
}
//非递归中序遍历
//对于每个父节点,都是先访问其最左下角的节点,然后再访问相应父节点,再考虑右子树。因此借助栈,使用指针p追踪每个节点所有左子节点并入栈,
//然后访问栈顶元素,并将指针p指向其右子树;重复同样的操作。只有当p==NULL且栈中也没有元素时,才表明已遍历完所有节点。
void inorder(node*& root) {
    if (root == NULL) {
        return;
    }
    stack<node*> st;
    node* p = root;
    while (!st.empty() || p != NULL) {
        //扫描p的所有左节点入栈
        while (p != NULL) {
            st.push(p);
            p = p->left;
        }
        if (!st.empty()) {
            p = st.top();
            st.pop();
            cout << p->val << ' ';
            p = p->right;
        }
    }
}

三、后序遍历

//递归后序遍历
void postorder(node* root) {
    if (root == NULL) {
        return;
    }
    postorder(root->left);
    postorder(root->right);
    cout << root->val << ' ';
}

因为后序非递归遍历二叉树的顺序是先访问左子树,再访问右子树,最后访问根节点。当用堆栈来存储节点,必须分清返回根节点时,是从左子树返回的,还从右子树返回的。所以,使用辅助指针r,其指向最近访问过的节点。也可以在节点中增加一个标志域,记录是否已被访问。
二叉树后序遍历的非递归实现

void postorder(TreeNode* root){
    TreeNode *p = root, *r = NULL;
    stack<TreeNode*> s;
    while(p!=NULL || !s.empty()){
        if(p!=NULL){
            s.push(p);
            p = p->left;
        }
        else{
            p = s.top();
            if(p->right!=NULL && p->right != r){  //右孩子非空且未被访问过
                p = p->right;
            }
            else{  //左右孩子都访问了
                s.pop();
                cout<<p->val<<ends;
                r = p;  //r记录最近访问的节点
                p = NULL;
            }
        }
    }//while
}

四、从前序与中序遍历序列构造二叉树

https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou-zao-9/
对于任意一颗树而言,前序遍历的形式总是
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
即根节点总是前序遍历中的第一个节点。
而中序遍历的形式总是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。

这样一来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置。

时间复杂度:O(n),其中 n 是树中的节点个数。

空间复杂度:O(n),除去返回的答案需要的 O(n) 空间之外,我们还需要使用 O(n) 的空间存储哈希映射,以及 O(h)(其中 h 是树的高度)的空间表示递归时栈空间。这里 h < n,所以总空间复杂度为 O(n)。

例如:先序:FDXEAG
中序:XDEFAG
先序第一个为根节点,在中序序列中找到此根节点的位置,则其左边size_left个节点为左子树的节点,右边为右子树的节点。把左子树所有节点(如XDE)进行递归得到左子树的序列,先序中对应的左子树的所有节点(如DXE)是从当前根节点(每次递归的根节点都是从先序序列中获取)开始往后的size_left个节点,两个序列中对应的左子树作为递归参数进行递归。

struct node{
    char val;
    node* left, *right;
    node() { ; }
    node(char x) :val(x), left(NULL), right(NULL) {}
};
void postorder(node* root) {
    if (root == NULL) {
        return;
    }
    postorder(root->left);
    postorder(root->right);
    cout << root->val;
}
//从前序与中序遍历序列构造二叉树(假设二叉树中无重复元素)
//后面4个int参数分别表示每次递归时先序和中序中对应子树的边界
node* buildtree(string& pre,string& in,unordered_map<char,int>& mp,int pre_l,int pre_h,int in_l,int in_h) {
    //当当前递归的子树(左子树或右子树)的左边界大于有边界时,说明此时该子树已无节点,则递归结束
    if (pre_l > pre_h) {
        return NULL;
    }
    //先构造根节点:当前先序子树的第一个元素
    node* root = new node(pre[pre_l]);
    int in_root = mp[pre[pre_l]]; //in_root为中序子树中的根节点下标
    int size_left = in_root - in_l; //中序中左子树节点数
    //递归构造左子树
    root->left = buildtree(pre, in, mp, pre_l+1, pre_l+size_left, in_l, in_root-1);
    //递归构造右子树
    root->right = buildtree(pre, in, mp, pre_l+size_left+1, pre_h, in_root+1, in_h);
    return root;
}
int main() {
    string s1, s2; //s1为前序遍历序列,s2为后序遍历序列
    while (cin >> s1 >> s2) {
        int n = s1.size(); //二叉树节点数
        unordered_map<char, int> mp;
        //将中序序列存入map,便于查找
        for (int i = 0; i < n; ++i) {
            mp[s2[i]] = i;
        }
        node* root = buildtree(s1,s2,mp,0,n-1,0,n-1);
        postorder(root);
		cout<<endl;
    }
}

五、从中序与后序遍历序列构造二叉树

https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/solution/

//从中序与后序遍历序列构造二叉树
node* buildtree(string& in,string& post, unordered_map<char, int>& mp, int post_l,int post_r,int in_l,int in_r) {
    //递归出口
    if (post_l > post_r) {
        return NULL;
    }
    node* root = new node(post[post_r]);//构造根节点:后序中子树的最后一个元素
    int in_root = mp[post[post_r]]; //中序子树中根节点下标
    int size_right = in_r - in_root; //中序中右子树节点数
    //递归构造右子树
    //root->right = buildtree(in, post, mp, post_r - size_right, post_r - 1, in_root + 1, in_r);
    //递归构造左子树
    root->left = buildtree(in, post, mp, post_l, post_r - size_right - 1, in_l, in_root - 1); 
    root->right = buildtree(in, post, mp, post_r - size_right, post_r - 1, in_root + 1, in_r);
    return root;
}

六、层次遍历 (广度优先搜索)

void bfs(node *root){
	queue<node*> Q;
	Q.push(root);
	while(!Q.empty()){
		node* tmp = Q.front();
		Q.pop();
		printf("%d",tmp->val);
		if(tmp->left!=NULL)
			Q.push(tmp->left);
		if(tmp->right!=NULL)
			Q.push(tmp->right);
	}
}

层次遍历每次遍历一层:
二叉树的层次遍历||

深度优先搜索

struct Node {
    int self;     // 數據
    Node *left;   // 左節點
    Node *right;  // 右節點
};

const int TREE_SIZE = 9;
std::stack<Node *> unvisited;
Node nodes[TREE_SIZE];
Node *current;

//初始化樹 (二叉树的创建)
for (int i = 0; i < TREE_SIZE; i++) {
    nodes[i].self = i;
    int child = i * 2 + 1;
    if (child < TREE_SIZE) // Left child
        nodes[i].left = &nodes[child];
    else
        nodes[i].left = NULL;
    child++;
    if (child < TREE_SIZE) // Right child
        nodes[i].right = &nodes[child];
    else
        nodes[i].right = NULL;
}
unvisited.push(&nodes[0]); //先把0放入UNVISITED stack

// 樹的深度優先搜索在二叉樹的特例下,就是二叉樹的先序遍歷操作(這裡是使用循環實現)
// 只有UNVISITED不空
while (!unvisited.empty()) {
    current = (unvisited.top()); //當前應該訪問的
    unvisited.pop();
    if (current->right != NULL)
        unvisited.push(current->right );
    if (current->left != NULL)
        unvisited.push(current->left);
    cout << current->self << endl;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值