构造数组的MaxTree

Posted Stan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了构造数组的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、测试代码未使用二叉树的遍历算法,大家就凑合着看看吧。^_^  ^_^   ^_^

 

注:

  转载请注明出处;

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

以上是关于构造数组的MaxTree的主要内容,如果未能解决你的问题,请参考以下文章

数据结构--单调栈--构造数组的MaxTree

《程序员面试指南》第一章 栈和队列 构造数组的MaxTree

算法进阶面试题03——构造数组的MaxTree最大子矩阵的大小2017京东环形烽火台问题介绍Morris遍历并实现前序/中序/后序

AC日记——「HNOI2017」单旋 LiBreOJ 2018

Java中String类的方法及说明

Java中String类的方法及说明