【剑指offer2】 chap2 数组

二、数组

1、基础知识

静态数组

Java 数组 | 菜鸟教程

 Arrays.XXX()

序号方法和说明
1public static int binarySearch(Object[] a, Object key)
用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
2public static boolean equals(long[] a, long[] a2)
如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
3public static void fill(int[] a, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
4public static void sort(Object[] a)
对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。

xxx[] a = b.clone()

动态数组

ArrayList:Java ArrayList | 菜鸟教程

ArrayList 中的元素实际上是对象,如果我们要存储其他类型,而 <E> 只能为引用数据类型,这时我们就需要使用到基本类型的包装类。

方法描述
add()将元素插入到指定位置的 arraylist 中
addAll()添加集合中的所有元素到 arraylist 中
clear()删除 arraylist 中的所有元素
clone()复制一份 arraylist
contains()判断元素是否在 arraylist
get()通过索引值获取 arraylist 中的元素
indexOf()返回 arraylist 中元素的索引值
removeAll()删除存在于指定集合中的 arraylist 里的所有元素
remove()删除 arraylist 里的单个元素,参数为索引值
size()返回 arraylist 里元素数量
isEmpty()判断 arraylist 是否为空
subList()截取部分 arraylist 的元素
set()替换 arraylist 中指定索引的元素
sort()Collections.sort(),可以对字符或数字列表进行排序。
toArray()将 arraylist 转换为静态数组
toString()将 arraylist 转换为字符串
ensureCapacity()设置指定容量大小的 arraylist
lastIndexOf()返回指定元素在 arraylist 中最后一次出现的位置
retainAll()保留 arraylist 中在指定集合中也存在的那些元素
containsAll()查看 arraylist 是否包含指定集合中的所有元素
trimToSize()将 arraylist 中的容量调整为数组中的元素个数
removeRange()删除 arraylist 中指定索引之间存在的元素
replaceAll()将给定的操作内容替换掉数组中每一个元素
removeIf()删除所有满足特定条件的 arraylist 元素
forEach()遍历 arraylist 中每一个元素并执行特定操作

LinkedList:Java LinkedList | 菜鸟教程

  • LinkedList 继承了 AbstractSequentialList 类。
  • LinkedList 实现了 Queue 接口,可作为队列使用。
  • LinkedList 实现了 List 接口,可进行列表的相关操作。
  • LinkedList 实现了 Deque 接口,可作为队列使用。
  • LinkedList 实现了 Cloneable 接口,可实现克隆。
  • LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
// 引入 LinkedList 类
import java.util.LinkedList; 

LinkedList<E> list = new LinkedList<E>();   // 普通创建方法
或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表

// 打印输出,格式:[x, y, z]
System.out.println(sites);
方法描述
public boolean add(E e)链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
public void add(int index, E element)向指定位置插入元素。
public boolean addAll(Collection c)将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。
public boolean addAll(int index, Collection c)将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。
public void addFirst(E e)元素添加到头部。
public void addLast(E e)元素添加到尾部。
public boolean offer(E e)向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
public boolean offerFirst(E e)头部插入元素,返回是否成功,成功为 true,失败为 false。
public boolean offerLast(E e)尾部插入元素,返回是否成功,成功为 true,失败为 false。
public void clear()清空链表。
public E removeFirst()删除并返回第一个元素。
public E removeLast()删除并返回最后一个元素。
public boolean remove(Object o)删除某一元素,返回是否成功,成功为 true,失败为 false。
public E remove(int index)删除指定位置的元素。
public E poll()删除并返回第一个元素。
public E remove()删除并返回第一个元素。
public boolean contains(Object o)判断是否含有某一元素。
public E get(int index)返回指定位置的元素。
public E getFirst()返回第一个元素。
public E getLast()返回最后一个元素。
public int indexOf(Object o)查找指定元素从前往后第一次出现的索引。
public int lastIndexOf(Object o)查找指定元素最后一次出现的索引。
public E peek()返回第一个元素。
public E element()返回第一个元素。
public E peekFirst()返回头部元素。
public E peekLast()返回尾部元素。
public E set(int index, E element)设置指定位置的元素。
public Object clone()克隆该列表。
public Iterator descendingIterator()返回倒序迭代器。
public int size()返回链表元素个数。
public ListIterator listIterator(int index)返回从指定位置开始到末尾的迭代器。
public Object[] toArray()返回一个由链表元素组成的数组。
public T[] toArray(T[] a)返回一个由链表元素转换类型而成的数组。

以下情况使用 ArrayList :

  • 频繁访问列表中的某一个元素。
  • 只需要在列表末尾进行添加和删除元素操作。

以下情况使用 LinkedList :

  • 你需要通过循环迭代来访问列表中的某些元素。
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。

2、基本题型

(1)双指针

一般数组中的数为正整数时,可以用双指针

 反向双指针

剑指 Offer II 006. 排序数组中两个数字之和

class Solution {
    //双指针
    public int[] twoSum(int[] numbers, int target) {
        int l = 0;
        int r = numbers.length - 1;
        while (l < r && numbers[l] + numbers[r] != target) {
            if (numbers[l] + numbers[r] < target) {
                l++;
            } else {
                r--;
            }
        }
        return new int[]{l, r};
    }
}

也可以用哈希表

class Solution {
    public int[] twoSum(int[] numbers, int target) {
    //哈希
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < numbers.length; i++) {
            if (map.containsKey(target - numbers[i])) {
                return new int[]{map.get(target - numbers[i]), i};
            } else {
                map.put(numbers[i], i);
            }
        }
        return new int[]{-1, -1};
    }
}

