算法导论 练习12.1

本文详细解析了基于给定关键字集构建不同高度的二叉搜索树的方法,并探讨了二叉搜索树与最小堆的性质差异。此外,还提供了在Θ(n)时间内完成先序、中序和后序遍历的非递归算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

12.1-1 对于关键字集合 { 1 , 4 , 5 , 10 , 16 , 17 , 21 } \{1,4,5,10,16,17,21\} {1,4,5,10,16,17,21},分别画出高度为2、3、4、5和6的二叉搜索树。

这里说明一下:算法导论中定义的高度是根节点到叶节点的最大距离

高度为2:      高度为3:                高度为4:
    10              10                      5
   /  \            /  \                    /  \
  4    17         4   16                  4    10
 / \   /\        / \   \                 /      \
1   5 16 21     1   5   17              1        16
                         \                         \
                          21                        17
                                                     \
                                                      21
高度为5:           高度为6:
    4                       1
   / \                       \
  1   5                       4
       \                       \
        10                      5
         \                       \
          16                      10
           \                        \
            17                       16
             \                         \
              21                        17 
                                          \
                                           21

12.1-2 二叉搜索树的性质与最小堆性质之间有什么不同?能使用最小堆性质在 O ( n ) O(n) O(n)时间内按序输出一颗n个结点树的关键字吗?可以的话,说明如何做,否则解释理由。

最小堆是一棵近似的完全二叉树(Perfect Binary Tree),满足堆中每个结点小于等于子结点。但是左右孩子之间的没有确定的大小关系。要想按序输出最小堆,需要使用堆排序的时间复杂度为 Ω ( n l g n ) \Omega(nlgn) Ω(nlgn)


12.1-3 设计一个执行中序遍历的非递归算法。(提示:一个容易的方法是使用栈作为辅助数组结构;另一种较复杂但比较简介的做法是不使用栈,但要假设能测试两个指针是否相等)。

这个问题和算法导论练习10.4-3和10.4-5的题目一致。我在另外一篇博客已经讲过,大家可以看下。
算法导论10.4-5题解


12.1-4 对于一棵有n个结点的树,请设计在 Θ ( n ) \Theta(n) Θ(n)时间内完成的先序遍历算法和后序遍历算法。

下面是我的实现代码,分别给出了先序和后序遍历的递归和非递归实现。

#include<iostream>
#include<stack>
#define NIL -1
using namespace std;

struct Tree
{
	Tree *left;
	Tree *right;
	int key;
};
//创建二叉树
void createTree(Tree *&t)
{
    int key;
    cin >> key;
    if(key==NIL){
        t=nullptr;
        return;
    }
    t=new Tree();
    t->key=key;
    createTree(t->left);
    createTree(t->right);
}
void print(const Tree* x)
{
    cout << x->key << " ";
}
void preOrder_recursion(Tree* x)
{
    if(x)
    {
        print(x);
        preOrder_recursion(x->left);
        preOrder_recursion(x->right);
    }
}
void preOrder_byStack(Tree *x)
{
    stack<Tree*> sta;
    Tree *p=x;
    while(p||!sta.empty())
    {
    while(p)
    {
        print(p);
        sta.push(p);
        p=p->left;
    }
    p=sta.top();
    sta.pop();
    p=p->right;
    }
}
void postOrder_recursion(Tree *x)
{
    if(x)
    {
        postOrder_recursion(x->left);
        postOrder_recursion(x->right);
        print(x);
    }
}
void postOrder_byStack(Tree *x)
{
    Tree *pre=nullptr;
    stack<Tree*> sta;
    while(x||!sta.empty())
    {
        while(x)//达到左子树
        {
            sta.push(x);
            x=x->left;
        }
        x=sta.top();
        //没有右子树或右子树已访问
        if(!x->right||x->right==pre){
            sta.pop();
            print(x);
            pre=x;
            x=nullptr;
        }
        else
        {
            x=x->right;
        }
    }
}
int main()
{
    Tree *head;
    createTree(head);
    cout << "先序遍历递归版:\n";
    preOrder_recursion(head);
    cout << "\n先序遍历非递归版:\n";
    preOrder_byStack(head);
    cout << "\n后序遍历递归版:\n";    
    postOrder_recursion(head);
    cout << "\n后序遍历非递归版:\n";        
    postOrder_byStack(head);
}

这有两组测试数据,分别是题目12.1-1高度为2和6的二叉树。

数据1:
10 4 1 -1 -1 5 -1 -1 17 16 -1 -1 21 -1 -1
结果:
先序: 10 4 1 5 17 16 21
后序: 1 5 4 16 21 17 10

数据2:
1 -1 4 -1 5 -1 10 -1 16 -1 17 -1 21 -1 -1
结果:
先序:1 4 5 10 16 17 21
后序:21 17 16 10 5 4 1


12.1-5 因为在基于比较排序模型中,完成n个元素的排序,其最坏情况下需要 Ω ( n l g n ) \Omega(nlgn) Ω(nlgn)时间。证明:任何基于比较的算法从n个元素的任意序列中构造一棵二叉搜索树,其最坏情况需要 Ω ( n l g n ) \Omega(nlgn) Ω(nlgn)的时间。

证明:反证法,假设我们我可以在最坏情况 ο ( n l g n ) \omicron(nlgn) ο(nlgn)下完成BST的建立。那么对于排序来说,我们可以通过构造BST树,然后遍历BST来实现。

根据定理12.1,我们可以在 Θ ( n ) \Theta(n) Θ(n)的时间内完成对BST的遍历。那么意味着我们在 ο ( n l g n ) \omicron(nlgn) ο(nlgn)的时间内完成对 n n n个元素的排序算法。这与Chapter 8的基于比较的排序算法的下界为 Ω ( n l g n ) \Omega(nlgn) Ω(nlgn)相矛盾。

因此,假设不成立。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值