力扣记录:剑指offer(1)——JZ3-12

JZ3 数字在升序数组重出现的次数

  • 遍历数组并存储到set中

    • 时间复杂度O(n),空间复杂度O(n)
    class Solution {
        public int findRepeatNumber(int[] nums) {
            //遍历数组并存储到set中
            Set<Integer> set1 = new HashSet<>();
            for(int i = 0; i < nums.length; i++){
                if(set1.contains(nums[i])){
                    return nums[i];
                }else{
                    set1.add(nums[i]);
                }
            }
            return -1;
        }
    }
    
  • 数组原地交换,将索引与值一一对应

    • 时间复杂度O(n),空间复杂度O(1)
class Solution {
    public int findRepeatNumber(int[] nums) {
        //数组原地交换,将索引与值一一对应
        int i = 0;
        //使用while,交换后i不递增,当nums[i] == i 的时候i才递增
        while(i < nums.length){
            if(nums[i] == i){
                i++;
                continue; 
            }
            if(nums[nums[i]] == nums[i]) return nums[i];
            int temp = nums[i];
            nums[i] = nums[temp];
            nums[temp] = temp;
        }
        return -1;
    }
}

JZ4 二维数组中的查找

  • 多次二分
    • 时间复杂度O(n*logm),空间复杂度O(1)
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        //二分
        int n = matrix.length;
        //判断特殊情况
        if(n == 0) return false;
        int m = matrix[0].length;
        if(m == 0) return false;
        //定义结果
        boolean result = false;
        for(int i = 0; i < n; i++){
            if(matrix[i][0] <= target && matrix[i][m - 1] >= target){
                result = binarySearch(matrix[i], target, m);
                if(result == true) return true;
            }
        }
        return result;
    }
    //二分查找
    private boolean binarySearch(int[] nums, int target, int m){
        //定义左右边界,左闭右闭
        int left = 0;
        int right = m - 1;
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(nums[mid] > target){
                right = mid - 1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                return true;
            }
            if(nums[left] == target || nums[right] == target) return true;
        }
        return false;
    }
}
  • 从右上角开始搜索,相当于二叉搜索树,左边比根节点小,下边比根节点大
    • 时间复杂度O(n+m),空间复杂度O(1)
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        //从右上角开始搜索,相当于二叉搜索树,左边比根节点小,下边比根节点大
        int n = matrix.length;
        //判断特殊情况
        if(n == 0) return false;
        int m = matrix[0].length;
        if(m == 0) return false;
        //定义起始行列
        int row = 0;
        int column = m - 1;
        //开始查找
        while(row <= n - 1 && column >= 0){
            if(matrix[row][column] > target){   //前一列
                column--;
            }else if(matrix[row][column] < target){ //下一行
                row++;
            }else{
                return true;
            }
        }
        //最后返回false
        return false;
    }
}

JZ5 替换空格

  • 双指针,左指针指向原来数组末尾,右指针指向新数组末尾,从后向前替换
    • 时间复杂度O(n),空间复杂度O(1)
class Solution {
    public String replaceSpace(String s) {
        //先定义左指针位置,原来数组末尾
        int left = s.length() - 1;
        //从后向前计算字符串有多少个空格并直接往后加长度
        for(int i = left; i >= 0; i--){
            if(s.charAt(i) == ' ') s += "  ";
        }
        //定义右指针,新数组末尾
        int right = s.length() - 1;
        //将字符串转为字符数组
        char[] s_arr = s.toCharArray();
        //从前往后替换
        while(left >= 0){
            //左指针不指向空格,直接复制
            if(s_arr[left] != ' '){
                s_arr[right] = s_arr[left];
            }else{//左指针指向空格,从后向前替换
                s_arr[right--] = '0';
                s_arr[right--] = '2';
                s_arr[right] = '%';
            }
            //开始下一轮循环
            left--;
            right--;
        }
        //返回结果
        return new String(s_arr);
    }
}