剑指 Offer II 007. 数组中和为 0 的三个数

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 用什么数据结构?动态数组
        List<List<Integer>> res = new LinkedList<>();

        //base case
        if (nums.length < 3) return res;

        //先排序 n·logn
        Arrays.sort(nums);


        //固定一个数,剩下用双指针
        for (int i = 0; i < nums.length - 2; ) {

            for (int j = i + 1, k = nums.length - 1; j < k; ) {
                if ((nums[i] + nums[j] + nums[k]) < 0) {
                    ++j;
                } else if ((nums[i] + nums[j] + nums[k]) > 0) {
                    --k;
                } else {
                    res.add(Arrays.asList(nums[i], nums[j], nums[k]));
                    //去重
                    int temp = nums[j];
                    while (nums[j] == temp && j < k) {
                        ++j;
                    }
                }
            }
            //去重
            int temp = nums[i];
            while (i < nums.length && nums[i] == temp) {
                ++i;
            }
        }
        return res;
    }
}

 同向双指针(又称为“滑动窗口”)

一般为for+while结构

剑指 Offer II 008. 和大于等于 target 的最短子数组

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int i = 0;
        int minLen = nums.length+1;
        int sum = 0;
        for (int j = 0; j < nums.length; j++) {
            sum += nums[j];
            while (i <= j && sum >= target) {
                minLen = Math.min(minLen, j - i + 1);
                sum -= nums[i++];
            }
        }
        return minLen == nums.length+1 ? 0 : minLen;
    }
}

方法二:前缀和 + 二分查找

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int[] sums = new int[n + 1]; 
        // 为了方便计算,令 size = n + 1 
        // sums[0] = 0 意味着前 0 个元素的前缀和为 0
        // sums[1] = A[0] 前 1 个元素的前缀和为 A[0]
        // 以此类推
        for (int i = 1; i <= n; i++) {
            sums[i] = sums[i - 1] + nums[i - 1];
        }
        for (int i = 1; i <= n; i++) {
            int target = s + sums[i - 1];
            int bound = Arrays.binarySearch(sums, target);
            if (bound < 0) {
                bound = -bound - 1;
            }
            if (bound <= n) {
                ans = Math.min(ans, bound - (i - 1));
            }
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/minimum-size-subarray-sum/solution/chang-du-zui-xiao-de-zi-shu-zu-by-leetcode-solutio/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer II 009. 乘积小于 K 的子数组

结构和上一道类似,注意数值范围

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        long mul = 1;
        int i = 0;
        int cnt = 0;
        for (int j = 0; j < nums.length; ++j) {
            mul *= nums[j];
            while (i <= j && mul >= k) {
                mul /= nums[i++];
            }
            cnt += (i <= j) ? j - i + 1 : 0;
        }
        return cnt;
    }
}

