剑指offer——解决面试题的思路

# 剑指offer——解决面试题的思路

1. 画图让抽象问题形象化

面试题19:二叉树的镜像

struct BinaryTreeNode{
    int m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};
/*前序遍历树的每个结点,若遍历到的结点有子结点,就交换他的两个子结点;当交换完所有非叶子结点的左右子结点后,就得到了树的镜像。*/
void MirrorRecursively(BinaryTreeNode* pNode){
    if (pNode==NULL)
        return;
    if (pNode->m_pLeft==NULL && pNode->m_pRight==NULL)
        return;
    BinaryTreeNode *pTemp = pNode->m_pLeft;
    pNode->m_pLeft = pNode->m_pRight;
    pNode->m_pRight = pTemp;
    if (pNode->m_pLeft)
        MirrorRecursively(pNode->m_pLeft);
    if (pNode->m_pRight)
        MirrorRecursively(pNode->m_pRight);
}
//循环解法
void Mirror(TreeNode *pRoot) {
    if (pRoot == nullptr || (pRoot->left == nullptr && pRoot->right == nullptr))
        return;

    stack<TreeNode*> stackNodes;
    stackNodes.push(pRoot);

    while (stackNodes.size() > 0)
    {
        TreeNode *pNode = stackNodes.top();
        stackNodes.pop();
        TreeNode *pTemp = pNode->left;
        pNode->left = pNode->right;
        pNode->right = pTemp;

        if (pNode->left != nullptr)
            stackNodes.push(pNode->left);
        if (pNode->right != nullptr)
            stackNodes.push(pNode->right);
    }

}

面试题20:顺时针打印矩阵

考虑情况:数组的行列数(相等;行数大于列数;行数小于列数)

void PrintMatrixClockwisely(int** number,int columns,int rows){
    if (number==NULL || columns<=0 || rows<=0)
        return;
    int start=0;
    while (rows>start*2 && columns>start*2){
        PrintMatrixCircle(number,columns,rows,start);
        start++;
    }   
}
void PrintMatrixCircle(int** number,int columns,int rows,int start){
    int endY = columns-start-1;
    int endX = rows-start-1;
    //从左到右打印一行
    for (int i=start;i<=endY;++i){
        int num = number[start][i];
        printNumber(number);
    }
    //从上到下打印一行
    if (start<endX){
        for (int i=start+1;i<=endX;++i){
            int num = number[i][endY];
            printNumber(number);
        }
    }
    //从右到左打印一行
    if (start<endX && start<endY){
        for (int i=endY-1;i>=start;--i){
            int num = number[endX][i];
            printNumber(number);
        }
    }
    //从下到上打印一行
    if (start<endX && start<endY){
        for (int i=endX-1;i>=start+1;--i){
            int num = number[i][startY];
            printNumber(number);
        }
    }
}

2. 举例让抽象问题具体化

面试题21:包含min函数的栈(定义栈的数据结构,调用min,pop,push的时间复杂度均为O(1))

/*使用辅助栈存储栈每次进栈后对应的最小元素*/
//m_data是数据栈,m_min是辅助栈
template <typename T> void StachWithMin<T>::push(const T &value){
    m_data.push(value);
    if (m_min.size()==0 || value <m_min.top())
        m_min.push(value);
    else
        m_min.push(m_min.top());
}
template <typename T> void StachWithMin<T>::pop(){
    assert(m_data.size()>0 && m_min.size()>0)
    m_data.pop();
    m_min.pop();
}
template <typename T> const T& StachWithMin<T>::min() const{
    assert(m_data.size()>0 && m_min.size()>0)
    return m_min.top();
}

面试题22:栈的压入,弹出序列(给定两个序列,压入序列,判断另一个是否为弹出序列)

/*建立辅助栈,按第一个序列将数字压入栈,并按第二个序列一次从栈中弹出数字。若即将弹出的元素是栈顶元素,则直接弹出;否则把压入序列的数字进栈直到栈顶元素是要弹出的数字;若所有数字均已入栈还没找到要弹出的数字,那么该序列不可能是一个弹出序列。*/
bool IsPopOrder(const int *pPush,const int *pPop,int nLength){
    bool bPossible = false;
    if (pPush!=NULL && pPop!=NULL && nLength>0){
        const int* pPushNext = pPush;
        const int* pPopNext = pPop;
        stack<int> stackData;
        while (pPopNext-pPop<nLength){
            while (stackData.empty() || stackData.top()!=*pPopNext){
                if (pPushNext-pPush)<nLength)
                    break;
                stackData.push(*pPushNext++);
            }
            if (stackData.top()!=*pPopNext)
                break;
            stackData.pop();
            pPopNext++;
        }
        if (stackData.empty() && pPopNext-pPop==nLength)
            bPossible = true;
    }
    return bPossible;
}