JZ6 从尾到头打印链表

  • 反转链表,双指针(或递归)原地反转链表后用数组接,参考206 反转链表
    • 时间复杂度O(n),空间复杂度O(1)
class Solution {
    public int[] reversePrint(ListNode head) {
        //双指针反转后用数组接
        ListNode left = null;
        ListNode right = head;
        ListNode temp = null;
        //计算链表长度
        int count = 0;
        while(right != null){
            temp = right.next;
            right.next = left;
            left = right;
            right = temp;
            count++;
        }
        //定义数组接
        int[] result = new int[count];
        for(int i = 0; i < count; i++){
            result[i] = left.val;
            left = left.next;
        }
        //返回结果
        return result;
    }
}
  • 使用栈实现
    • 时间复杂度O(n),空间复杂度O(n)
class Solution {
    public int[] reversePrint(ListNode head) {
        //使用栈实现
        Deque<Integer> stack = new LinkedList<>();
        //计算链表长度
        int count = 0;
        while(head != null){
            stack.push(head.val);
            head = head.next;
            count++;
        }
        //定义数组接
        int[] result = new int[count];
        for(int i = 0; i < count; i++){
            result[i] = stack.pop();
        }
        //返回结果
        return result;
    }
}

JZ7 重建二叉树

  • 从前序和中序遍历重建二叉树,递归
    • 时间复杂度O(n),空间复杂度O(1)
    • 补充:后序和中序重建二叉树,注意索引!
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //初始化,输入两个数组及其起始终止位置(减少定义新数组的次数,提高效率)
        //左闭右开
        return buildTreePreIn(preorder, 0, preorder.length, inorder, 0, inorder.length);
    }
    //递归函数
    private TreeNode buildTreePreIn(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight){
        //如果数组为空,直接返回
        if(inRight - inLeft < 1) return null;
        //只有一个元素
        if(inRight - inLeft == 1) return new TreeNode(inorder[inLeft]);
        //若数组不为空(节点不为空),则取前序数组的第一个节点(根节点)为中序数组的切割点
        int val = preorder[preLeft];
        TreeNode root = new TreeNode(val);
        int inCut = inLeft;
        for(; inCut < inRight; inCut++){
            if(inorder[inCut] == val) break;
        }
        //再根据中序数组的左右两部分(个数)切割前序数组为左右两部分
        //递归处理他们两个的左右部分
        //左闭右开
        //中序数组左右部分之间有一个切割点不能取
        //但是前序数组左右两部分之间没有切割点,前序数组的切割点在开头,因此第一个不能取
        root.left = buildTreePreIn(preorder, preLeft + 1, preLeft + (inCut - inLeft + 1), inorder, inLeft, inCut);
        root.right = buildTreePreIn(preorder, preLeft + (inCut - inLeft + 1), preRight, inorder, inCut + 1, inRight);
        //返回结果
        return root;
    }
}

JZ9 用两个栈实现队列

  • 栈实现队列
    • 时间复杂度O(1),空间复杂度O(n)
    • 补充:队列实现栈
class CQueue {
    //定义两个栈
    Deque<Integer> stackIn;
    Deque<Integer> stackOut;
    public CQueue() {
        //初始化
        stackIn = new LinkedList<>();
        stackOut = new LinkedList<>();
    }
    //添加元素
    public void appendTail(int value) {
        //push时直接将数据压入输入栈
        stackIn.push(value);
    }
    //删除元素
    public int deleteHead() {
        //当pop时需要进行判断
        //如果输出栈为空,则将输入栈中的所有数据按弹出顺序压入输出栈
        if(stackOut.isEmpty()){
            //若队列中没有元素,返回 -1
            if(stackIn.isEmpty()){
                return -1;
            }
            while(!stackIn.isEmpty()){
                stackOut.push(stackIn.pop());
            }
        }
        //然后pop,如果输入栈不为空则直接pop
        return stackOut.pop();
    }
}

