剑指offer刷题第一天

本文解析了剑指 Offer 中关于数组操作(重复数字、二维查找)和链表操作(反转打印、重建二叉树)的多种算法,包括哈希表、原地交换、二分查找、线性查找和动态数据结构。通过实例演示和代码实现,帮助读者理解并提升编程技巧。
摘要由CSDN通过智能技术生成
1.剑指 Offer 03. 数组中重复的数字

方法一:哈希表法

  • 看到重复的数字,首先想到哈希表(set)记录数组中各个数字,具体方法是先新建一个hashset,然后遍历数组中的每一个数字num:当num在表中,则返回num,最后将Num添加到哈希表中。返回-1.因为本题中一定有重复数字,返回其他值也行。

  • 代码:

    class Solution {
        public int findRepeatNumber(int[] nums) {
            HashSet <Integer> set = new HashSet<>();
            for(int x : nums){
                if(set.contains(x)){
                    return x;
                }
            set.add(x);
            }
            return -1;
        }
    }
    

方法二:原地交换法

  • 数组的索引和值是一对多的关系。首先判断索引和索引对应的值是否相等,如果相等,则不需要交换;判断索引 nums[i]处和索引 i处对应的元素值都为Nums[i],即重复,返回nums[i];否则将索引i和nums[i]的元素值交换

  • 代码

    class Solution {
        public int findRepeatNumber(int[] nums) {
            for(int i=0;i<nums.length;i++){
                while(nums[i] != i){
                    if(nums[i]==nums[nums[i]]){
                        return nums[i];
                    }
                    int temp=nums[i];
                    nums[i]=nums[temp];
                    nums[temp]=temp;
                }
            }
            return -1;
        }
    }
    
2.剑指 Offer 04. 二维数组中的查找

方法一:暴力解法

  • 双循环,一个一个查找

    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        int m = matrix.length;
        if (m == 0) return false;
        int n = matrix[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == target) {
                    return true;
                }
            }
        }
        return false;
    }
    
    

方法二:二分查找

  • 思路:首先,要先比较m,n二者的大小,其中较小的值就是对角线的个数,也就是循环的次数,然后分别在行和列方向上做二分查找。定义两个方法,如果其中的一个为真,则返回true。在行上,首先将行值给最低的数,因为要从这行开始找,然后开始循环,找到中位数,此时固定行,列是变量,列就为Mid一直在变,和目标值进行比较,小于目标值就将左指针右移到mid右边,大于就将右指针左移一位,相等直接返回true,否则返回false。在列方法里面,和行一样,也就是把row换成了col。

  • 代码

    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        int m = matrix.length;
        if (m == 0) return false;
        int n = matrix[0].length;int shortDim = Math.min(m, n);
        for (int i = 0; i < shortDim; i++) {
            boolean rowFound = binarySearchRow(matrix, i, target);
            boolean colFount = binarySearchCol(matrix, i, target);if (rowFound || colFount) {
                return true;
            }
        }
        return false;
    }private boolean binarySearchRow(int[][] matrix,
                                    int row, int target) {
        int lo = row;
        int hi = matrix[0].length - 1;while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (matrix[row][mid] == target) {
                return true;
            } else if (matrix[row][mid] < target) {
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }return false;
    }
    
    

方法三:线性查找

  • 思路:发现,左下角的元素值大于它上面的元素,小于它右边的元素,因此如果目标值大于矩阵中的某一个数,那么就像列元素加一,如果小于,就将行元素减一,找到了就返回true,否则返回false。

  • 代码:

    public boolean findNumberIn2DArray(int[][] matrix,int target) {
        int m = matrix.length;
        if (m == 0) return false;
        int n = matrix[0].length;int i = m - 1;
        int j = 0;
        while (i >= 0 && j < n) {
            if (matrix[i][j] < target) j++;
            else if (matrix[i][j] > target) i--;
            else return true;
        }return false;
    }
    
    
3.剑指 Offer 05. 替换空格

