剑指offer刷题——数组和字符串

文章目录

剑指刷题——数组和字符串

1.有序数组 一定第一时间想到二分法(11、21、53)

2.超过一半的某数 ——摩尔投票法(39)

3.有序求所有指定和——滑动窗口(57II)

面试题4. 二维数组中的查找

在一个 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。

限制:

0 <= n <= 1000

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if(matrix.length == 0)
            return false;

        int x = 0;
        int y = matrix.length - 1;

        while(x < matrix[0].length && y >= 0){
            if(matrix[y][x] > target) {
                y--;
            } else if(matrix[y][x] < target) {
                x++;
            } else {
                return true;
            }
        }

        return false;
    }
}

剑指 Offer 05. 替换空格

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

示例 1:

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

限制:

0 <= s 的长度 <= 10000

class Solution {
    public String replaceSpace(String s) {
        return s.replace(" ","%20");
    }
}

class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0 ; i < s.length(); i++){
            char ch = s.charAt(i);
            if(ch == ' ') {
                sb.append("%20");
            }
            else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }
}

两种都可以

剑指 Offer 11. 旋转数组的最小数字

难度简单

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2][1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2]
输出:1

示例 2:

输入:[2,2,2,0,1]
输出:0

注意:本题与主站 154 题相同:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/

//暴力
class Solution {
    public int minArray(int[] numbers) {
        for (int i = 1; i < numbers.length; i++) {
            if(numbers[i]<numbers[0]){
                return numbers[i];
            }
        }
        return numbers[0];
    }
}

//二分法
class Solution {
    public int minArray(int[] numbers) {
        int left = 0;
        int right = numbers.length-1;
        while(left<right){
            int mid = (left+right)/2;
            if(numbers[mid]<numbers[right]){
                right = mid;
            }else if( numbers[mid]>numbers[right]){
                left = mid+1;
            }else{
                right--;
            }
        }
        return numbers[left];
    }
}
剑指 Offer 17. 打印从1到最大的n位数

难度简单

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

示例 1:

输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]

说明:

  • 用返回一个整数列表来代替打印
  • n 为正整数
class Solution {
    public int[] printNumbers(int n) {
        int[] arr = new int[(int) Math.pow(10, n)-1];
        for (int i = 0; i < arr.length; i++) {
            arr[i]=i+1;
        }
        return arr;
    }
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

难度简单

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

提示:

  1. 1 <= nums.length <= 50000
  2. 1 <= nums[i] <= 10000

通过次数75,260

提交次数117,255

class Solution {
    public int[] exchange(int[] nums) {
        int left = 0;
        int right = nums.length-1;
        int temp = 0;
        while(left<right){

            if(nums[left]%2 == 0 && nums[right]%2!=0){
                temp = nums[left];
                nums[left]=nums[right];
                nums[right]=temp;
                left++;
                right--;
            }
            if(nums[left]%2 != 0){
                left++;
            }
            if(nums[right]%2 ==0){
                right--;
            }
        }
        return nums;

    }
}

//方法二 更简洁
class Solution {
    public int[] exchange(int[] nums) {
        int start = 0;
        int end = nums.length - 1;
        while(start < end) {
            while(start < end && (nums[start] % 2) == 1) {
                start++;
            }
            while(start < end && (nums[end] % 2) == 0) {
                end--;
            }
            int tmp = nums[start];
            nums[start] = nums[end];
            nums[end] = tmp;
        }
        return nums;
    }
}
剑指 Offer 29. 顺时针打印矩阵

难度简单

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

限制:

  • 0 <= matrix.length <= 100
  • 0 <= matrix[i].length <= 100

注意:本题与主站 54 题相同:https://leetcode-cn.com/problems/spiral-matrix/

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        if(matrix.length == 0) return new int[0];
        int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1, x = 0;
        int[] res = new int[(r + 1) * (b + 1)];
        while(true) {
            for(int i = l; i <= r; i++) res[x++] = matrix[t][i]; // left to right.
            if(++t > b) break;
            for(int i = t; i <= b; i++) res[x++] = matrix[i][r]; // top to bottom.
            if(l > --r) break;
            for(int i = r; i >= l; i--) res[x++] = matrix[b][i]; // right to left.
            if(t > --b) break;
            for(int i = b; i >= t; i--) res[x++] = matrix[i][l]; // bottom to top.
            if(++l > r) break;
        }
        return res;
    }
}
剑指 Offer 39. 数组中出现次数超过一半的数字

难度简单

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

限制:

1 <= 数组长度 <= 50000

注意:本题与主站 169 题相同:https://leetcode-cn.com/problems/majority-element/

class Solution {
    public int majorityElement(int[] nums) {
        int cur = 0;
        int count = 0;
        for(int num:nums){
            if(count == 0) {   
                cur = num;
            }
            if(num == cur){
                count++;
            }else{
                count--;
            }
        }
        return cur;    
    }
}

摩尔投票法最优

其他方法:1.排序后取中数 2.HashMap

剑指 Offer 53 - II. 0~n-1中缺失的数字

难度简单

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

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

示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

限制:

1 <= 数组长度 <= 10000
//二分法
class Solution {
    public int missingNumber(int[] nums) {
        int left = 0;
        int right = nums.length-1;
        while(left<=right){			//注意这个临界值的等号
            int mid = (left+right)/2;
            if(nums[mid]==mid){
                left = mid+1;
            }else{
                right = mid-1;
            }
        }
        return left;
    }
}
剑指 Offer 53 - I. 在排序数组中查找数字 I

难度简单

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

限制:

0 <= 数组长度 <= 50000

**注意:**本题与主站 34 题相同(仅返回值不同):https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/

