每日做题之剑指offer(十)

1.删除链表中重复的结点

时间限制:1秒 空间限制:32768K 热度指数:192077

本题知识点: 链表

题目描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

解题思路:

  • 以题目,给的例子来说明,当我们遍历到第一个值为3的节点的时候,pPre指向2,因为接下来的结点还是3,所以这两个结点应该被删除
  • 所以pPre应该和第一个值为4的结点相连,继续向后遍历,发现值为4的结点,所以pPre最终和值为5的结点连在一起
  • 当然了头结点也可能和后面的结点重复,所以删除函数的指针应该为二级指针
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(NULL == pHead)
            return NULL;
        if(NULL == pHead->next)
            return pHead;
        
        ListNode* pCur = pHead;
        if(pCur && pCur->val != pCur->next->val)
        {
            pCur->next = deleteDuplication(pCur->next);
            return pHead;
        }
        else{
            pCur = pCur->next->next;
            while(pCur && pHead->val == pCur->val)
            {
                pCur = pCur->next;
            }
        }
        return deleteDuplication(pCur);
    }
};

2.二叉树的下一个结点

时间限制:1秒 空间限制:32768K 热度指数:97627

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

解题思路:{d,b,h,e,j,a,f,c,g,}

  • 因为是要找中序遍历中的下一个结点。所以如果一个结点有右子树,那么它的下一个结点就是它的右子树中的最左子节点
  • 也就是说,从右子节点触发一直沿着指向左子结点的指针,就可以找到它的下一个结点
  • 那么如果树没有右子树呢?如果结点是它父节点的左子节点,那么它的下一个结点就是它的父节点
  • 如果一个结点既没有右子树,而且还是它父节点的右子结点,我们可以沿着指向父节点的指针一直向上遍历,直到找到一个是它父节点的左子节点的结点。那么这个节点的父节点就是我们要找的下一个结点。以图中的j结点为例,先到达e,e是她的父节点的右子结点,再继续往上,找到了b, b是它的父节点的左子节点,因此a就是j的下一个结点
  • 结点g的下一个节点的步骤类似
/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(NULL == pNode)
            return NULL;
        TreeLinkNode* pNext = NULL;
        if(pNode->right)
        {
            TreeLinkNode* pRight = pNode->right;
            while(pRight->left)
                pRight = pRight->left;
            
            pNext = pRight;
        }
        else if(pNode->next)
        {
            TreeLinkNode* pCur =  pNode;
            TreeLinkNode* pParent = pNode->next;
            while(pParent && pCur == pParent->right)
            {
                pCur = pParent;
                pParent = pParent->next;
            }
            pNext = pParent;
        }
        return pNext;
    }
};

3.对称的二叉树

时间限制:1秒 空间限制:32768K 热度指数:92245

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

解题思路:

  • 以上述三棵树为例。第一棵树的前序遍历结果为{8,6,5,7,6,7,5},既然是对称的,那么我们是否可以根据前序遍历自己定义一种遍历算法呢? 前中后序遍历都是先左子树后右子树,那么我们可以先右子树后左子树,当然了是基于前序遍历的先右后左,如果树是对称的它得到的序列应该和前序遍历的结果一样,结果为{8,6,5,7,6,7,5},所以第一棵树是对称的二叉树
  • 按照上述方法遍历第二颗树,前序遍历结果为{8,6,5,7,9,7,5},自己定义的结果为{8,9,5,7,5,7,5},不一样,不是对称二叉树
  • 第三种树因为结点上的值都一样,前序遍历和对称遍历序列是一样的,无法区分到底是不是对称二叉树,我们可以将NULL指针也算进来。前序遍历{7,7,7,NULL,NULL,7,NULL,NULL,7,7,NULL,NULL,NULL};对称遍历序列为{7,7,NULL,7,NULL,NULL,7,7,NULL,NULL,7,NULL,NULL},不一样所以不是对称二叉树
/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot)
    {
          return isSymmetrical(pRoot,pRoot);
    }
    bool isSymmetrical(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(NULL == pRoot1 && NULL == pRoot2)
            return true;
        
       if(NULL == pRoot1 || NULL == pRoot2)
            return false;
        
        if(pRoot1->val != pRoot2->val)
            return false;
        
        return isSymmetrical(pRoot1->left, pRoot2->right) && isSymmetrical(pRoot1->right, pRoot2->left);
    }
    

};

4.按之字形顺序打印二叉树

时间限制:1秒 空间限制:32768K 热度指数:104701

题目描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