法二:同样可以转化为前缀和,只是这里的前缀和需要通过log运算将乘积转化为求和

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if (k == 0) {
            return 0;
        }
        int n = nums.length;
        double[] logPrefix = new double[n + 1];
        for (int i = 0; i < n; i++) {
            logPrefix[i + 1] = logPrefix[i] + Math.log(nums[i]);
        }
        double logk = Math.log(k);
        int ret = 0;
        for (int j = 0; j < n; j++) {
            int l = 0;
            int r = j + 1;
            int idx = j + 1;
            double val = logPrefix[j + 1] - logk + 1e-10;
            while (l <= r) {
                int mid = (l + r) / 2;
                if (logPrefix[mid] > val) {
                    idx = mid;
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            ret += j + 1 - idx;
        }
        return ret;
    }
}

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/subarray-product-less-than-k/solution/cheng-ji-xiao-yu-k-de-zi-shu-zu-by-leetc-92wl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(2)前缀和

若数组有可能是负数,需要使用前缀和方法

 一维前缀和+HashMap

剑指 Offer II 010. 和为 k 的子数组

class Solution {
    //前缀和解法
    public int subarraySum(int[] nums, int k) {
        //key: sum, value:count
        Map<Integer, Integer> map = new HashMap<>();
        int sum = 0;
        int count = 0;
        map.put(0, 1);
        for (int num : nums) {
            sum += num;
            count += map.getOrDefault(sum - k, 0);
            map.put(sum, map.getOrDefault(sum, 0) + 1);
        }
        return count;
    }
}

基础解法是枚举

public class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
        for (int start = 0; start < nums.length; ++start) {
            int sum = 0;
            for (int end = start; end >= 0; --end) {
                sum += nums[end];
                if (sum == k) {
                    count++;
                }
            }
        }
        return count;
    }
}

剑指 Offer II 011. 0 和 1 个数相同的子数组

和上一题完全一样,只不过上一道求个数,这一道求长度,所以map里面存放的不一样,这里只存放第一次出现的和的索引值

class Solution {
    public int findMaxLength(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        int sum = 0;
        int maxLen = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i] == 0 ? -1 : 1;
            map.putIfAbsent(sum,i);
            maxLen = Math.max(maxLen, i - map.get(sum));
        }
        return maxLen;
    }
}

剑指 Offer II 012. 左右两边子数组的和相等

class Solution {
    public int pivotIndex(int[] nums) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        int temp = 0;
        for (int i = 0; i < nums.length; i++) {
            temp += nums[i];
            if (temp - nums[i] == sum - temp) {
                return i;
            }
        }
        return -1;
    }
}

二维前缀和

剑指 Offer II 013. 二维子矩阵的和

class NumMatrix {
    private int[][] sums;

    public NumMatrix(int[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0) {
            return;
        }

        sums = new int[matrix.length + 1][matrix[0].length + 1];
        for (int i = 0; i < matrix.length; i++) {
            int rowSum = 0;
            for (int j = 0; j < matrix[0].length; j++) {
                rowSum += matrix[i][j];
                sums[i + 1][j + 1] = sums[i][j + 1] + rowSum;
            }
        }
    }

    public int sumRegion(int row1, int col1, int row2, int col2) {
        return sums[row1][col1] - sums[row1][col2 + 1] - sums[row2 + 1][col1] + sums[row2 + 1][col2 + 1];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值