剑指OFFER25天打卡---第一天

剑指OFFER打卡

剑指 Offer 03. 数组中重复的数字

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

方法一: 暴力破解

class Solution {
    public int findRepeatNumber(int[] nums) {
     int[] q = new int[100005];
     for(int i = 0; i < nums.length; i ++ ) {
         int res = nums[i];
         q[res]++;
     }
     for(int i = 0; i < 100005; i ++ ) {
         if(q[i] > 1) {
             return i;
         }
     }
     return 0;
    }
}

方法二:哈希表

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

方法三:原地交换

思路

(原地交换) O(n)
首先遍历一遍数组,如果存在某个数不在0到n- 1的范围内,则返回-1。下面的算法的主要思想是把每个数放到对应的位置上,即让nums[i] = i.从前往后遍历数组中的所有数,假设当前遍历到的数是nums[i]=x , 那么:如果x!=i&&nums[x]==x,则说明x出现了多次,直接返回x即可;如果nums[x]!=x那我们就把x交换到正确的位置上,即swap(nums[x], nums[i]),交换完之后如果 nums[i] != i , 则重复进行该操作。由于每次交换都会将-个数放在正确的位置上,所以swap 操作最多会进行n次,不会发生死循环。循环结束后,如果没有找到任何重复的数,则返回-1。

class Solution {
    public int findRepeatNumber(int[] nums) {
        
        for(int i = 0; i < nums.length; i ++ ) {
        //顺序不能变
        // [3, 4, 2, 1, 1, 0] 如果最后一个return的方法放在前面
        // 会返回2 ,但结果是1,产生错误,
        // 所以要放在最后判断
            if(nums[i] != i && nums[nums[i]] != nums[i]) {
                 int t = nums[i];
                 nums[i] = nums[t];
                 nums[t] = t;
             }
             if(nums[i] == i) {
                 continue;
             }

              if(nums[nums[i]] == nums[i]) {
                return nums[i];
            }
            
        }
        return -1;
    }
}

时间复杂度分析: 每次swap 操作都会将一个数放在正确的位置 上,后-次swap 会将两个数同时放到正确位置上,一只有n个数和n个位置,所以swap最多会进行n-1 次。所以总时间复杂度是O(n) 。

剑指 Offer 04. 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右 非递减 的顺序排序,每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]

给定 target = 5,返回 true。

给定 target = 20,返回 false。

方法一: 二插搜索树

(单调性扫描) O(n + m)
在m * n矩阵matrix中我们可以发现一个性质:对于每个子矩阵右上角的数x,x左边的数都小于等
于x,x下边的数都大于x。

  • 如果x大于target ,则x下边的数-定都大于target, 我们可以直接排序当前一 整列的数;
    排除一整行就是让枚举的点的横坐标加一, 排除- 整列就是让纵坐标减一 。 当我们排除完整 个矩阵后仍没有找到目
    标值时,就说明目标值不存在,返迥false。
    具体过程如下:
  • 1、初始化i = 0,j=matrix[0].size() - 1。
  • 2、如果matrix[i][j] == target 返回 true。
  • 3、如果matrix[i][j] < target ,i++ ,排除一行。
  • 4、如果matrix[i][j] > target ,j-- ,排除一列。
  • 5、如果出界还未找到target, 则返回false 。
    在这里插入图片描述

因此我们可以从整个矩阵的右上岍始枚举,假设当前枚举的数是x :

  • 如果x等于target ,则说明我们找到了目标值,返迥true ;
  • 如果x于target ,则x左边的数一定都小于target ,我们可以直接排除当前一整行的数;
  • 如果x大于target ,则x下边的数-定都大于target, 我们可以直接排序当前一 整列的数;

排除一整行就是让枚举的点的横坐标加一, 排除- 整列就是让纵坐标减一 。 当我们排除完整 个矩阵后仍没有找到目标值时,就说明目标值不存在,返迥false。
具体过程如下:

  • 1、初始化i = 0,j=matrix[0].size() - 1。
  • 2、如果matrix[i][j] == target 返回 true。
  • 3、如果matrix[i][j] < target ,i++ ,排除- 行。
  • 4、如果matrix[i][j] > target ,j-- ,排除一列。
  • 5、如果出界还未找到target, 则返回false 。

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
    // 以左下角作为二插树的根节点
       int i = matrix.length - 1;
       int j = 0;
       while(i >= 0 && j < matrix[0].length) {
           if(matrix[i][j] > target) i --;
           else if(matrix[i][j] < target) j ++ ;
           else return true;
       }
      return false;
}
   }

时间复杂度分析: 每一步排除一行或者一列, 矩阵一共有n行, m列, 最多会进行 n + m 步,所以时间复杂度是O(n + m);

方法二: 二分

模板套一套, 不懂得兄弟们可以看我的另一篇文章, 2个模板解决所有二分

第一种二分: 右边界
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
         if(matrix.length == 0 || matrix[0].length == 0) return false;
         int m = matrix.length;
         int n = matrix[0].length;
         for(int i = 0; i < m; i ++ ) {
             if(check(matrix[i], target)) return true;
         }
         return false;
    }

    private boolean check(int[] q, int target) {
        int l = 0, r = q.length - 1;
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (q[mid] <= target) l = mid;
                else r = mid - 1;
            }
            if (q[l] == target) return true;
             else return false;

    }
}

第二种二分: 左边界
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
         if(matrix.length == 0 || matrix[0].length == 0) return false;
         int m = matrix.length;
         int n = matrix[0].length;
         for(int i = 0; i < m; i ++ ) {
             if(check(matrix[i], target)) return true;
         }
         return false;
    }

    private boolean check(int[] q, int target) {
        int l = 0, r = q.length - 1;
            while (l < r) {
                int mid = l + r >> 1;
                if (q[mid] >= target) r = mid;
                else l = mid + 1;
            }
            if (q[l] == target) return true;
             else return false;

    }
}
剑指 Offer 05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1:

输入:s = “We are happy.”
输出:“We%20are%20happy.”

限制:

0 <= s 的长度 <= 10000

方式一: 字符拼接
思路
(线性扫描) O(n)
这个题在C+ +里比较好做,我们可以从前往后枚举原字符串:

  • 1、如果遇到空格,则在string类型的答案中添加"%20" ;
  • 2、如果遇到其他字符,则直接将它添加在答案中;
class Solution {
    public String replaceSpace(String s) {
       String s1 = "";
       char[] c1 = s.toCharArray();
       for(int i = 0; i < c1.length; i ++ ) {
           if(c1[i] == ' ') {
               s1 = s1 + "%20";
           } else {
               s1 += c1[i];
           }
       }
       return s1;
    }
}

时间复杂度分析: 原字符串只会被遍历常数次,所以总时间复杂度是O(n) 。

方式二 :遍历添加


class Solution {
    public String replaceSpace(String s) {
        StringBuilder res = new StringBuilder();
        for(Character c : s.toCharArray())
        {
            if(c == ' ') res.append("%20");
            else res.append(c);
        }
        return res.toString();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值