数组和字符串相关

目录

数组

基础知识

常见题目

寻找数组的中心索引

搜索插入位置

合并区间

合并两个无序数组并去重排序

字符串

常见题目

最长公共前缀

最长回文子串

双指针

基础知识

常见题目

长度最小的子数组

二分法

模板一

常见题目

x的平方根

搜索排序旋转数组

模板二

常见题目

旋转排序数组中的最小值


数组

基础知识

数组会用一些名为 索引 的数字来标识每项数据在数组中的位置,且在大多数编程语言中,索引是从 0 算起的。我们可以根据数组中的索引,快速访问数组中的元素。

常见的语言中,C++和Java中 数组的元素类型必须保持一致,但是python中可以不同,是list。

其次,数组中的元素在内存中是连续存储的,每个元素占有相同大小的内存空间。

列表中是没有索引的,这是数组和列表的最大区别。

常见题目

寻找数组的中心索引

给你一个整数数组 nums ,请计算数组的 中心下标 。数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 

  • 思路
    • 走到我这里,我左面的和等于剩下数字的累加和,就满足当前的需求 
    public int pivotIndex(int[] nums) {
        if(nums == null || nums.length == 0)
            return -1;
        int sum = 0;
        for(int i = 0; i < nums.length; i ++){
            sum += nums[i];
        }
        int leftSum = 0;
        for(int i = 0; i < nums.length; i ++){
            if(leftSum * 2 == (sum - nums[i]))
                return i;
            else
                leftSum += nums[i];
        }
        return -1;
    }

搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

  • 思路
    • 很明显,要求二分法。不过有一个需要注意的就是边界值!!!当小于最左或者大于最右的时候考虑好!!
class Solution {
    public int searchInsert(int[] nums, int target) {
        if(nums == null || nums.length == 0)
            return -1;
        if(target <= nums[0])
            return 0;
        if(target > nums[nums.length - 1])
            return nums.length;
        if(target == nums[nums.length - 1])
            return nums.length - 1;
        int left = 0;
        int right = nums.length - 1;
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            }
            // 这里面不会有越界的情况 因为已经在前面判断过了
            if(target > nums[mid] && target < nums[mid + 1]){
                return mid + 1;
            }
            if(nums[mid] > target){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        return -1;
    }
}

合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

  • 思路
    • 先对二维数组的0位置元素作为参照,进行升序排序,以进行区间之间的聚合
    • 如果当前位置的[1]元素大等于后面的[0]位置元素,就需要进行合并
    • ⚠️!!!:需要把扩在一起的区间们的[1]做个比较,让最大的来守护大家
      • 就比如说:[[1, 10], [2, 3], [4, 5]] 如果只是用当前的和下一个比的话,那么3比4小,就会得出[[1, 10], [4, 5]]这样的结果,[4, 5]没有被囊括进来,所以需要一个变量来迭代这个守护线
class Solution {
    public int[][] merge(int[][] intervals) {
        if(intervals == null || intervals.length == 0)
            return null;
        Arrays.sort(intervals, (o1, o2) -> {
            if(o1[0] == o2[0])
                return o1[1] - o2[1];
            else
                return o1[0] - o2[0];
        });
        List<int[]> res = new ArrayList<>();
        int i = 0;
        while(i < intervals.length){
            int[] list = new int[2];
            list[0] = intervals[i][0];
            // 这个tmp就是守护线
            int tmp = intervals[i][1];
            // 下面比较的时候,比较的也是tmp,比tmp小的就进来
            while((i < intervals.length - 1) && (tmp >= intervals[i + 1][0])){
                i ++;
                // 在这里迭代
                tmp = Math.max(tmp, intervals[i][1]);
            }
            list[1] = tmp;
            i ++;
            res.add(list);
        }
        return res.toArray(new int[res.size()][2]);
    }
}

合并两个无序数组并去重排序

题目描述:两个无序的数组,请合并他们并去重排序