JZ10-I 斐波那契数列

  • 动态规划DP
    • 时间复杂度O(n),空间复杂度O(1)
    • 补充:取模(Math.floorMod)、取余(%)对比:取模的商需要更接近负无穷,取余的商需要更接近0;科学计数法为double类型
class Solution {
    public int fib(int n) {
        //判断特殊情况
        if(n <= 1) return n;
        //定义dp数组
        int[] dp = new int[2];
        //初始化
        dp[0] = 0;
        dp[1] = 1;
        //遍历顺序正序
        for(int i = 2; i <= n; i++){
            //求模
            int temp = Math.floorMod(dp[0] + dp[1], 1000000007);
            dp[0] = dp[1];
            dp[1] = temp;
        }
        //返回结果
        return dp[1];
    }
}

JZ10-II 青蛙跳台阶问题

  • 动态规划DP,同斐波那契数列
    • 时间复杂度O(n),空间复杂度O(1)
class Solution {
    public int numWays(int n) {
        //判断特殊情况
        if(n <= 1) return 1;
        //定义dp数组
        int[] dp = new int[2];
        //初始化
        dp[0] = 1;
        dp[1] = 1;
        //遍历顺序正序
        for(int i = 2; i <= n; i++){
            int temp = Math.floorMod(dp[0] + dp[1], 1000000007);
            dp[0] = dp[1];
            dp[1] = temp;
        }
        //返回结果
        return dp[1];
    }
}

JZ11 旋转数组的最小数字

  • 二分法
    • 时间复杂度O(logn),空间复杂度O(1)
class Solution {
    public int minArray(int[] numbers) {
        //判断特殊情况
        if(numbers.length == 1) return numbers[0];
        //二分法定义区间,左闭右闭
        int left = 0;
        int right = numbers.length - 1;
        while(left < right){
            int mid = left + (right - left) / 2;
            if(numbers[mid] < numbers[right]){
                right = mid;
            }else if(numbers[mid] > numbers[right]){
                left = mid + 1;
            }else{
                right--;
            }
        }
        //返回结果
        return numbers[left];
    }
}

JZ12 矩阵中的路径

  • 回溯
    • 时间复杂度O(MN3^L),空间复杂度O(1)
class Solution {
    public boolean exist(char[][] board, String word) {
        //定义回溯函数进行判断
        //遍历二维数组,从第(i, j)出发判断该位置是否符合第x个元素
        //定义标记数组保证走过的位置不重复走
        int h = board.length;
        int w = board[0].length;
        int[][] used = new int[h][w];
        for(int i = 0; i < h; i++){
            for(int j = 0; j < w; j++){
                boolean res = backtracking(board, word, i, j, used, 0);
                //如果找到路径则返回true
                if(res == true) return true;
            }
        }
        //否则最后返回false
        return false;
    }
    //回溯函数
    //输入二维数组,要求的字符串,二维数组的位置i,j,已走过的位置数组,当前判断字符串的第几位
    private boolean backtracking(char[][] board, String word, int i, int j, int[][] used, int x){
        //当位置不合法时直接返回false
        if(i < 0 || i >= board.length || j < 0 || j >= board[0].length) return false;
        //当前位置使用过直接返回false
        if(used[i][j] == 1) return false;
        //不符合条件时直接返回false
        if(board[i][j] != word.charAt(x)) return false;
        //字符串遍历结束后返回true
        if(x == word.length() - 1) return true;
        //标记used数组,进行下一个字符判断
        used[i][j] = 1;
        //下一个字符可能为四个方向
        //使用for循环减少代码重复
        int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        boolean result = false;
        for (int[] dir : directions) {
            boolean flag = backtracking(board, word, i + dir[0], j + dir[1], used, x + 1);
            if (flag) {
                result = true;
                break;
            }
        }
        used[i][j] = 0;
        return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值