算法打卡day13|二叉树篇02|Leetcode 102.二叉树的层序遍历、226.翻转二叉树、101. 对称二叉树

  在做题之前说明

DequeQueue

在Java中,DequeQueue是两种不同的数据结构接口,它们都继承自Collection接口;DequeQueue的超集,提供了更多的操作和灵活性,以下它们之间存在一些关键的区别:

  1. 操作范围

    • Queue接口仅定义了一端(队尾)用于插入元素,另一端(队首)用于删除元素的操作,这符合先进先出(FIFO)的原则。
    • Deque接口则扩展了Queue的能力,允许在双端进行插入和删除元素,因此它不仅支持FIFO原则,还可以支持后进先出(LIFO)原则,类似于栈。
  2. 具体实现

    • Queue接口的典型实现是LinkedListPriorityQueue。其中,LinkedList实现了所有Queue接口的方法,同时还支持Deque接口的方法。而PriorityQueue则是一个基于优先级堆的无界队列,它不支持Deque接口。
    • Deque接口的实现包括ArrayDequeLinkedListArrayDeque是基于数组的双端队列实现,通常比LinkedList更高效,特别是在随机访问和尾部操作方面。LinkedList也实现了Deque接口,因此它既可以作为队列使用,也可以作为双端队列使用。
  3. 方法差异

    • Queue接口提供的方法如offer(E e)用于添加元素,poll()用于移除并返回队首元素,peek()用于查看队首元素而不移除。
    • Deque接口除了包含Queue的所有方法外,还提供了额外的方法,如addFirst(E e)addLast(E e)用于在双端分别添加元素,removeFirst()removeLast()用于从双端移除元素,以及getFirst()getLast()用于获取双端的元素而不移除。
  4. 性能考虑

    • 对于需要频繁在两端插入和删除元素的场景,Deque通常是更好的选择,因为它专门为此设计。
    • 如果应用场景只需要队列的基本操作(即只有一端添加,另一端删除),那么使用Queue可能更为简洁,尤其是当使用PriorityQueue实现时,它提供了特定的排序机制。

offer()方法与offerLast()

Deque接口中的offer()方法与offerLast()方法都用于向双端队列的末尾添加元素,但它们之间有细微的差别。

offer()方法Queue接口中定义的,它被所有实现了Queue接口的类继承,包括Deque。当你调用offer()方法时,它会将元素添加到Deque的末尾。如果Deque已经满了offer()方法将抛出一个IllegalStateException。而offerLast()方法是Deque接口特有的,Deque已满时不会抛出异常,而是返回一个特殊的值false,表明元素没有被添加到队列中。这种行为使得offerLast()方法在某些情况下更加健壮,因为它避免了因队列满而导致的程序异常。

peek()peekFirst()

peek()方法是从Queue接口继承来的,它返回队列头部的元素,但不移除它。如果队列为空,则peek()方法返回null

peekFirst()方法是Deque接口特有,它与peek()方法类似,也是返回队列头部的元素,但不移除它。然而,与peek()方法不同的是,peekFirst()方法不会抛出空指针异常,即使队列为空。当队列为空时,peekFirst()方法返回null。这使得peekFirst()方法在某些情况下更加安全和可靠

以此类推,其他的比如poll()和pollFirst()也是如此;

算法题

Leetcode  102.二叉树的层序遍历

题目链接:102.二叉树的层序遍历

大佬视频讲解:二叉树的层序遍历视频讲解

个人思路

层序遍历也就是图论中的广度优先遍历,是使用队列的结构;用队列一层层遍历,并加入结果数组,最后返回;

解法
迭代法
class Solution {
    public List<List<Integer>> resList = new ArrayList<List<Integer>>();

    public List<List<Integer>> levelOrder(TreeNode root) {
        checkFun01(root);

        return resList;
    }

    public void checkFun01(TreeNode root){
        if(root==null) return;

        Queue<TreeNode> que=new LinkedList<TreeNode>();
        que.offer(root);//加入节点

        while(!que.isEmpty()){//终止条件

            int len =que.size();//当前层数 元素的数量
            List<Integer> itemList=new ArrayList<Integer>();

            while(len>0){
                TreeNode temp=que.poll();
                itemList.add(temp.val);//结果子列表加入值

                if(temp.left!=null) que.offer(temp.left);//加入左节点
                if(temp.right!=null) que.offer(temp.right);

                len--;//遍历下一个节点
            }
            resList.add(itemList);//结果列表加入子列表
        }
    }

}

时间复杂度:O(n);(遍历整棵树)

空间复杂度:O(n);(使用一个结果子列表,和一个列表)

递归法
class Solution {
    //1.确定递归函数的参数和返回值
    public List<List<Integer>> resList = new ArrayList<List<Integer>>();