class Solution {
    public int search(int[] nums, int target) {
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i]==target){
                count++;
            }
        }
        return count;
    }
}

//二分法
class Solution {
    public int search(int[] nums, int target) {
        return getRightMargin(nums, target) - getRightMargin(nums, target - 1);
    }
    int getRightMargin(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while(left <= right) {
            int mid = (left + right) / 2;
            if(nums[mid] <= target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
}
剑指 Offer 57. 和为s的两个数字

难度简单

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]

示例 2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]

限制:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^6
//普通算法超时,所以用双指针
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int left = 0;
        int right = nums.length-1;
        while(left<right){
            if(nums[left]+nums[right]<target){
                left++;
            }else if(nums[left]+nums[right]>target){
                right--;
            }else{
                return new int[]{nums[left],nums[right]};
            }
        }
        return new int[0];
    }
}
剑指 Offer 57 - II. 和为s的连续正数序列

难度简单

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

输入:target = 9
输出:[[2,3,4],[4,5]]

示例 2:

输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]

限制:

  • 1 <= target <= 10^5
//滑动窗口
class Solution {
    public int[][] findContinuousSequence(int target) {
        int i = 1;
        int j = 1;
        int sum = 0;
        List<int[]> res = new ArrayList<>();
        while(i<=target/2){
            if(sum<target){
                sum += j;   
                j++;        //右边界右移
            }else if(sum>target){
                sum -= i ;  
                i++;        //左边界右移
            }else{
                int[] arr = new int[j-i];
                for (int k = i; k < j; k++) {
                    arr[k-i]=k;
                }
                res.add(arr);   
                sum -=i;
                i++;        //左边界右移   
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}

滑动窗口 只向右移
注意左右边界控制 左闭右开

剑指 Offer 58 - I. 翻转单词顺序

难度简单

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。

示例 1:

输入: "the sky is blue"
输出: "blue is sky the"

示例 2:

输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:

输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

说明:

  • 无空格字符构成一个单词。
  • 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
  • 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

**注意:**本题与主站 151 题相同:https://leetcode-cn.com/problems/reverse-words-in-a-string/

**注意:**此题对比原题有改动

//暴力
class Solution {
    public String reverseWords(String s) {
        String[] strArr = s.trim().split("\\s+");
        StringBuilder str = new StringBuilder();
        for (int i = strArr.length - 1; i >= 0; i--) {
            str.append(strArr[i]);
            str.append(" ");
        }
        return str.toString().trim();
    }
}
//双指针
class Solution {
    public String reverseWords(String s) {
        s = s.trim();
        int i = s.length()-1;
        int j = i;
        StringBuilder ss = new StringBuilder("");
        while(i>=0){
            while(i>=0 && s.charAt(i)!=' ') i--; //找第一个空格
            ss.append(s.substring(i+1, j+1)+" ");//注意左闭右开
            while(i>=0 && s.charAt(i)==' ') i--; //跳过单词的空格
            j=i;
        }
        return ss.toString().trim();
    }
}
//面试不建议使用 还是用双指针
class Solution {
    public String reverseWords(String s) {
        String[] strs = s.trim().split(" "); // 删除首尾空格,分割字符串
        StringBuilder res = new StringBuilder();
        for(int i = strs.length - 1; i >= 0; i--) { // 倒序遍历单词列表
            if(strs[i].equals("")) continue; // 遇到空单词则跳过
            res.append(strs[i] + " "); // 将单词拼接至 StringBuilder
        }
        return res.toString().trim(); // 转化为字符串,删除尾部空格,并返回
    }
}
//同上
class Solution {
    public String reverseWords(String s) {
        s= s.trim();
        String[] ss = s.split(" ");
        StringBuilder str = new StringBuilder();
        for (int i =ss.length-1; i >=0; i--) {
            if(!ss[i].equals("")){
                str.append(ss[i]+" ");
            }
        }
        return str.toString().trim();
    }
}

切分一个或多个空格

剑指 Offer 58 - II. 左旋转字符串

难度简单

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

示例 1:

输入: s = "abcdefg", k = 2
输出: "cdefgab"

示例 2:

输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"

限制:

  • 1 <= k < s.length <= 10000
class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n, s.length()) + s.substring(0, n);
    }
}
剑指 Offer 66. 构建乘积数组

难度中等

给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

示例:

输入: [1,2,3,4,5]
输出: [120,60,40,30,24]

提示:

  • 所有元素乘积之和不会溢出 32 位整数
  • a.length <= 100000
//笨方法
class Solution {
    public int[] constructArr(int[] a) {
        int[] arr = new int[a.length];
        int t = 1;
        int count = 0;
        for (int i = 0; i < arr.length; i++) {
            if(a[i]!=0){
                t*=a[i];
            }else{
                count++;
            }
        }
        if(count == 0){
            for (int i = 0; i < arr.length; i++) {
                arr[i]=t/a[i];
            }
        }if(count == 1){
            for (int i = 0; i < arr.length; i++) {
                if(a[i]==0){
                    arr[i]=t;
                }else{
                    arr[i]=0;
                }
            }
        }
        return arr;
    }
}
//正三角与倒三角
class Solution {
    public int[] constructArr(int[] a) {
        if(a.length == 0) return new int[0];
        int[] b = new int[a.length];
        b[0] = 1; //注意初始化
        int tmp = 1;
        for(int i = 1; i < a.length; i++) { //下三角从上到下
            b[i] = b[i - 1] * a[i - 1];
        }
        for(int i = a.length - 2; i >= 0; i--) { //上三角从下到上
            tmp *= a[i + 1];
            b[i] *= tmp;
        }
        return b;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值