剑指offer算法题分析与整理(四)

下面整理一下我在刷剑指offer时,自己做的和网上大神做的各种思路与答案,自己的代码是思路一,保证可以通过,网友的代码提供出处链接。

目录

1、合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路一:非递归

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)return list2;
        if(list2==null)return list1;

        ListNode t=new ListNode(520);//随便哪个值都一样,反正只返回它的next
        ListNode list=t;

        while(list1!=null&&list2!=null){
            if(list1.val<=list2.val){
                t.next=list1;
                t=t.next;
                list1=list1.next;
            }
            else{
                t.next=list2;
                t=t.next;
                list2=list2.next;
            }
       }
       if(list1==null)t.next=list2;
       if(list2==null)t.next=list1;        
       return list.next;
    }
}

思路二:递归

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)return list2;
        if(list2==null)return list1;
        ListNode t=null;
        if(list1.val<list2.val){
            t=list1;
            t.next=Merge(list1.next,list2);            
        }
        else{
            t=list2;
            t.next=Merge(list1,list2.next);           
        }     
        return t;
    }
}

2、二叉搜索树转成双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路一:递归,FL函数返回一个树转化为双向链表后的头节点和尾节点,对根节点的左右两个子树分别递归,然后将递归得到的左右两段与中间的根节点建立双向联系,拼起来。

/** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
       if(pRootOfTree==null)return null;
       TreeNode[] A=FL(pRootOfTree);
       return A[0];
    }

    TreeNode[] FL(TreeNode p){
        TreeNode[] t=new TreeNode[2];//左边放头节点,右边放尾节点
        if(p.left==null&&p.right==null){
            t[0]=p;
            t[1]=p;
        }
        else if(p.right==null){
            TreeNode[] t1=FL(p.left);
            p.left=t1[1];
            p.left.right=p;
            t[0]=t1[0];
            t[1]=p;
        }
        else if(p.left==null){
            TreeNode[] t1=FL(p.right);
            p.right=t1[0];
            p.right.left=p;
            t[0]=p;
            t[1]=t1[1];
        }
        else{
            TreeNode[] t1=FL(p.left);
            TreeNode[] t2=FL(p.right);
            p.left=t1[1];
            p.left.right=p;
            p.right=t2[0];
            p.right.left=p;
            t[0]=t1[0];
            t[1]=t2[1];            
        }
        return t;        
    }
}

用递归来中序遍历,用pre来保存前一个节点

public class Solution{
    TreeNode head=null;
    TreeNode pre=null;
    public TreeNode Convert(TreeNode root) {        
         f(root);
         return head;
    }

     void f(TreeNode t){
         if(t==null)return;
         f(t.left);
         if(pre!=null){
             pre.right=t;
             t.left=pre;
         }
         else{
             head=t;
         }
         pre=t;
         f(t.right);         
     }     
}
/** //请注意,这样为什么就错了 public class Solution { TreeNode head = null; public TreeNode Convert(TreeNode root) { TreeNode pre = null; f(root, pre); return head; } void f(TreeNode t, TreeNode pre) { if (t == null) return; f(t.left, pre); if (pre != null) { pre.right = t; t.left = pre; } else { head = t; } pre = t; f(t.right, pre); } } */

思路二:非递归,中序遍历

import java.util.Stack;
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
       if(pRootOfTree==null)return null;

       Stack<TreeNode> s=new Stack<>();
       TreeNode t=pRootOfTree;
       TreeNode pre=null;
       TreeNode head=null;
       do{
           if(t!=null){
               s.push(t);
               t=t.left;
           }
           else{
               t=s.pop();

               if(pre==null){ //第一个节点 
                   pre=t;
                   head=pre; //头节点要保存下来 
               }
               else{  //将遍历到的节点与前一个节点建立双向联系 
                   pre.right=t;
                   t.left=pre;
                   pre=t; //更新pre 
               }

               t=t.right;
           }

       }while(t!=null||!s.isEmpty());
       return head;
    }
}

思路三:不论是用栈还是递归,都要使用O(n)的空间,Morris Traversal方法与前两种方法的不同在于该方法只需要O(1)空间,而且同样可以在O(n)时间内完成
来自牛客网@丁满历险记