思路:

  1. 对两个数组分别排序,把排序方法抽出为一个方法
  2. 再合并到一个列表里,注意去重

代码实现:

public Integer[] generateOneArray(Integer[] array1, Integer[] array2){
        if(array1 != null && array1.length > 0){
            //排序
            sort(array1, 0, array1.length - 1);
        }
        if(array2 != null && array2.length > 0){
            //排序
            sort(array2, 0, array2.length - 1);
        }
        System.out.println(Arrays.toString(array1));
        System.out.println(Arrays.toString(array2));
        List<Integer> res = new ArrayList<>();
        Integer index1 = 0, index2 = 0;
        while(index1 < array1.length && index2 < array2.length){
            // 先放入小的
            if(array1[index1] < array2[index2]){
                if(! res.contains(array1[index1]))
                    res.add(array1[index1 ++]);
                else
                    index1 ++;
            }else{
                if(! res.contains(array2[index2]))
                    res.add(array2[index2 ++]);
                else
                    index2 ++;
            }
        }
        while(index1 < array1.length){
            if(! res.contains(array1[index1]))
                res.add(array1[index1 ++]);
            else
                index1 ++;
        }
        while(index2 < array2.length){
            if(! res.contains(array2[index2]))
                res.add(array2[index2 ++]);
            else
                index2 ++;
        }
        return res.toArray(new Integer[res.size()]);
    }
    private void sort(Integer[] array, Integer left, Integer right){
        Integer mid = left + (right - left) / 2;
        if(left < right){
            sort(array, left, mid);
            sort(array, mid + 1, right);
            merge(array, left, mid, right);
        }
    }
    private void merge(Integer[] array, Integer left, Integer mid, Integer right){
        Integer[] temp = new Integer[right - left + 1];
        Integer index1 = left, index2 = mid + 1;
        Integer k = 0;
        while(index1 <= mid && index2 <= right){
            if(array[index1] < array[index2]){
                temp[k ++] = array[index1 ++];
            }else{
                temp[k ++] = array[index2 ++];
            }
        }
        while(index1 <= mid){
            temp[k ++] = array[index1 ++];
        }
        while(index2 <= right){
            temp[k ++] = array[index2 ++];
        }
        for(int j = 0; j < temp.length; j ++){
            array[left + j] = temp[j];
        }
    }

字符串

常见题目

最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""

  •  思路
    • 从前向后迭代,挨个比较两个字符串的公共前缀
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null || strs.length < 1)
            return "";
        if(strs.length == 1)
            return strs[0];
        String res = strs[0];
        int i = 1;
        while(i < strs.length){
            int j = res.length();
            int k = strs[i].length();
            res = res.substring(0, Math.min(j, k));
            for(int p = 0; p < Math.min(j, k); p ++){
                if(res.charAt(p) != strs[i].charAt(p)){
                    res = res.substring(0, p);
                    break;
                }
            }
            i ++;
        }
        return res;
    }
}

最长回文子串

  • 思路
    • 先把给定的字符串翻转
    • 对两个字符串进行比较,通过dp,迭代相等的子串的大小
      • 当子串长度大于当前的最大长度的时候,需要判断「翻转字符串的镜面对称 + 当前相等子串的长度 == i的位置」,再进行迭代
class Solution {
    public String longestPalindrome(String s) {
        if(s == null || s.length() == 0)
            return "";
        String revers = new StringBuilder(s).reverse().toString();
        int[] arr = new int[s.length()];
        int maxLen = 0;
        int endX = 0;
        for(int i = 0; i < s.length(); i++){
            for(int j = s.length() - 1; j >= 0; j --){
                if(s.charAt(i) == revers.charAt(j)){
                    if(j == 0){
                        arr[j] = 1;
                    }else{
                        arr[j] = arr[j - 1] + 1;
                    }
                }else{
                    arr[j] = 0;
                }
                if(arr[j] > maxLen){
                    int before = s.length() - 1 - j;
                    if(before + arr[j] - 1 == i){
                        maxLen = arr[j];
                        endX = i;
                    }
                }
            }
        }
        return s.substring(endX - maxLen + 1, endX + 1);
    }
}

