《剑指offer》刷题打卡第9天

面试题26:二叉搜索树与双向链表
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。比如,输入下图左边的二叉搜索树,则输出转换之后的排序双向链表。二叉树节点的定义如下:
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
在这里插入图片描述
思路:
在二叉树中,每个节点都有两个指向子节点的指针。在双向链表中,每个节点也有两个指针,分别指向前一个节点和后一个节点。在搜索二叉树中,左子节点的值总是小于父节点的值,右子节点总是大于父节点的值。因此,我们在将二叉搜索树转换成排序双向链表时,原先指向左子节点的指针调整为链表中指向前一个节点的指针,原先指向右子节点的指针调整为链表中指向后一个节点的指针。
由于要求转换之后的链表是排好序的,我们可以中序遍历树中的每个节点。这是因为中序遍历算法的特点是按照从小到大的顺序遍历二叉树的每个节点。当遍历到根节点的时候,我们把树看成三部分:值为10的节点;根节点值为6的左子树;根节点值为14的右子树。根据排序链表的定义,值为10的节点将和它的左子树的最大一个节点(值为8的节点)链接起来,同时它还将和右子树最小的节点(值为12的节点)链接起来,如下图所示。

在这里插入图片描述
注:根节点、左子树和右子树。在把左、右子树都转换成排序双向链表之后再和根节点链接起来,整棵二叉搜索树也就转换成了排序双向链表。
按照中序遍历的顺序,当我们遍历转换到根节点(值为10的节点)时,它的左子树已经转换成一个排序的链表了,并且处在链表中的最后一个节点是当前值的最大节点。我们把值为8的节点和根节点连接起来,此时链表中最后一个节点就是10了。接着我们去遍历转换右子树,并把根节点和右子树中的最小的节点链接起来,可以采用递归的方式去转换根节点的左右子树。
代码实现:

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        
        TreeNode *pLastNodeInList = nullptr;
        ConvertNode(pRootOfTree,&pLastNodeInList);
        
        //pLastNodeInList指向双向链表的尾节点,
        //我们需要返回头节点
        TreeNode* pHeadOfList = pLastNodeInList;
        while(pHeadOfList != nullptr && pHeadOfList -> left != nullptr)
            pHeadOfList = pHeadOfList -> left;
        return pHeadOfList;
    }
    void ConvertNode(TreeNode* pNode,TreeNode** pLastNodeInList)
    {
        if(pNode == nullptr)
            return;
        TreeNode *pCurrent = pNode;
        if(pCurrent -> left != nullptr)
            ConvertNode(pCurrent -> left,pLastNodeInList);
        pCurrent -> left = *pLastNodeInList;
        if(*pLastNodeInList != nullptr)
            (*pLastNodeInList) -> right = pCurrent;
        *pLastNodeInList = pCurrent;
        if(pCurrent -> right != nullptr)
            ConvertNode(pCurrent -> right,pLastNodeInList);
    }
};

面试题27:字符串的排列
题目:输入一个字符串,打印出该字符串的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符abc、acb、bca、cab和cba。

思路:
求整个字符串的排列,可以看成两部。第一步:求所有可能出现在第一个位置的字符,即把第一个字符和后面所有的字符交换。第二步:固定一个字符,求后面所有字符的排列。这时候仍把后面的所有字符分成两部分:后面字符的第一个字符,以及这个字符之后的所有字符。然后把第一个字符逐一和它后面的字符交换。
在这里插入图片描述
代码实现:

class Solution {
public:
    vector<string> Permutation(string str) {
        vector<string> str1;
        if(str.size() != 0)
        {
            int nSize = str.size();
            Permutation(str,nSize,0);
        }
        for(set<string>::iterator iter=
            permutationSet.begin();
            iter != permutationSet.end();
            ++iter)
        {
        permutation.push_back(*iter);    
        }
        return permutation;
    }
    void Permutation(string str,int nSize,int n)
    {
        if(n == str.size())
        {
            permutationSet.insert(str);
        }
        else{
            Permutation(str,nSize,n+1);
                for(int i = n+1;i < str.size();++i)
                {
                    if(str[n] != str[i])
                    {
                        char tmp = str[n];
                        str[n] = str[i];
                        str[i] = tmp;
                        
                        Permutation(str,nSize,n+1);
                        tmp = str[n];
                        str[n] = str[i];
                        str[i] = tmp;
                    }
                }
        }
    }
    public:
    vector<string>permutation;
    set<string>permutationSet;
};

猜你喜欢

转载自blog.csdn.net/tpf930726/article/details/89246688