方法一:StringBuilder

  • 思路:java中的StringBuilder是表达可变的字符串的。首先初始化一个实例,然后遍历,如果遍历到非空字符串时,直接将字符串append到实例中,否则将%20append到实例中,最后返回toString

  • 代码

    public String replaceSpace(String s) {
    ​
       StringBuilder sb = new StringBuilder();
       for (char c : s.toCharArray()) {
           if (c == ' ') sb.append("%20");
           else sb.append(c);
       }return sb.toString();
    }
    

方法二:数组

  • 思路:首先,初始化一个静态数组,长度为3*n,再初始化两个指针,i指向静态数组的第一个位置,j指向元数组位置,

  • 代码

    public String replaceSpace(String s) {
        int n = s.length();
        char[] newArr = new char[3 * n];
        int j = 0;
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (c == ' ') {
                newArr[j++] = '%';
                newArr[j++] = '2';
                newArr[j++] = '0';
            } else {
                newArr[j++] = c;
            }
        }
        return new String(newArr, 0, j);
    }
    
    

    或者

    public String replaceSpace(String s) {
        int n = s.length();
        int cnt = 0;
        for (char c : s.toCharArray()) {
            if (c == ' ') cnt++;
        }char[] newArr = new char[n + 2 * cnt];
        int j = 0;
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (c == ' ') {
                newArr[j++] = '%';
                newArr[j++] = '2';
                newArr[j++] = '0';
            } else {
                newArr[j++] = c;
            }
        }
        return new String(newArr);
    }
    

4.剑指 Offer 06. 从尾到头打印链表

思路:要返回一个数组,首先要计算数组的长度,等于链表的节点个数:从头节点往后遍历,求出节点个数之后u,将遇到节点的值从后往前放。

代码:

public int[] reversePrint(ListNode head){
    if(head = null){
        return new int[]{};
    }
    
    int length = 0;
    ListNode curr = head;
    while(curr != null){
        length++;
        curr = curr.next;
    }
    
    int[] res = new int[length];
    int i = length-1;
    curr = head;
    while(curr != null){
        res[i] = cur.val;
        i--;
        curr = curr.next;
    }
    return res;
}

或者采用栈的方法

public int[] reversePrint(ListNode head) {
    if (head == null) return new int[]{};// 1. 先将链表从头至尾的节点依次添加到栈中
    Stack<ListNode> stack = new Stack<>();
    ListNode curr = head;
    while (curr != null) {
        stack.push(curr);
        curr = curr.next;
    }// 2. 依次从栈中取出节点,然后添加到数组中
    int[] res = new int[stack.size()];
    int i = 0;
    while (!stack.isEmpty()) {
        res[i] = stack.pop().val;
        i++;
    }return res;
}

5.剑指 Offer 07. 重建二叉树

思路:

  • 前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
    中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。

代码:

class Solution{
    HashMap<Integer,Integer> map = new HashMap<>();//标记中序遍历
    int[] preorder;//保留的先序遍历,方便递归时依据索引查看先序遍历的值
    
    public TreeNode buildTree(int[] preorder,int[] inorder){
        this.preorder = preorder;
        //将中序遍历的值及索引放在map中,方便递归时获取左子树与右子树的数量及其根的索引
        for(int i =0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return recur(0,0,inorder.length-1);  
        //三个索引分别为
        //当前根的的索引
        //递归树的左边界,即数组左边界
        //递归树的右边界,即数组右边界
    }
    
    TreeNode recur(int pre_root,int in_left,int in_right){
        if(in_left > in_right) return null;
        TreeNode root = new TreeNode(preorder[pre_root]);//获取root节点
        int idx = map.get(preorder[pre_root]);//获取在中序遍历中根节点所在索引,以方便获取左子树的数量
        root.left = recur(pre_root + 1,in_left,idx-1);
        //左子树的根的索引为先序中的根节点+1 
        //递归左子树的左边界为原来的中序in_left
        //递归右子树的右边界为中序中的根节点索引-1
        root.right = recur(pre_root + (idx-in_left)+1,idx+1,in_right);
        //右子树的根的索引为先序中的 当前根位置 + 左子树的数量 + 1
        //递归右子树的左边界为中序中当前根节点+1
        //递归右子树的有边界为中序中原来右子树的边界
        return root;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值