面试题23:从上往下打印二叉树(层序遍历,广度优先遍历)

struct BinaryTreeNode{
    int m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};
/*从根结点开始,若打印的结点有子结点,将子结点压入队尾;接下来打印队首结点元素,重复前面,直到队列为空。*/
void PrintFromTopToBottom(BinaryTreeNode* pTreeRoot){
    if (pTreeRoot==NULL)
        return;
    deque<BinaryTreeNode*} dequeTreeNode;
    dequeTreeNode.push(pTreeRoot);
    while (!dequeTreeNode.empty()){
        BinaryTreeNode* pNode = dequeTreeNode.front();
        dequeTreeNode.pop_front();
        printf("%d ",pNode->m_nValue);
        if (pNode->m_pLeft!=NULL)
            dequeTreeNode.push_back(pNode->m_pLeft);
        if (pNode->m_pRight!=NULL)
            dequeTreeNode.push_back(pNode->m_pRight);
    }
}

举一反三:广度优先遍历有向图或树,利用队列:把起始结点(树的根结点)放入队列。接下来每一次从队列的头部取出一个结点,遍历这个结点能达到的结点(树中的子结点)都依次放入队列。重复上述过程,直到队列为空。

面试题24:判断数组是否是二叉搜索树的后序遍历序列

/*后序遍历的最后一个元素为根结点,前面的数字应该可以分为两部分:小于根结点(左子树),大于根结点(右子树);再递归判断两个子数组即可*/
bool VerifySquenceofBST(int sequence[],int lenght){
    if (sequence==NULL || length<=0)
        return false;
    int root = squence[length-1];
    //搜索左子树:在二叉搜索树中左子树结点小于根结点
    int i=0;
    for (;i<length-1;++i){
        if (squence[i]>root)
            break;
    }
    //搜索右子树:在二叉搜索树中右子树结点大于根结点
    int j=i;
    for (;j<length-1;++j){
        if (squence[i]<root)
            return false;
    }
    //判断左子树是不是二叉搜索树
    bool left = true;
    if (i>0)
        left = VerifySquenceofBST(sequence,i);
    //判断右子树是不是二叉搜索树
    bool right = true;
    if (i>0)
        right = VerifySquenceofBST(sequence+i,length-1-i);
    return (left && right);
}

面试题25:打印二叉树中和为某一值的所有路径(从根结点到叶子结点经过的结点形成一条路径)

/*采用前序遍历和栈:将遍历到结点压入栈,并累加值得和;若当前结点为叶子结点且累加和等于给定值,则打印出来;若当前结点不是叶子结点,继续访问其子结点;当前结点访问结束后,回到其父结点并从栈中弹出当前结点并减去其值*/
struct BinaryTreeNode{
    int m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};
void FindPath(BinaryTreeNode *pRoot,int expectedSum){
    if (pRoot==NULL)
        return;
    vector<int> path;
    int currentSum = 0;
    FindPath(pRoot,expectedSum,path,currentSum);
}
void FindPath(BinaryTreeNode *pRoot,int expectedSum,vect<int> & path,int currentSum){
    currentSum += pRoot->m-nValue;  
    path.push_back(pRoot->m-nValue);
    bool isLeafNode = (pRoot->m_pLeft==NULL) && (pRoot->m_pRight==NULL);
    //若当前结点为叶子结点且累加和等于给定值,则打印出来
    if (currentSum == exceptedSum && isLeaf){
        printf("A Path is found: ");
        vector<int>::iterator iter = path.begin();
        for (;iter!=path.end();++iter)
            printf("%d\t",*iter);
        printf("\n");
    }
    //若当前结点不是叶子结点,则继续访问其子结点
    if (pRoot->m_pLeft!=NULL)
        FindPath(pRoot->m_pLeft,exceptedSum,path,currentSum);
    if (pRoot->m_pRight!=NULL)
        FindPath(pRoot->m_pRight,exceptedSum,path,currentSum);
    //在返回父结点前,在路径上删除当前结点
    path.pop_back();
    currentSum -= pRoot->m-nValue;
}

3. 分解让复杂问题简单化(分治法)

面试题26:复杂链表的复制