public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        TreeNode p = pRootOfTree, pre = null, res = null;
        while (p != null) {//p是按中序遍历顺序,如果p为空说明整个树都遍历完了
            //p的左子树不为空,按中序遍历来说,左子树里面还有应该在p前面的点
            while (p.left != null) {
                //找到p的左子树里面最右的结点,这个点的右节点指向空,但这个节点是p的前驱,所以将空的右节点指向p
                TreeNode q = p.left;
                while (q.right != null) {
                    q = q.right;
                }
                q.right = p;
                //当我们可以从p的左子树里面通过上面建立的指针回到p点,p应该更新为p的左节点,然后旧的p点的左指针指向空
                TreeNode tmp = p.left;
                p.left = null;
                p = tmp;
            }
            //p的左节点为空,p就是当前要加入双向链表的点,将p点与pre建立双向联系
            p.left = pre;
            if (pre == null) {
                res = p;
            } else {
                pre.right = p;
            }
            pre = p;
            //p更新为p的右节点
            p = p.right;
        }
        return res;
    }
}

3、数组中第一个重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路一:用状态数组标记

 public boolean duplicate(int numbers[],int length,int [] duplication) {
       int[] count=new int[length];
        for(int i=0;i<length;i++){
            count[numbers[i]]++;            
        }
        for(int i=0;i<length;i++){
            if(count[numbers[i]]>1){
                duplication[0]=numbers[i];
            return true;
            }
        }
        return false;
    }
}

但是int数组占用空间有点大,考虑到题目只是找重复的数,而不关心重复的数到底重复了几次,因此可以用布尔数组,或者bit-map
来自牛客网@Aurora1

//碰到第一个重复的,输出,结束,后面的都不用看了
public boolean duplicate(int numbers[], int length, int[] duplication) {
        boolean[] k = new boolean[length];
        for (int i = 0; i < k.length; i++) {
            if (k[numbers[i]] == true) {
                duplication[0] = numbers[i];
                return true;
            }
            k[numbers[i]] = true;
        }
        return false;
    }

思路二:由于题目保证数组中的数不会超过length-1,因此就用这个数组本身当标记数组,怎么标记呢,把num[i]+=length,弊端就是:一、改变了原来的数组,如果输入的数组有误,里面有数本来就超过了length-1,就搞不清是自己加得还是本来就这么大;二、如果length太大,加几次搞不好要溢出Integer.MAX_VALUES。
来自牛客网@王大爷

bool duplicate(int numbers[], int length, int* duplication) {
    for(int i=0;i!=length;++i){
        int index=numbers[i]%length;//取余得到真面目
        if(numbers[index]>=length){//大于length-1说明重了
            *duplication=index;
            return 1;
        }              
        numbers[index]+=length;  
    }
    return 0;
}

思路三:交换来判断,它可以找到所有重复的数,但是:一、它改变了原数组;二、对于输入016645778,输出的是7而不是6,而第一个重复的数明明是6
来自牛客网@搬一块叫CV的砖

/* 1、判断输入数组有无元素非法 2、从头扫到尾,只要当前元素值与下标不同,就做一次判断,numbers[i]与numbers[numbers[i]],相等就认为找到了 重复元素,返回true,否则就交换两者,继续循环。直到最后还没找到认为没找到重复元素,返回false */
class Solution {
public:
    // Parameters:
    // numbers: an array of integers
    // length: the length of array numbers
    // duplication: (Output) the duplicated number in the array number
    // Return value: true if the input is valid, and there are some duplications in the array number
    // otherwise false
    bool duplicate(int numbers[], int length, int* duplication) {
        if(length<=0||numbers==NULL)
            return false;
        //判断每一个元素是否非法
        for(int i=0;i<length;++i)
        {
            if(numbers[i]<=0||numbers[i]>length-1)
                return false;
        }
        for(int i=0;i<length;++i)
        {
            while(numbers[i]!=i)
            {
                if(numbers[i]==numbers[numbers[i]])
                {
                    *duplication = numbers[i];
                    return true;
                }
                //交换numbers[i]和numbers[numbers[i]]
                int temp = numbers[i];
                numbers[i] = numbers[temp];
                numbers[temp] = temp;
            }
        }
        return false;        
    }
};

4、栈的压入弹出序列是否匹配

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路一:模拟栈的状态

import java.util.ArrayList;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        ArrayList<Integer> a=new ArrayList<>();
        //寻找第一个出栈的数字在入栈序列中的位置
        int index=-1;
        for(int m=0;m<pushA.length;m++){
            if(popA[0]==pushA[m]){
                index=m;
                break;
            }
        }
        if(index==-1)return false;//出栈的数字竟然不在入栈序列中?!
        //将a装成栈的状态
        int i=0;
        for(;i<index;i++){
            a.add(pushA[i]);
        }
        //对后面的每一个出栈的数字
        for(int j=1;j<popA.length;j++){
            //找到他在入栈序列中的位置
            index=-1;
            for(int m=0;m<pushA.length;m++){
                if(popA[j]==pushA[m]){
                    index=m;
                    break;
                }
            }
            if(index==-1)return false;//纳尼?!不存在?
            //位置靠后,没问题
            if(index>i){
                for(i++;i<index;i++){
                    a.add(pushA[i]);//更新栈的状态
                }
            }
            //是栈顶的数,没问题
            else if(popA[j]==a.get(a.size()-1)){
                a.remove(a.size()-1);
            }
            //那肯定是栈顶前面的数,不合法,因为它毕竟是栈,中间的数不可能跳出来
            else{
                return false;
            }
        }
        return true;
    }
}