双指针

基础知识

一般是两种方式:

  • 相反的方向,同样的速度,直到相遇/到达两边的边界
  • 相同的方向,不同的速度,快指针向前走,满足一定条件的时候慢指针才更新

常见题目

长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

  •  思路
    • 快指针先移动,当当前的累加和超过target的时候,计算左右边界的区间大小
      • 这个时候,左指针向前走(缩减当前的累加和),当当前的累加和还是超过target,左指针继续向前走,缩小左右区间的大小
      • 当小于target的时候,右指针开始向后移动,继续找到新的满足条件的区间大小
  • 代码
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        if(nums == null || nums.length == 0 || target <= 0)
            return -1;
        int res = Integer.MAX_VALUE;
        int slower = 0, faster = 0;
        int cur = 0;
        while(faster < nums.length){
            // 叠加累加和
            cur = cur + nums[faster];
            while(cur >= target){
                res = Math.min(res, faster - slower + 1);
                // 缩减累加和
                cur = cur - nums[slower];
                // 左指针向后移动,减小当前的区间
                slower ++;
            }
            // 右指针向后移动
            faster ++;
        }
        return res == Integer.MAX_VALUE ? 0 : res;
    }
}

二分法

模板一

  • 初始条件:left = 0, right = length-1

  • 终止:left > right

  • 向左查找:right = mid-1

  • 向右查找:left = mid+1

常见题目

x的平方根

  • 题目
    • 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

  • 思路

    • 二分法,迭代计算mid*mid, 比较和x的大小

    • 两个注意事项:

      • 第一个:乘积溢出

      • 第二个:循环截止的条件是left>right,所以遇到小数的时候,应该返回right,right是退去小数后的整数

  • 代码

class Solution {
    public int mySqrt(int x) {
        if(x < 2)
            return x;
        int left = 1, right = x;
        while(left <= right){
            // 一定要以这种方式,left+right/2 的方式会造成溢出
            int mid = left + (right - left) / 2;
            long res = (long)mid * mid;
            if(res == x){
                return mid;
            }else if(res < x){
                left = mid + 1;
            }else
                right = mid - 1;
        }
        return right;
    }
}

搜索排序旋转数组

  • 题目
    • 整数数组 nums 按升序排列,数组中的值 互不相同 。在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

      你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

  • 思路

    • 二分法

    • 要么左半边有序,要么右半边有序,以这个角度去搜索

  • 代码

class Solution {
    public int search(int[] nums, int target) {
        if(nums == null)
            return -1;
        int left = 0, right = nums.length - 1;
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] >= nums[left]){
                if((nums[mid] > target) && (target >= nums[left])){
                    right = mid - 1;
                }else{
                    left = mid + 1;
                }
            }else{
                // 右边递增
                if((nums[mid] < target) && (target <= nums[right])){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
}

模板二

  • 初始条件:left = 0, right = length
  • 终止:left == right
  • 向左查找:right = mid
  • 向右查找:left = mid+1

常见题目

旋转排序数组中的最小值

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

  • 思路
    • log(n)很明显二分
    • 然后如果中间的元素大于后面的元素,很明显甩过去了;否则还在前面
  • 代码
class Solution {
    public int findMin(int[] nums) {
        if(nums == null || nums.length == 0)
            return -1;
        if(nums.length == 1)
            return nums[0];
        int left = 0, right = nums.length - 1;
        while(left < right){
            int mid = left + (right - left) / 2;
            // 说明肯定在右面
            if(nums[mid] > nums[right]){
                left = mid + 1;
            }else{ //当前的位置也需要考虑
                right = mid;
            }
        }
        return nums[right];
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值