• 构造数组的MaxTree


    【说明】:

      本文是左程云老师所著的《程序员面试代码指南》第一章中“构造数组的MaxTree”这一题目的C++复现。

      本文只包含问题描述、C++代码的实现以及简单的思路,不包含解析说明,具体的问题解析请参考原书。

      感谢左程云老师的支持。

    【题目】:

      定义二叉树节点如下:

    class Node
    {
    public:
        Node(int data)
        {
            value = data;
            left = NULL;
            right = NULL;
        }
    public:
        int value;
        Node *left;
        Node *right;
    };
    View Code

      一个数组的 MaxTree 定义如下:

    •   数组必须没有重复元素;
    •   MaxTree 是一棵二叉树,数组的每一个值对应一个二叉树节点;
    •   包括 MaxTree 树在内且在其中一的每一棵子树上,值最大的节点都是树的头。

      给定一个没有重复元素的数组 arr,写出生成这个数组的 MaxTree 的函数,要求如果数组长度为 N,则时间复杂度为 O(N),额外空间复杂度为 O(N)。

     【思路】:

      利用栈找到每个数左右两边第一个比它大的数,并且用hash_map保存。(估计这样说大家都不明白,大家可以看原书,或者分析代码喽)

    【编译环境】:

      CentOS6.7(x86_64)

      gcc 4.4.7

     【实现】:

      实现及测试代码:

    #include <sstream>
    
    using namespace std;
    using namespace __gnu_cxx;
    
    class Node
    {
    public:
        Node(int data)
        {
            value = data;
            left = NULL;
            right = NULL;
        }
    public:
        int value;
        Node *left;
        Node *right;
    };
    
    /*
     *函数说明:利用hash_map分别为每一个数的左边和右边第一个最大值(序号)设定关联
     *输入参数:s为存放数组序号的栈;map为待建好的映射表
     *输出参数:s为存放数组序号的栈;map为整理好的映射表
     *返回值:
     */
    void popStackSetMap(stack<int> &s,hash_map<int,string> &map)
    {
        int tmp = s.top();
        s.pop();
        stringstream ss;
        string str;
        if(s.empty())
        {
            map[tmp] = str;
        }
        else
        {
            ss << s.top();
            ss >> str;
            map[tmp] = str;        //序号对应序号的关系
        }
        return ;
    }
    
    
    
    
    Node* getMaxTree(int a[],int len)
    {
        stack<int> s;
        hash_map<int,string> lBigMap;
        //为数组内的每个数据分别对应一个左端第一个最大值,没有则为空
        for(int i=0; i<len; i++)
        {
            while(!s.empty() && a[s.top()] < a[i])
                popStackSetMap(s,lBigMap);
            s.push(i);    //注意,这里保存的是数组序号,而不是数组值
        }
        while(!s.empty())
            popStackSetMap(s,lBigMap);
        //为数组内的每个数据分别对应一个右端第一个最大值,没有则为空
        hash_map<int,string> rBigMap;
        for(int i=len-1; i>=0; i--)
        {
            while(!s.empty() && a[s.top()] < a[i])
                popStackSetMap(s,rBigMap);
            s.push(i);    //注意,这里保存的是数组序号,而不是数组值
        }
        while(!s.empty())
            popStackSetMap(s,rBigMap);
        //构造Node数组
        Node* nArr[len];
        for(int i=0;i<len;i++)
        {
            nArr[i] = new Node(a[i]);
        }
        //构造Node的MaxTree
        Node *head = NULL;
        for(int i=0;i<len;i++)
        {
            //调试代码
            //cout << "nArr[" << i << "]->value = " << nArr[i]->value << endl;
            //cout << "lBigMap = " << lBigMap[i] << endl;
            //cout << "rBigMap = " << rBigMap[i] << endl;
    
            string ls = lBigMap[i];
            string rs = rBigMap[i];
            if(ls.empty() && rs.empty())
            {
                head = nArr[i];
            }
            else if(ls.empty())
            {
                stringstream ss(rs);
                int rID;
                ss >> rID;
                if(NULL == nArr[rID]->left)
                    nArr[rID]->left = nArr[i];
                else
                    nArr[rID]->right = nArr[i];
            }
            else if(rs.empty())
            {
                stringstream ss(ls);
                int lID;
                ss >> lID;
                if(NULL == nArr[lID]->left)
                    nArr[lID]->left = nArr[i];
                else
                    nArr[lID]->right = nArr[i];
            }
            else
            {
                stringstream lss(ls);
                int lID;
                lss >> lID;
    
                stringstream rss(rs);
                int rID;
                rss >> rID;
    
                int pID = a[lID] < a[rID] ? lID : rID;
                
                if(NULL == nArr[pID]->left)
                    nArr[pID]->left = nArr[i];
                else
                    nArr[pID]->right = nArr[i];
            }
        }
        return head;
    }
    
    int main()
    {
        int a[] = {3,4,5,1,2};
        Node *head = getMaxTree(a,5);
        Node *left = head->left;
        Node *right = head->right;
        cout << "head->value = "<< head->value << endl;
        cout << head->value <<"->left->value = "<< left->value << endl;
        cout << head->value <<"->right->value = "<< right->value << endl;
        if(NULL != left->left)
            cout << left->value << "->left->value = " << left->left->value << endl;
        else 
            cout << left->value << "->left is empty!" << endl;
            
        if(NULL != left->right)
            cout << left->value << "->right->value = " << left->right->value << endl;
        else 
            cout << left->value << "->right is empty!" << endl;
        
        if(NULL != right->left)
            cout << right->value << "->left->value = " << right->left->value << endl;
        else 
            cout << right->value << "->left is empty!" << endl;
        
        if(NULL != right->right)
            cout << right->value << "->right->value = " << right->right->value << endl;
        else 
            cout << right->value << "->right is empty!" << endl;
        return 0;
    }
    View Code

    【说明】

      1、hash_map并不属于标准的STL,但是大部分的开发环境已经将其实现。在GCC中也可直接使用,但是需要声明命名空间为 __gnu_cxx;

      2、C++中的hash_map的使用自定义的类型时,需要在类中实现hash函数和等于比较函数,所以我使用了string作为替代;

      3、在string与int类型的互换中,我使用了stringstream这一辅助类型。

      4、关键的思路还是参考了左程云老师提出的思路,具体实现有些差别(我是将数组的序号压入栈中,在hash_Map中将数组序号(int类型)与字符串类型(string)相对应),小伙伴们可以自行查看。

      5、hash_map使用方法的参考文章:

          C++ STL中哈希表 hash_map介绍

          map,hashmap与自定义类型

      6、我觉得这个题目十分像堆排序中的建立大顶堆这一问题,只不过在排序中自然不可能形成二叉树这么明显的结构了。堆排序代码如下:

     1 void print(int a[], int n){  
     2     for(int j= 0; j<n; j++){  
     3         cout<<a[j] <<"  ";  
     4     }  
     5     cout<<endl;  
     6 }  
     7   
     8   
     9   
    10 /** 
    11  * 已知H[s…m]除了H[s] 外均满足堆的定义 
    12  * 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选,  
    13  * 
    14  * @param H是待调整的堆数组 
    15  * @param s是待调整的数组元素的位置 
    16  * @param length是数组的长度 
    17  * 
    18  */  
    19 void HeapAdjust(int H[],int s, int length)  
    20 {  
    21     int tmp  = H[s];  
    22     int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置)  
    23     while (child < length) {  
    24         if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)  
    25             ++child ;  
    26         }  
    27         if(H[s]<H[child]) {  // 如果较大的子结点大于父结点  
    28             H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点  
    29             s = child;       // 重新设置s ,即待调整的下一个结点的位置  
    30             child = 2*s+1;  
    31         }  else {            // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出  
    32              break;  
    33         }  
    34         H[s] = tmp;         // 当前待调整的结点放到比其大的孩子结点位置上  
    35     }  
    36     print(H,length);  
    37 }  
    38   
    39   
    40 /** 
    41  * 初始堆进行调整 
    42  * 将H[0..length-1]建成堆 
    43  * 调整完之后第一个元素是序列的最小的元素 
    44  */  
    45 void BuildingHeap(int H[], int length)  
    46 {   
    47     //最后一个有孩子的节点的位置 i=  (length/2 -2),此处和原文不同  
    48     for (int i = (length/2 -1) ; i >= 0; --i)  
    49         HeapAdjust(H,i,length);  
    50 }  
    51 /** 
    52  * 堆排序算法 
    53  */  
    54 void HeapSort(int H[],int length)  
    55 {  
    56     //初始堆  
    57     BuildingHeap(H, length);  
    58     //从最后一个元素开始对序列进行调整  
    59     for (int i = length - 1; i > 0; --i)  
    60     {  
    61         //交换堆顶元素H[0]和堆中最后一个元素  
    62         int temp = H[i]; H[i] = H[0]; H[0] = temp;  
    63         //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整  
    64         HeapAdjust(H,0,i);  
    65   }  
    66 }   
    67   
    68 int main(){  
    69     int H[10] = {3,1,5,7,2,4,9,6,10,8};  
    70     cout<<"初始值:";  
    71     print(H,10);  
    72     HeapSort(H,10);  
    73     //selectSort(a, 8);  
    74     cout<<"结果:";  
    75     print(H,10);  
    76   
    77 }  
    View Code

      堆排序的代码我是直接从 八大排序算法 这一文章内直接copy过来的,里面有一处不同(//最后一个有孩子的节点的位置 ,我认为应该时i=  (length/2 -1) )。

      利用大顶堆的方法实现MaxTree,代码如下:

      1 /*
      2  *文件名:bigTopHeap_MaxTree.cpp
      3  *作者:
      4  *摘要:利用大顶堆的方法实现MaxTree
      5  */
      6 
      7 #include <iostream>
      8 
      9 using namespace std;
     10 
     11 class Node
     12 {
     13 public:
     14     Node(int data)
     15     {
     16         value = data;
     17         left = NULL;
     18         right = NULL;
     19     }
     20 public:
     21     int value;
     22     Node *left;
     23     Node *right;
     24 };
     25 
     26 //利用数组建立大顶堆
     27 void buildBTHeap(int arr[],int len) 
     28 {
     29     int i = len/2 - 1;
     30     for(; i >= 0; i--)
     31     {
     32         int fpos = i;
     33         int fvalue = arr[fpos];
     34         int child = 2*i + 1;
     35         while(child < len)
     36         {
     37             if( child+1 < len && arr[child] < arr[child+1])
     38                 child++;
     39             if(fvalue < arr[child])
     40             {
     41                 arr[fpos] = arr[child];
     42                 fpos = child;
     43                 child = 2*fpos + 1;
     44             }
     45             else
     46                 break;
     47             arr[fpos] = fvalue;
     48         }
     49     }
     50     for(int i=0;i<len;i++)
     51         cout << arr[i] << " " ;
     52     cout << endl;
     53 }
     54 
     55 //利用数组建立二叉树(顺序建立)
     56 Node* buildBinaryTree(int arr[],int len,int pos=0)
     57 {
     58     if(NULL == arr || 0 >= len)
     59         return NULL;
     60     if(pos >= len)
     61         return NULL;
     62     Node *head = new Node(arr[pos]);
     63     head->left = buildBinaryTree(arr,len,2*pos+1);
     64     head->right = buildBinaryTree(arr,len,2*pos+2);
     65     return head;
     66 }
     67 
     68 Node *getMaxTree(int arr[],int len)
     69 {
     70     buildBTHeap(arr,len);
     71     return buildBinaryTree(arr,len);
     72 }
     73 
     74 int main()
     75 {
     76     int a[] = {3,4,5,1,2};
     77     Node *head = getMaxTree(a,5);
     78     Node *left = head->left;
     79     Node *right = head->right;
     80     cout << "head->value = "<< head->value << endl;
     81     cout << head->value <<"->left->value = "<< left->value << endl;
     82     cout << head->value <<"->right->value = "<< right->value << endl;
     83     if(NULL != left->left)
     84         cout << left->value << "->left->value = " << left->left->value << endl;
     85     else 
     86         cout << left->value << "->left is empty!" << endl;
     87         
     88     if(NULL != left->right)
     89         cout << left->value << "->right->value = " << left->right->value << endl;
     90     else 
     91         cout << left->value << "->right is empty!" << endl;
     92     
     93     if(NULL != right->left)
     94         cout << right->value << "->left->value = " << right->left->value << endl;
     95     else 
     96         cout << right->value << "->left is empty!" << endl;
     97     
     98     if(NULL != right->right)
     99         cout << right->value << "->right->value = " << right->right->value << endl;
    100     else 
    101         cout << right->value << "->right is empty!" << endl;
    102     return 0;
    103 }
    View Code

      大顶堆的方法实现的 MaxTree 结构和栈方法实现的 MaxTree 结构略有不同,但也达到了要求。

      7、测试代码未使用二叉树的遍历算法,大家就凑合着看看吧。^_^  ^_^   ^_^

    注:

      转载请注明出处;

      转载请注明源思路来自于左程云老师的《程序员代码面试指南》。

  • 相关阅读:
    Python图形图像处理库的介绍之Image模块
    python re.sub
    eclipse 安装git插件
    一组神奇的 3D Gif 动图
    互联网颠覆房地产
    一位IT行业高收入者的理财规划方案
    阿里核心系统团队介绍
    大规模SNS中兴趣圈子的自动挖掘
    关于 MySQL LEFT JOIN 你可能需要了解的三点
    Could not connect to SMTP host: localhost, port: 25;
  • 原文地址:https://www.cnblogs.com/PrimeLife/p/5339965.html
Copyright © 2020-2023  润新知