同样是模拟栈的状态,大神的答案更加简洁精炼
来自牛客网@Alias

import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        if(pushA.length == 0 || popA.length == 0)
            return false;
        Stack<Integer> s = new Stack<Integer>();
        //用于标识弹出序列的位置
        int popIndex = 0;
        for(int i = 0; i< pushA.length;i++){
            s.push(pushA[i]);
            //如果栈不为空,且栈顶元素等于弹出序列
            while(!s.empty() &&s.peek() == popA[popIndex]){
                //出栈
                s.pop();
                //弹出序列向后一位
                popIndex++;
            }
        }
        return s.empty();
    }
}

5、数组中只出现一次的数

一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
思路一 O(n2) O ( n 2 ) 时间复杂度,不可取

import java.util.Iterator;
import java.util.LinkedList;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
           LinkedList<Integer> l=new LinkedList<>();
           boolean f;
           for(int i=0;i<array.length;i++){
               f=false;
               //前面存在相同的,就消掉,开心消消乐
               Iterator it=l.iterator();
               while(it.hasNext()){
                   if(it.next().equals(array[i])){
                       it.remove();
                       f=true;
                       break;
                   }
               }
               /**//foreach里面用remove要谨慎 for(Integer j:l){ if(j==array[i]){ l.remove(j); f=true; break; } }*/
               //没有相同的就加入链表
               if(!f)l.add(array[i]);
           }
           num1[0]=l.getFirst();
           num2[0]=l.getLast();
    }
}

需要关注ArrayList Remove方法遇到的坑

思路二:异或大法好
来自牛客网@高琥

/** * 数组中有两个出现一次的数字,其他数字都出现两次,找出这两个数字 * @param array * @param num1 * @param num2 */
    public static void findNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if(array == null || array.length <= 1){
            num1[0] = num2[0] = 0;
            return;
        }
        int len = array.length, index = 0, sum = 0;
        //DABCDC异或之后,等于AB异或的值
        for(int i = 0; i < len; i++){
            sum ^= array[i];
        }

        //AB异或一定不为0,那结果的二进制至少有一位为1,从右到左找出第一个为1的位的位置
        for(index = 0; index < 32; index++){
            if((sum & (1 << index)) != 0) break;
        }
        //以这个位置为划分标准划分两个数组,结果是,AB分别在不同数组,且每个数组剩余的数都是成对的
        for(int i = 0; i < len; i++){
            if((array[i] & (1 << index))!=0){
                num2[0] ^= array[i];
            }else{
                num1[0] ^= array[i];
            }
        }
        /**//来自牛客网@drdr int split = sum&~(sum - 1);//神了!!! for(int i = 0; i < len; i++){ if((array[i] & split)!=0){ num2[0] ^= array[i]; }else{ num1[0] ^= array[i]; } } */
   }
/** * 数组a中只有一个数出现一次,其他数都出现了2次,找出这个数字 * @param a * @return */
    public static int find1From2(int[] a){
        int len = a.length, res = 0;
        for(int i = 0; i < len; i++){
            res = res ^ a[i];
        }
        return res;
    }
/** * 数组a中只有一个数出现一次,其他数字都出现了3次,找出这个数字 * @param a * @return */
    public static int find1From3(int[] a){
        int[] bits = new int[32];
        int len = a.length;
        for(int i = 0; i < len; i++){
            for(int j = 0; j < 32; j++){
                bits[j] = bits[j] + ( (a[i]>>>j) & 1);
            }
        }
        int res = 0;
        for(int i = 0; i < 32; i++){
            if(bits[i] % 3 !=0){//不是3的整数倍的位,那个单独的数这个位一定为1
                res = res | (1 << i);
            }
        }
        return res;
    }

思路三:HashMap

6、斐波那契数列

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
思路一:单纯的递归,效率很低,有很多重复的计算,可能StackOverflow。

public class Solution {
    public int Fibonacci(int n) {
        if(n==0)return 0;//这两个if可以合并啊
        if(n==1)return 1;
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
}

尾递归,可避免溢出
来自牛客网@a00000000000

public class Solution {
    public int Fibonacci(int n) {
        return Fibonacci(n,0,1);
    }

