剑指offer刷题详细分析:part12:56题——60题

本文详细分析了剑指Offer中的五道题目,包括删除链表中重复结点、二叉树的下一个结点、对称的二叉树、按之字形顺序打印二叉树以及把二叉树打印成多行的方法。通过递归和特殊顺序遍历等技巧解决这些问题。
摘要由CSDN通过智能技术生成
  • 剑指offer所有题目详解,可访问我的github项目:KongJetLin-offer

  • 目录

  1. Number56:删除链表中重复结点
  2. Number57:二叉树的下一个结点
  3. Number58:对称的二叉树
  4. Number59:按之字形顺序打印二叉树
  5. Number60:把二叉树打印成为多行

题目56 删除链表中重复结点

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

//删除以 pHead 为根结点的
    public ListNode deleteDuplication(ListNode pHead)
    {
        /*
        1)当链表第一个结点为null,直接返回这个结点
        2)当遍历到链表的最后一个结点,下一个结点为null,此时不会有与他相同的下一个结点,返回这个最后的结点
        注意,必须要判断 pHead.next 不为null,这是下面操作的前提!!
         */

        if(pHead == null || pHead.next == null)
            return pHead;

        //当 pHead.next 存在的时候,将其取出进行判断(这里 pHead.next存在 是下面操作的前提)
        ListNode nextNode = pHead.next;

        //当 pHead.val == nextNode.val 的时候,我们需要删除pHead结点以及后面值与他相同的结点
        if(pHead.val == nextNode.val)//nextNode不存在的时候,这里会报空指针异常,因此前面必须要排除 nextNode不存在的情况
        {
            //如果 pHead.val == nextNode.val,我们持续将 nextNode 的指针后移,直到 nextNode=null 或者 pHead.val != nextNode.val
            while(nextNode != null && pHead.val == nextNode.val)
            {
                nextNode = nextNode.next;
            }
            //注意,由于 pHead 的值与nextNode 的值相同,pHead 也必须删除
            //当 nextNode=null 的时候,deleteDuplication(pHead) 返回 null,正确;
            //当 nextNode!=null 的时候,deleteDuplication(pHead) 返回以nextNode为头结点的删除了重复元素的链表,正确
            return deleteDuplication(nextNode);//这样返回就将 pHead 也删除了
        }
        else
        {
            //当 pHead.val != nextNode.val 的时候,我们不需要删除pHead,删除以 pHead.next为根的链表的重复结点并返回
            pHead.next = deleteDuplication(pHead.next);//注意将pHead连接到返回的链表上
            return pHead;
        }
    }

题目57 二叉树的下一个结点

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

  分析:结合图,我们可发现分成两大类:
1)有右子树的,那么下个结点就是右子树最左边的点(eg:D,B,E,A,C,G);
2)没有右子树的,也可以分成两类:
  a)是父节点左孩子(eg:N,I,L) ,那么父节点就是下一个节点 ;
  b)是父节点的右孩子(eg:H,J,K,M),找他的父节点的父节点的父节点…直到当前结点是其父节点的左孩子位置,那么当前结点的父结点就是我们要找的当前结点的下一个结点;如果遍历到根结点都没有找到,那么说明当前结点已经是最后一个结点。
  为什么是这种规律?根据中序遍历特性,左父结点一定已经被中序遍历访问过,所以下一个结点一定是在父节点路径上的第一个右父节点。停车不管是情况a还是情况b,我们都需要向上,直到找到当前结点的父节点在右边,也就是说当前结点是其父节点的左孩子结点,那么其父节点就是我们想要寻找的下一个结点。
在这里插入图片描述
  参考:添加链接描述

  代码如下:

public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        //首先,如果pNode=null,说明当前结点不存在,那么下一个结点肯定也不存在,直接返回null
        if(pNode == null)
            return null;

        //第一种情况,pNode存在右子树,找到右子树最左边的结点
        if(pNode.right != null)
        {
            pNode = pNode.right;//将指针移动到右子树根结点
            if(pNode.left != null)
                pNode = pNode.left;//一直将指针移动到当前结点的左孩子结点,直到左孩子结点不存在,那么当前结点就是下一个结点(右子树的最左结点)
            return pNode;
        }

        //第二种情况,pNode不存在右子树,那么一直找他的父节点,直到当前结点是其父结点的左孩子结点(父节点是当前结点的右父节点)
        //如果父亲结点存在,一直寻找符合条件的父亲结点
        while(pNode.next != null)
        {
            if(pNode.next.left == pNode)
                return pNode.next;//找到“右父节点”,那么这个“右父节点”就是下一个结点
            pNode = pNode.next;//没有找到“右父节点”,就一直将指针移动到父节点向上寻找
        }
        //如果遍历到父亲结点不存在还是没有满足条件的结点,说明pNode是最后一个结点你,直接返回null
        return null;
    }

题目58 对称的二叉树

  题目描述:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
  分析:首先分析下这个对称二叉树,也就是一个二叉树中间对称。所以我们可以使用递归的思想,首先以根节点以及其左右子树左子树的左子树和右子树的右子树相同左子树的右子树和右子树的左子树相同。两个条件都要符合,所以我们第一个传根节点的左子树和右子树,先判断左右子树根结点的比较。然后分辨对左子树的左子树和右子树的右子树。左子树的右子树和右子树的左子树进行判断。只有两个条件都满足则返回的是true,一层一层递归进入,则可以得到结果。

boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot == null)
            return true;//如果根结点为null,表示二叉树是对称的,返回true

        return isSymmetrical(pRoot.left , pRoot.right);
    }

    //用于比较 以node1为根的左子树 与 以node2位根的右子树 的对应位置的结点是否全部相等,返回比较的Boolean结果
    private boolean isSymmetrical(TreeNode node1 , TreeNode node2)
    {
        //---------------------------------------首先比较2个子树的根结点
        //如果node1与node2都是null,说明到了二叉树的末尾,此时应该返回true
        if(node1 == null && node2 == null)
            return true;
        //如果node1与node2不都是null,但是node1或者node2有一个是null,此时说明二叉树不是对称的
        if(node1 == null || node2 == null)//前面已经把node1与node2都是null的情况排除,现在这里只有可能出现 node1或者node2有一个是null
            return false;
        //如果 node1与node2都存在,且他们的值不相等,二叉树不对称,返回false
        if(node1.val != node2.val)
            return false;

        //-----------------------------------------其次比较2个子树的子树对应位置的结点是否相等
        //如果2个结点相等,且还没有到达二叉树末尾,那么比较 以node1的左孩子结点为根的树 与 以node2右孩子结点为根的树 是否相等,
        //以及 以node1右孩子结点为根的树 与 以node2的左孩子结点为根的树 是否相等,只有他们全部相等才会返回true(这部分画个图就知道了)
        return isSymmetrical(node1.left , node2.right) && isSymmetrical(node1.right , node2.left);
    }

题目59 按之字形顺序打印二叉树

  题目描述:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
  分析:这题类似 60 题,不管这题需要按照不同的行采取从左到右或者从右到左的打印方式。相对于60题,我们添加一个变量来确定这一行的打印方式,使用 Collections.reverse 方法来翻转存储某一行元素的 ArrayList。
  代码如下:

public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
    {
        ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();
        //注意,这一题如果pRoot为null的时候,我们直接返回arrayList即可
        //否则因为根结点为空,queue.add(pRoot) 的时候回出现空指针异常
        if(pRoot == null)
            return arrayLists;
        //创建一个队列用于存储结点
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(pRoot);//先将根结点存储进来
        boolean reverse = false;//创建一个变量判断反转,第一行从左到右不需要反转,设置初始值为false

        while(!queue.isEmpty())
        {
            ArrayList<Integer> temp = new ArrayList<>();//用于存储某一行结点值的临时ArrayList
            //用一个变量记录队列保存的上一层的存储到队列中的结点值的数量
            //这里必须将上一层存储的结点数量先取出来,不能一边减少一边取,因为我们在取出的上一层结点值同时,这一层的结点值也在存储进去
            int count = queue.size();
            while (count>0)
            {
                TreeNode node = queue.remove();
                temp.add(node.val);
                count--;

                //取出上一层的某一个结点后,如果这个结点右左右孩子结点,这些孩子结点属于这一层,将他们存储到队列中
                if(node.left != null)
                    queue.add(node.left);
                if(node.right != null)
                    queue.add(node.right);
            }
            if(reverse)
                Collections.reverse(temp);
            reverse = !reverse;//将标志反转

            arrayLists.add(temp);
        }
        return arrayLists;
    }

题目60 把二叉树打印成为多行

  题目描述:从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
  注意区分于第22题:从上往下打印二叉树,第22题的题目要求是:从上往下打印出二叉树的每个节点,同层节点从左至右打印。这里虽然也是从上往下打印二叉树每个节点,队首要求每一层节点输出为一行,也就是说,我们必须把在同一层的结点打印完后,再打印其他层的结点。其实方法与22题差不多,如下:

/**这一题注意对比第22题的解法*/
    ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
    {
        ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();

        //注意,这一题如果pRoot为null的时候,我们直接返回arrayList即可
        //否则因为根结点为空,queue.add(pRoot) 的时候回出现空指针异常
        if(pRoot == null)
            return arrayLists;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(pRoot);
        while(!queue.isEmpty())
        {
            //用一个变量记录队列保存的上一层的结点的数量
            int count = queue.size();
            ArrayList<Integer> temp = new ArrayList<>();
            //把上一层放入队列的结点的值全部取出放入ArrayList,同时将这些结点的左右孩子结点放入队列
            while(count>0)
            {
                TreeNode cur = queue.remove();
                temp.add(cur.val);
                count--;//每取出一个count-1,这样就能保证不打印这一层放入的新的结点

                //将队列取出的结点的孩子结点放入队列,这些结点是下一层的结点,用于下一层的打印
                if(cur.left != null)
                    queue.add(cur.left);
                if(cur.right != null)
                    queue.add(cur.right);
            }
            //将添加了这一层所有结点的ArrayList放入最大的ArrayList
            arrayLists.add(temp);
        }
        //添加完所有ArrayList以后,将最大的ArrayList返回
        return arrayLists;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值