解题思路:

  • 打印根节点之后,它的左右子节点要按照从右往左的顺序打印,那么可以借助栈,2先入栈,3后入栈,所以3就可以先打印了,即从右往左打印第二层
  • 接下来就是打印第三层,是从左往右打印,先打印2的两个节点4和5,后打印结点3的两个节点6和7,我们可以仍然借助栈来进行,不过这个栈是另外一个栈,不能和2和3的栈共用一个:因为3出栈后,就要让它的两个子节点入栈,那么栈顶就不是2了,打印出错就会
  • 第二个栈要注意,要先入右结点,再是左子节点,这样才是从左往右打印
  • 总结:需要两个栈。当前打印的是奇数层的话,先让左节点入栈,右节点后入栈;当前打印的是偶数层的话,先保存右结点后保存左结点
void Print(TreeNode* pRoot)
{
    if(NULL == pRoot)
        return NULL;

    std::stack<TreeNode*> levels[2]; //定义了两个栈
    int current = 0;
    int next = 1;

    levels[current].push(pRoot);
    while( !levels[0].empty() || !levels[1].empty() )
    {
        TreeNode* pCur = levels[current].top();
        levels[current].pop();

        printf("%d", pCur->val);

        if(current == 0)
        {
            if(pCur->left)
                levels[next].push(pCur->left);
            if(pCur->right)
                levels[next].push(pCur->right);
        }
        else
        {
            if(pCur->right)
                levels[next].push(pCur->right);
            if(pCur->left)
                levels[next].push(pCur->left);
        }
        if(levels[current].emoty())
        {
            printf("\n");
            current = 1 - current;
            next = 1 - next;
        }
    }
}

5.把二叉树打印成多行

时间限制:1秒 空间限制:32768K 热度指数:86349

题目描述从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

解题思路:

  • 该题和从上打印二叉树类似,也是使用一个队列来保存将要打印的结点
  • 但是为了把二叉树的每一行单独打印到一行里,我们需要两个变量
  • 一个是表示当前层中还没有打印的节点数
  • 另一个变量是表示下一层节点的数目
  void Print(TreeNode* pRoot) {
            if(NULL == pRoot)
                return {};
              
            std::queue<TreeNode*> nodes;
            nodes.push(pRoot);
            
            int nextLevel = 0; //下一层的结点数目,如果一个结点有子节点,每一个子节点入队列的时候,nextLevel加1
            int toBePrint = 1; //当前层所剩的没有被打印的节点个数
            
            while(! nodes.empty())
            {
                TreeNode* pCur = nodes.front();
                printf("%d", pCur->val);
                
                if(pCur->left)
                {
                    nodes.push(pCur->left);
                    ++nextLevel;
                }
                if(pCur->right)
                {
                    nodes.push(pCur->right);
                    ++nextLevel;
                }
                
                nodes.pop();
                --toBePrint;
                
                if(toBePrint == 0)
                {
                    printf("\n");
                    toBePrint = nextLevel;
                    nextLevel = 0;
                }
            }        
        }  

6.序列化二叉树

时间限制:1秒 空间限制:32768K 热度指数:103816

题目描述请实现两个函数,分别用来序列化和反序列化二叉树

解题思路:

  • 二叉树的序列从根结点开始,那么我们可以选择前序遍历的顺序来序列化二叉树,在遍历二叉树的时候遇到NULL指针,将这些NULL指针序列化为特殊的字符(如$),另外节点之间用特殊符号隔开(如 ,)
  • “1,2,4,$,$,$,3,5,$,$,6,$,$”,分析反序列化二叉树。第一个读出的数字是1.由于前序遍历是从根节点开始的,这是根节点的值。
  • 接下来读出的数字是2,根据前序遍历的规则,这是左子节点的值。同样,4是值为2的结点的左子节点。然后从序列化的粽子反复穿里面读出两个字符'$',这表明值为4的节点的左右结点均为NULL指针,因此4是一个叶子结点
  • 接下来回到值为2的结点,重建它的右子节点,下一个字符是'$',表明值为2的结点的右子节点为NULL指针。这个节点的左右结点都已经构建完毕
  • 接下来回到根节点,反序列化根节点的右子树
  • 方法和重建左子树一样
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Serialize(self, root):
        # write code here
        if root == None:
            return '$'
        return str(root.val) + ',' + self.Serialize(root.left) + "," + self.Serialize(root.right)
    
    def Deserialize(self, s):
        # write code here
        root, index = self.deserialize(s.split(","), 0)
        return root
    
    def deserialize(self, s, index):
        if s[index]=='$':
            return None,index+1
        
        root=TreeNode(int(s[index]))
        index += 1
        root.left, index = self.deserialize(s, index)
        root.right, index = self.deserialize(s, index)
        return root, index

猜你喜欢

转载自blog.csdn.net/ijn842/article/details/81784033