    private static int Fibonacci(int n,int acc1,int acc2){
        if(n==0) return 0;
        if(n==1) return acc2;
        else     return Fibonacci(n - 1, acc2, acc1 + acc2);         
    }
}

思路二:迭代
来自牛客网@fanhk

class Solution {
public:
    int Fibonacci(int n) {
        int f = 0, g = 1;
        while(n-->0) {
            g += f;
            f = g - f;
        }
        return f;
    }
};

来自牛客网@楚云天

public class Solution {
    public int Fibonacci(int n) {
        int preNum=1;
        int prePreNum=0;
        int result=0;
        if(n==0)return 0;
        if(n==1)return 1;
        for(int i=2;i<=n;i++){
            result=preNum+prePreNum;
            prePreNum=preNum;
            preNum=result;
        }
        return result;
    }
}

思路三:快速幂, O(logn) O ( l o g n ) 的时间复杂度
来自牛客网@elseyu

/* * O(logN)解法:由f(n) = f(n-1) + f(n-2),可以知道 * [f(n),f(n-1)] = [f(n-1),f(n-2)] * {[1,1],[1,0]} * 所以最后化简为:[f(n),f(n-1)] = [1,1] * {[1,1],[1,0]}^(n-2) * 所以这里的核心是: * 1.矩阵的乘法 * 2.矩阵快速幂(因为如果不用快速幂的算法,时间复杂度也只能达到O(N)) */
public class Solution {
    public int Fibonacci(int n) {
        if (n < 1) {
            return 0;
        }
        if (n == 1 || n == 2) {
            return 1;
        }
        //底
        int[][] base = {{1,1},
                        {1,0}};
        //求底为base矩阵的n-2次幂
        int[][] res = matrixPower(base, n - 2);
        //根据[f(n),f(n-1)] = [1,1] * {[1,1],[1,0]}^(n-2),f(n)就是
        //1*res[0][0] + 1*res[1][0]
        return res[0][0] + res[1][0];
    }

    //矩阵乘法
    public int[][] multiMatrix(int[][] m1,int[][] m2) {
        //参数判断什么的就不给了,如果矩阵是n*m和m*p,那结果是n*p
        int[][] res = new int[m1.length][m2[0].length];
        for (int i = 0; i < m1.length; i++) {
            for (int j = 0; j < m2[0].length; j++) {
                for (int k = 0; k < m2.length; k++) {
                    res[i][j] += m1[i][k] * m2[k][j];
                }
            }
        }
        return res;
    }
    /* * 矩阵的快速幂: * 1.假如不是矩阵,叫你求m^n,如何做到O(logn)?答案就是整数的快速幂: * 假如不会溢出,如10^75,把75用用二进制表示:1001011,那么对应的就是: * 10^75 = 10^64*10^8*10^2*10 * 2.把整数换成矩阵,是一样的 */
    public int[][] matrixPower(int[][] m, int p) {
        int[][] res = new int[m.length][m[0].length];
        //先把res设为单位矩阵
        for (int i = 0; i < res.length; i++) {
            res[i][i] = 1;
        } //单位矩阵乘任意矩阵都为原来的矩阵
        //用来保存每次的平方
        int[][] tmp = m;
        //p每循环一次右移一位
        for ( ; p != 0; p >>= 1) {
            //如果该位不为零,应该乘
            if ((p&1) != 0) {
                res = multiMatrix(res, tmp);
            }
            //每次保存一下平方的结果
            tmp = multiMatrix(tmp, tmp);
        }
        return res;
    }     
}

7、和为S的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
思路:左右加逼大法

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> A=new ArrayList();        
        int len=array.length;
        if(len<2)return A;

        int small=-1,large=-1,M=Integer.MAX_VALUE;
        for(int m=0,n=len-1;m<n;){
            if(array[m]+array[n]<sum){m++;}
            else if(array[m]+array[n]>sum){n--;}
            else{//找到了,就筛选出乘积最小的
                if(m*n<M){
                    small=m;large=n;
                    M=m*n; 
                }
                m++;n--; //改变下标
            }            
        }
        if(small==-1)return A;
        A.add(array[small]);
        A.add(array[large]);
        return A;    
    }
}

不过可证明找到的第一组数字,它们之间的距离最远,所以乘积最小,所以筛选的工作可以免了。

8、二叉树的下一个节点

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

思路:这个节点有右子树,下一个点就是右子树的最左的节点;如果没有右子树,判断自己是不是自己父节点的左节点,如果是,下一个节点就是自己的父节点,如果自己是自己父节点的右节点,那就向上找子节点是父节点左节点的情况。

