• 力扣记录:剑指offer(3)——JZ24-32


    JZ24 反转链表

    • 双指针,左指针指向上一个元素,右指针指向当前元素,右指针元素指向左指针元素
      • 时间复杂度O(n),空间复杂度O(1)
    class Solution {
        public ListNode reverseList(ListNode head) {
            //双指针
            ListNode left = null;
            ListNode right = head;
            //临时变量交换
            ListNode temp = null;
            while(right != null){
                temp = right.next;
                right.next = left;
                left = right;
                right = temp;
            }
            //返回结果
            return left;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 递归,同双指针
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public ListNode reverseList(ListNode head) {
            //递归
            return reverse(null, head);
        }
        private ListNode reverse(ListNode left, ListNode right){
            //右指针到尾时结束
            if(right == null) return left;
            //临时变量交换
            ListNode temp = right.next;
            right.next = left;
            left = right;
            right = temp;
            //递归
            return reverse(left, right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    JZ25 合并两个排序的链表

    • 迭代,定义虚拟头结点依次取两条链表的较小节点
      • 时间复杂度O(m+n),空间复杂度O(1)
    class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            //定义虚拟头结点
            ListNode virtualHead = new ListNode(0);
            //定义处理的当前节点
            ListNode cur = virtualHead;
            //将节点指向两个链表中较小的那个节点
            while(l1 != null && l2 != null){
                if(l1.val > l2.val){
                    cur.next = l2;
                    l2 = l2.next;
                }else{
                    cur.next = l1;
                    l1 = l1.next;
                }
                //移动当前节点
                cur = cur.next;
            }
            //将剩下非空的链表直接放在后面
            cur.next = (l1 == null ? l2 : l1);
            //返回结果
            return virtualHead.next;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 递归,返回当前节点中较小的,当前节点指向两条链路下一节点节点中较小的节点
      • 时间复杂度O(m+n),空间复杂度O(m+n)
    class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            //递归
            if(l1 == null){
                return l2;
            }else if(l2 == null){
                return l1;
            }else if(l1.val > l2.val){
                l2.next = mergeTwoLists(l1, l2.next);
                return l2;
            }else{
                l1.next = mergeTwoLists(l1.next, l2);
                return l1;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    JZ26 树的子结构

    • 递归,B为A的子结构或B为A左子树的子结构或B为A右子树的子结构。
      • 时间复杂度O(mn),空间复杂度O(m)
    class Solution {
        public boolean isSubStructure(TreeNode A, TreeNode B) {
            //A或B为空则为false
            if(A == null || B == null) return false;
            //递归
            boolean res1 = compareStructure(A, B);//B为A的子结构
            boolean res2 = isSubStructure(A.left, B);//B为A左子树的子结构
            boolean res3 = isSubStructure(A.right, B);//B为A右子树的子结构
            return res1 || res2 || res3;
        }
        //判断B是否在A的子结构
        private boolean compareStructure(TreeNode A, TreeNode B){
            //遍历完B后直接返回true
            if(B == null) return true;
            if(A == null && B != null) return false;
            //当前节点相同,递归
            if(A.val == B.val){
                return compareStructure(A.left, B.left) && compareStructure(A.right, B.right);
            }
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    JZ27 二叉树的镜像

    • 递归,可以在本函数直接递归,注意左右子树赋值
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public TreeNode mirrorTree(TreeNode root) {
            //判断特殊情况
            if(root == null) return null;
            //递归
            //前序遍历
            TreeNode temp = root.left;
            //交换左右子树
            root.left = mirrorTree(root.right);
            root.right = mirrorTree(temp);
            return root;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    JZ28 对称的二叉树

    • 递归,注意判断同一侧:内侧和外侧
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public boolean isSymmetric(TreeNode root) {
            //判断特殊情况
            if(root == null) return true;
            //递归
            return compareSymmetric(root.left, root.right);
        }
        //判断两棵子树对应位置是否一致
        private boolean compareSymmetric(TreeNode left, TreeNode right){
            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 compareSymmetric(left.left, right.right) && compareSymmetric(left.right, right.left);
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    JZ29 顺时针打印矩阵

    • 模拟打印顺序,注意循环次数(圈数)、每次循环四条边的起点和终点、最后中心部分的处理。
      • 时间复杂度O(mn),空间复杂度O(1)
    class Solution {
        public int[] spiralOrder(int[][] matrix) {
            //模拟打印顺序
            int m = matrix.length;  //行数
            if(m == 0){ //判断特殊情况
                int[] res = {};
                return res;
            }
            int n = matrix[0].length;   //列数
            int[] res = new int[m * n]; //结果及下标计数
            int count = 0;
            int loop = Math.min(m, n) / 2;  //循环次数,圈数
            //第一圈圈第一条边的起点
            int startX = 0;
            int startY = 0;
            //第一圈横竖边长度
            int lengX = n - 1;
            int lengY = m - 1;
            //开始模拟
            while(loop >= 1){
                //每圈第一条边的起点
                int i = startX;
                int j = startY;
                //第一条边
                for(; j < lengX; j++){
                    res[count++] = matrix[i][j];
                }
                //第二条边
                for(; i < lengY; i++){
                    res[count++] = matrix[i][j];
                }
                //第三条边
                for(; j > startX; j--){
                    res[count++] = matrix[i][j];
                }
                //第四条边
                for(; i > startY; i--){
                    res[count++] = matrix[i][j];
                }
                //修改下一次循环的起点和边长度
                startX++;
                startY++;
                lengX--;
                lengY--;
                loop--;
            }
            //最后处理中心部分
            if(m > n){
                //行数m大于列数n时,且n为奇数时,中心为(m-循环次数*2)*1的矩阵
                //起点即为循环次数,终点为起点加长度,纵向
                if(n % 2 == 1){
                    loop = n / 2;   //循环次数
                    int leng = m - loop;    //终点
                    //开始添加
                    for(int i = loop; i < leng; i++){
                        res[count++] = matrix[i][loop];
                    }
                }
            }else if(m < n){
                //行数m小于列数n时,且m长度为奇数时,中心为1*(n-循环次数*2)的矩阵
                //起点即为循环次数,终点为起点加长度,横向
                if(m % 2 == 1){
                    loop = m / 2;   //循环次数
                    int leng = n - loop;    //终点
                    //开始添加
                    for(int j = loop; j < leng; j++){
                        res[count++] = matrix[loop][j];
                    }
                }
            }else{
                //行数m等于列数n时,且长度为奇数时,中心为1*1矩阵
                if(m % 2 == 1){
                    loop = m / 2;   //中心点即为循环次数
                    //开始添加
                    res[count++] = matrix[loop][loop];
                }
            }
            return res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    JZ30 包含min函数的栈

    • 定义辅助栈保存当前栈中的最小值。
      • 时间复杂度O(1),空间复杂度O(n)
    class MinStack {
        //定义辅助栈和输出栈
        Deque<Integer> stackm;
        Deque<Integer> stackOut;
        /** initialize your data structure here. */
        public MinStack() {
            //初始化(可以初始化辅助栈入栈int最大值)
            stackm = new LinkedList<>();
            stackOut = new LinkedList<>();
        }
        //入栈时进行判断,保证辅助栈栈顶保存当前最小元素
        public void push(int x) {
            //输出栈直接入栈
            stackOut.push(x);
            //辅助栈为空时直接入栈
            if(stackm.isEmpty()){
                stackm.push(x);
            }else{
                //若当前元素小于辅助栈最小值,则压入当前元素,否则再压入辅助栈最小值
                stackm.push(Math.min(stackm.peek(), x));
            }
        }
        //出栈时直接出栈
        public void pop() {
            stackOut.pop();
            stackm.pop();
        }
        //判断输出栈栈顶
        public int top() {
            return stackOut.peek();
        }
        //判断辅助栈栈顶
        public int min() {
            return stackm.peek();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    JZ31 栈的压入、弹出序列

    • 模拟栈的压入弹出,遍历弹入数组和弹出数组,对于栈有三种可能,将将弹入数组当前元素压入再弹出、将栈顶元素弹出、将弹入数组当前元素压入栈。
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public boolean validateStackSequences(int[] pushed, int[] popped) {
            //模拟
            int leng1 = pushed.length;
            int leng2 = popped.length;
            Deque<Integer> stackPush = new LinkedList<>();
            //判断特殊情况
            if(leng1 == 0 && leng2 == 0) return true;
            int i = 0;
            int j = 0;
            while(j < leng2 && i <= leng1){
                if(!stackPush.isEmpty() && stackPush.peek() == popped[j]){
                    stackPush.pop();
                    j++;
                }else if(i < leng1 && pushed[i] == popped[j]){
                    i++;
                    j++;
                }else{
                    if(i >= leng1) break;
                    stackPush.push(pushed[i++]);
                }
            }
            return j == leng2;
        }
        //优化
        public boolean validateStackSequences(int[] pushed, int[] popped) {
            Deque<Integer> stackPush = new LinkedList<>();
            int j = 0;
            for(int p : pushed){
                stackPush.push(p);
                while(!stackPush.isEmpty() && stackPush.peek() == popped[j]){
                    stackPush.pop();
                    j++;
                }
            }
            return stackPush.isEmpty();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    JZ32-I 从上到下打印二叉树

    • 层序遍历。
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public int[] levelOrder(TreeNode root) {
            //判断特殊情况
            if(root == null){
                int[] result = {};
                return result;
            }
            //层序遍历,使用队列
            Queue<TreeNode> queue = new LinkedList<>();
            ArrayList<Integer> res = new ArrayList<>();
            //初始化
            queue.offer(root);
            //开始遍历
            while(!queue.isEmpty()){
                int leng = queue.size();    //每层的长度
                for(int i = 0; i < leng; i++){
                    TreeNode cur = queue.poll();
                    if(cur.left != null) queue.offer(cur.left);
                    if(cur.right != null) queue.offer(cur.right);
                    res.add(cur.val);
                }
            }
            //将可变数组转为数组
            int[] result = new int[res.size()];
            result = res.stream().mapToInt(Integer::valueOf).toArray();
            //也可遍历赋值
            // for(int i = 0; i < res.size(); i++)
            //     result[i] = res.get(i);
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    JZ32-II 从上到下打印二叉树II

    • 层序遍历,每一层保存为一个数组。
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public List<List<Integer>> levelOrder(TreeNode root) {
            //层序遍历,使用队列
            Queue<TreeNode> queue = new LinkedList<>();
            ArrayList<List<Integer>> res = new ArrayList<>();
            //判断特殊情况
            if(root == null) return res;
            queue.offer(root);  //初始化
            while(!queue.isEmpty()){
                int leng = queue.size();    //每层长度
                ArrayList<Integer> resLine = new ArrayList<>();
                for(int i = 0; i < leng; i++){
                    TreeNode cur = queue.poll();
                    if(cur.left != null) queue.offer(cur.left);
                    if(cur.right != null) queue.offer(cur.right);
                    resLine.add(cur.val);    //保存该层元素
                }
                res.add(resLine);   //保存每层结果
            }
            //将可变数组转为数组
            return res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    JZ32-III 从上到下打印二叉树III

    • 层序遍历,使用双端队列(存过程或结果),每一层保存为一个数组,同时定义变量决定当前层为正序或反序。
      • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        //过程
        public List<List<Integer>> levelOrder(TreeNode root) {
            //层序遍历,使用双端队列
            Deque<TreeNode> deque = new LinkedList<>();
            ArrayList<List<Integer>> res = new ArrayList<>();
            //判断特殊情况
            if(root == null) return res;
            deque.offer(root);  //初始化
            boolean flag = true;   //决定当前层遍历顺序,true为正序,false为反序
            while(!deque.isEmpty()){
                int leng = deque.size();    //当前层长度
                ArrayList<Integer> resLine = new ArrayList<>();
                TreeNode cur = new TreeNode(-1);
                for(int i = 0; i < leng; i++){
                    if(flag){   //正序
                        cur = deque.pollFirst();
                        if(cur.left != null) deque.offerLast(cur.left);
                        if(cur.right != null) deque.offerLast(cur.right);
                    }else{
                        cur = deque.pollLast();
                        if(cur.right != null) deque.offerFirst(cur.right);
                        if(cur.left != null) deque.offerFirst(cur.left);
                    }
                    resLine.add(cur.val);       
                }
                res.add(resLine);
                flag = !flag;
            }
            return res;
        }
        //结果
        public List<List<Integer>> levelOrder(TreeNode root) {
            //层序遍历,使用队列
            Queue<TreeNode> queue = new LinkedList<>();
            ArrayList<List<Integer>> res = new ArrayList<>();
            //判断特殊情况
            if(root == null) return res;
            queue.offer(root);  //初始化
            boolean flag = true;   //决定当前层遍历顺序,true为正序,false为反序
            while(!queue.isEmpty()){
                int leng = queue.size();    //每层长度
                Deque<Integer> resLine = new LinkedList<>(); //使用双端队列
                for(int i = 0; i < leng; i++){
                    TreeNode cur = queue.poll();
                    if(flag){   //正序
                        resLine.offerLast(cur.val);
                    }else{  //反序
                        resLine.offerFirst(cur.val);
                    }
                    if(cur.left != null) queue.offer(cur.left);
                    if(cur.right != null) queue.offer(cur.right);
                }
                res.add(new LinkedList<Integer>(resLine));   //保存每层结果
                flag = !flag;
            }
            //将可变数组转为数组
            return res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
  • 相关阅读:
    多svn仓库一键更新脚本分享
    jenkins默认环境变量值
    端口安全、MAC地址漂移、MACsec、流量控制、DHCP snooping
    FFmpeg 命令:从入门到精通 | ffmpeg 命令直播
    达梦:开启sql日志记录
    (209)Verilog HDL:设计一个电路之Rule 90
    数据库上云实践:使用Ora2pg进行数据库迁移
    Linux多线程
    MySQL企业级参数调优实践分享
    k8s Helm安装EFK
  • 原文地址:https://blog.csdn.net/Kiwi_fruit/article/details/125748441