struct ComplexListNode{
    int m_nValue;
    ComplexListNode *m_pnext;//下一个结点
    ComplexListNode *m_pSibling;//兄弟    
}
//第一步:复制原始链表的每个结点N创建新结点N'链接到N的后面
void CloneNodes(ComplexListNode *pHead){
    ComplexListNode *pNode = pHead;
    while (pHead!=NULL){
        ComplexListNode *pCloned = new ComplexListNode();
        pClone->m_nValue = pNode->m_nValue;
        pClone->m_pNext = pNode->m_pNext;
        pClone->m_pSibling = NULL;
        pNode->m_pNext = pCloned;
        pNode = pNode->m_pNext;     
    }
}
//第二步:如果原始链表的结点N的m_pSibling指向结点S,则它对应的复制结点N'的m_pSibling指向结点S'
void ConnectSiblingNodes(ComplexListNode *pHead){
    ComplexListNode *pNode = pHead;
    while (pNode!=NULL){
        ComplexListNode *pCloned = pNode->m_pNext;  
        if (pNode->m_pSibling!=NULL){
            pCloned->m_pSibling = p->m_pSibling->m_pNext;
        }
        pNode = pClone->m_pNext;
    }
}
//第三步:将第二步得到的链表拆分为两个链表:奇数位置上的结点组成原始链表,偶数位置上的结点组成复制链表
ComplexListNode * ReConnectNodes(ComplexListNode* phead){
    ComplexListNode *pNode = pHead;
    ComplexListNode *pClonedHead = NULL;
    ComplexListNode *pClonedNode = NULL;
    if (pNode!=NULL){
        pClonedHead = pClonedNode = pNode->m_pNext;
        pNode->m_pNext = pClonedNode->m_pNext;
        pNode = pNode->m_pNext;
    }
    while (pNode!=NULL){
        pClonedNode->m_pNext = pNode->m_pNext;
        pClonedNode = pClonedNode->m_pNext;
        pNode->m_pNext = pClonedNode->m_pNext;
        pNode = pNode->m_pNext;
    }
}
//完整过程
ComplexListNode * Clone(ComplexListNode * pHead){
    CloneNodes(pHead);
    ConnectSiblingNodes(pHead);
    return ReConnectNodes(phead);
}

面试题27:二叉搜索树调整指针指向构成排序的双向链表

struct BinaryTreeNode{
    int m_nValue;
    BinaryTreeNode * m_pLeft;
    BinaryTreeNode * m_pRight;
};
BinaryTreeNode * Convert(BinaryTreeNode * pRootOfTree){
    BinaryTreeNode * pLastNodeInList = NULL;
    ConvertNode(pRootOfTree,&pLastNodeInList);
    BinaryTreeNode * pHeadOfList = pLastNodeInList;
    while (pHeadOfList!=NULL && pHeadOfList->m_pLeft!=NULL)
        pHeadOfList = pHeadOfList->m_pLeft;
    return pHeadOfList;
}
void ConvertNode(BinaryTreeNode * pNode,BinaryTreeNode **pLastNodeInList){
    if (pNode==NULL)
        return;
    BinaryTreeNode * pCurrent = pNode;
    if (pCurrent->m_pLeft!=NULL)
        ConvertNode(pCurrent->m_pLeft,pLastNodeInList);
    pCurrent->m_pLeft = *pLastNodeInList;
    if (*pLastNodeInList!=NULL)
        (*pLastNodeInList)->m_pRight = pCurrent;
    *pLastNodeInList = pCurrent;    
    if (pCurrent->m_pRight!=NULL)
        ConvertNode(pCurrent->m_pRight,pLastNodeInList);
}

面试题28:字符串的排列

/*将字符串分为两部分:第一个字符与剩余字符串,再求其他字符串的排列;逐个交换第一个字符与后面的字符*/
void Permutation(char* pStr){
    if (pStr==NULL)
        return;
    Permutation(pStr,pStr);
}
void Permutation(char* pStr,char* pBegin){
    if (*pBegin=='\0')
        printf("%s\n",pStr);
    else{
        for (char *pCh = pBegin;*pCh!='\0';++pCh){
            char temp = *pCh;
            *pCh = *pBegin;
            *pBegin = temp;
            Permutation(pStr,pBegin+1);
            temp = *pCh;
            *pCh = *pBegin;
            *pBegin = temp;
        }
    }
}
//abc acb bac bca cba cab

举一反三:如果面试题是按照一定要求摆放若干个数字,可以先求出这些数的所有排列,再一一判断哪些排列符合要求(比如:象棋的摆放)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值