/** public class TreeLinkNode { int val; TreeLinkNode left = null; TreeLinkNode right = null; TreeLinkNode next = null; TreeLinkNode(int val) { this.val = val; } } */
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode){

        if(pNode.right!=null){
            TreeLinkNode t=pNode.right;
            while(t.left!=null)t=t.left;
            return t;
        }

        TreeLinkNode p=pNode.next;//parent-node
        TreeLinkNode c=pNode;//child-node
        while(p!=null&&p.right==c){
            c=p;
            p=p.next;  
        }
        return p;    
    }
}

9、数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。

思路一:原谅我一时没反应过来它的考点,请无视

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       int c=0;
       for(int i=0;i<array.length;i++){
           if(array[i]==k)c++;
       }
       return c;
    }
}

思路二:二分查找, O(logn) O ( l o g n ) 的时间复杂度
来自牛客网@披萨大叔

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int length = array.length;
        if(length == 0){
            return 0;
        }
        int firstK = getFirstK(array, k, 0, length-1);
        int lastK = getLastK(array, k, 0, length-1);
        if(firstK != -1 && lastK != -1){
             return lastK - firstK + 1;
        }
        return 0;
    }
    //递归写法
    private int getFirstK(int [] array , int k, int start, int end){
        if(start > end){
            return -1;
        }
        int mid = (start + end) >> 1;
        //int mid=start+(end-start)>>1 //其实这样更好,避免start + end太大溢出
        if(array[mid] > k){
            return getFirstK(array, k, start, mid-1);
        }else if (array[mid] < k){
            return getFirstK(array, k, mid+1, end);
        }else if(mid-1 >=0 && array[mid-1] == k){
            return getFirstK(array, k, start, mid-1);
        }else{
            return mid;
        }
    }
    //循环写法
    private int getLastK(int [] array , int k, int start, int end){
        int length = array.length;
        int mid = (start + end) >> 1;
        while(start <= end){
            if(array[mid] > k){
                end = mid-1;
            }else if(array[mid] < k){
                start = mid+1;
            }else if(mid+1 < length && array[mid+1] == k){
                start = mid+1;
            }else{
                return mid;
            }
            mid = (start + end) >> 1;
        }
        return -1;
    }
}

还有更简洁的
来自牛客网@drdr

//因为data中都是整数,所以可以稍微变一下,不是搜索k的两个位置,而是搜索k-0.5和k+0.5
//这两个数应该插入的位置,然后相减即可。
class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        return biSearch(data, k+0.5) - biSearch(data, k-0.5) ;
    }
private:
    int biSearch(const vector<int> & data, double num){
        int s = 0, e = data.size()-1;     
        while(s <= e){
            int mid = (e - s)/2 + s;
            if(data[mid] < num)
                s = mid + 1;
            else if(data[mid] > num)
                e = mid - 1;
        }
        return s;
    }
};

上述方法,就算k不存在于数组中,两次查找返回的值都是一样的,相减为0,没问题。

10、把二叉树层序打印成多行

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

思路一:第一想法在每层节点之间加null节点来分隔,不过可以计数不用分隔符

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer> > AA=new ArrayList<ArrayList<Integer>>();
        if(pRoot==null)return AA;

        Queue<TreeNode> q=new LinkedList<>();
        q.offer(pRoot);
        int count,c;
        ArrayList<Integer> A=new ArrayList<>();

        while(!q.isEmpty()){
            count=q.size();
            c=0;
            while(c++<count){//每一层的遍历
                if(q.peek().left!=null)q.offer(q.peek().left);
                if(q.peek().right!=null)q.offer(q.peek().right);
                A.add(q.peek().val);
                q.poll();                
            }
            //添加进去的是引用,如果直接把A加进去然后clear,AA里面的也会被clear
            AA.add(new ArrayList<>(A));
            A.clear();
        }
        return AA;
    }
}

思路二:层序遍历,还可以递归,扩展思路
来自牛客网@spursKawhi

//用递归做的
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> list = new ArrayList<>();
        depth(pRoot, 1, list);
        return list;
    }

    private void depth(TreeNode root, int depth, ArrayList<ArrayList<Integer>> list) {
        if(root == null) return;
        if(depth > list.size())//先构造一个空的ArrayList,传入嵌套容器
            list.add(new ArrayList<Integer>());
        list.get(depth -1).add(root.val);//然后把同一层的节点装进去

        depth(root.left, depth + 1, list);
        depth(root.right, depth + 1, list);
    }
}

思路三:用两个队列,来交替存储每层的节点

点赞