    public List<List<Integer>> levelOrder(TreeNode root) {
        checkFun02(root,0);

        return resList;
    }


    
    public void checkFun02(TreeNode root, int deep){
        if(root==null) return;//2.确定递归终止条件
        
        //3.确定单层递归的逻辑
        deep++;//层级
        if(resList.size()<deep){//利用list的索引值进行层级界定

            List<Integer> item=new ArrayList<Integer>();
            resList.add(item); //当层级增加时,list的Item也增加
        }
        resList.get(deep-1).add(root.val);//get 层级 

        checkFun02(root.left,deep);//递归左子树
        checkFun02(root.right,deep);//递归右子树
        
    }
}

时间复杂度:O(n);(遍历整棵树)

空间复杂度:O(n);(使用一个结果子列表,和一个列表)


Leetcode 226.翻转二叉树

题目链接:226.翻转二叉树

大佬视频讲解:翻转二叉树视频讲解

个人思路

遍历二叉树,将每个节点的左右孩子交换一下

解法

这道题关键在于遍历顺序,这道题目使用前序遍历,后序遍历和层序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次

递归法(DFS)
class Solution {
    public TreeNode invertTree(TreeNode root) {//1.确定递归函数的参数和返回值
        if (root == null) {//2.确定终止条件
            return null;
        }
        
        //3.确定单层递归的逻辑
        invertTree(root.left);//后序遍历:左右中(根)
        invertTree(root.right);
        swapChildren(root);
        return root;
    }

    private void swapChildren(TreeNode root) {
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
    }
}

时间复杂度:O(n);(遍历整棵树)

空间复杂度:O(n);(递归树的高度h,使用临时节点来换位)

迭代法(BFS)

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {return null;}

        ArrayDeque<TreeNode> deque = new ArrayDeque<>();//队列进行层序遍历
        deque.offer(root);

        while (!deque.isEmpty()) {
            int size = deque.size();
            while (size-- > 0) {//遍历各层的各个节点
                TreeNode node = deque.poll();
                swap(node);//交换节点的左右子树

                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
            }
        }
        return root;
    }

    public void swap(TreeNode root) {
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }
}

时间复杂度:O(n);(遍历整棵树)

空间复杂度:O(n);(使用队列进行层序遍历和临时节点来换位)


Leetcode 101. 对称二叉树

题目链接:101. 对称二叉树

大佬视频讲解:对称二叉树视频讲解

个人思路

将二叉树分成两半一边为左子树A,一边为右子树B,然后就对比 A节点的左边和B节点的右边,A节点的右边和B节点的左边,都相等就对称了

解法
递归法

因为要遍历两棵树而且要比较内侧和外侧节点,所以一个树的遍历顺序是左右中,一个树的遍历顺序是右左中

递归三部曲

1.确定递归函数的参数和返回值

因为要比较的是根节点的两个子树是否是相互翻转的,参数就是左子树节点和右子树节点。返回值为布尔类型。

2.确定终止条件

要比较两个节点数值相不相同,要把两个节点为空的情况分清楚,不然后面比较数值的时候就会操作空指针了。

节点为空的情况有:

  1. 1.左节点为空,右节点不为空,不对称,return false
  2. 左不为空,右为空,不对称 return false
  3. 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:左右都不为空,比较节点数值,不相同就return false

把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况

3.确定单层递归的逻辑

处理 左右节点都不为空,且数值相同的情况。

1.比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。

2.比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。

如果左右都对称就返回true ,有一侧不对称就返回false 。

 public boolean isSymmetric1(TreeNode root) {
        return compare(root.left, root.right);
    }

    private boolean compare(TreeNode left, TreeNode right) {1.确定递归函数的参数和返回值
        
        2.确定终止条件
        //处理节点为空的4种情况
        if (left == null && right != null) {
            return false;
}
        if (left != null && right == null) {
            return false;
        }

        if (left == null && right == null) {
            return true;
        }
        if (left.val != right.val) {
            return false;
        }

        3.确定单层递归的逻辑
        // 递归比较二叉树外侧
        boolean compareOutside = compare(left.left, right.right);
        // 递归比较内侧
        boolean compareInside = compare(left.right, right.left);
        return compareOutside && compareInside;//都为真则为真
    }

时间复杂度:O(n);(遍历二叉树)

空间复杂度:O(n);(递归树的高度h)

迭代法

    public boolean isSymmetric3(TreeNode root) {
        Queue<TreeNode> deque = new LinkedList<>();
        deque.offer(root.left);
        deque.offer(root.right);

        while (!deque.isEmpty()) {
            TreeNode leftNode = deque.poll();
            TreeNode rightNode = deque.poll();
            if (leftNode == null && rightNode == null) {
                continue;
            }

            // 将其他三个判断条件合并
            if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
                return false;
            }
            //左子树的左节点和右子树的右节点
            deque.offer(leftNode.left);
            deque.offer(rightNode.right);

            //左子树的右节点和右子树的左节点
            deque.offer(leftNode.right);
            deque.offer(rightNode.left);
        }
        return true;
    }

时间复杂度:O(n);(遍历二叉树)

空间复杂度:O(n);(使用队列)

以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网

  • 19
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值