劍指Offer(面試題38~39)

面試題38:數字在排序數組中出現的次數

題目:統計一個數字在排序數組中出現的次數。例如輸入排序數組{1,2,3,3,3,3,4,5}和數字3,由於3在這個數組中出現了4次,因此輸出4。

//遞歸的代碼找到排列數組中的第一個k:
int GetFirstK(int* data,int length,int k,int start,int end)
{
    if(start > end)
        return -1;

    int middleIndex = (start + end) / 2;
    int middleData = data[middleIndex];

    if(middleData == k)
    {
        if((middleIndex > 0 && data[middleIndex - 1] != k)
           || middleIndex == 0)
            return middleIndex;
        else
            end = middleIndex - 1;
    }
    else if(middleData > k)
        end = middleIndex - 1;
    else
        start = middleIndex + 1;

    return GetFirstK(data,length,k,start,end);
}

int GetLastK(int* data,int length,int k,int start,int end)
{
    if(start > end)
        return -1;

    int middleIndex = (start + end) / 2;
    int middleData = data[middleIndex];

    if(middleData == k)
    {
        if((middleIndex < length - 1 && data[middleIndex +1 ] != k)
           || middleIndex == length - 1)
        {
            return middleIndex;
        }
        else
            start = middleIndex + 1;
    }
    else if(middleData < k)
        start =middleIndex + 1;
    else
        end = middleIndex - 1;

    return GetLastK(data,length,k,start,end);
}

//在分別找到第一個k和最後一個k的下標之後,我們就能計算出k在數組中出現的次數了。
int GetNumberOfK(int* data,int length, int k)
{
    int number = 0;

    if(data != NULL && length > 0)
    {
        int first = GetFirstK(data,length,k,0,length -1);
        int last = GetLastK(data,length,k,0,length - 1);

        if(first > -1 && last > -1)
            number = last - first +1;
    }

    return number;
}

在上述代碼中,GetFirstK和GetLastK都是用二叉查找法在數組中查找一個合乎要求的數字,它們的時間複雜度都是O(logn),因此GetNumberOfK的總的時間複雜度也只有O(logn)。

面試題39:二叉樹的深度

題目一:輸入一棵二叉樹的根結點,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。
二叉樹的結點定義如下:

struct BinaryTreeNode
{
    int              m_nValue;
    BinaryTreeNode*  m_pLeft;
    BinaryTreeNode*  m_pRight;
};

用遞歸的方式實現代碼如下:

int TreeDepth(BinaryTreeNode* pRoot)
{
    if(pRoot == NULL)
        return 0;

    int nLeft = TreeDepth(pRoot->m_pLeft);
    int nRight = TreeDepth(pRoot->m_pRight);

    return (nLeft > nRight) ? (nLeft + 1):(nRight + 1);
}

題目二:輸入一棵二叉樹的根結點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意結點的左右子樹的深度相差不超過1,那麼它就是一棵平衡二叉樹。

方法一:調用函數TreeDepth得到它的左右子樹的深度。如果每個結點的左右子樹的深度相差都不超過1,按照定義它就是一棵平衡的二叉樹。代碼如下:

bool IsBalanced(BinaryTreeNode* pRoot)
{
    if(pRoot == NULL)
        return true;

    int left = TreeDepth(pRoot->m_pLeft);
    int right = TreeDepth(pRoot->m_pRight);
    int diff = left - right;
    if(diff > 1 || diff < -1)
        return false;

    return IsBalanced(pRoot->m_pLeft) && IsBalanced(pRoot->m_pRight);
}

上面的代碼固然簡潔,但我們也要注意到由於一個結點會被重複遍歷多次,這種思路的時間效率不高。

每個結點只遍歷一次的解法,正是面試官喜歡的

bool IsBalanced(BinaryTreeNode* pRoot,int* pDepth)
{
    if(pRoot == NULL)
    {
        *pDepth = 0;
        return true;
    }

    int left,right;
    if(IsBalanced(pRoot->m_pLeft,&left)
       && IsBalanced(pRoot->m_pRight,&right))
    {
        int diff = left - right;
        if(diff <= 1 && diff >= -1)
        {
            *pDepth = 1 + (left > right ? left :right);
            return true;
        }
    }
    return false;
}
bool IsBalanced(BinaryTreeNode* pRoot)
{
    int depth = 0;
    return IsBalanced(pRoot,&depth);
}

在上面的代碼中,我們用後序遍歷的方式遍歷整棵二叉樹。在遍歷某結點的左右子結點之後,我們可以根據它的左右子結點的深度判斷它是不是平衡的,並得到當前結點的深度。當最後遍歷到樹的根結點的時候,也就判斷整棵二叉樹是不是平衡二叉樹。

參考資料《劍指Offer》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章