一文通数据结构与算法之——数组+常见题型与解题策略+Leetcode经典题
Posted 尚墨1111
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文通数据结构与算法之——数组+常见题型与解题策略+Leetcode经典题相关的知识,希望对你有一定的参考价值。
2 数组
数组是一种基础数据结构,可以用来处理常见的排序和二分搜索问题,典型的处理技巧包括对撞指针、滑动窗口等,数组是数据结构中的基本模块之一。因为字符串是由字符数组形成的,所以二者是相似的。大多数面试问题都属于这个范畴。
数组之中常和双指针、二分查找、排序算法、字符串、矩阵相结合,以及分治算法
2.1 常见题型及解题策略
剑指offer 数组(共14道题目):
- 【剑指 Offer】3、数组中重复的数字(
HashSet
特点) - 【剑指Offer】4、二维数组中的查找(首尾双指针)
- 【剑指Offer】11、旋转数组的最小数字(二分查找)
- 【剑指Offer 】17、打印从1到最大的n位数(字符串加法)
- 【剑指Offer】21、调整数组顺序使奇数位于偶数前面(快排收尾双指针)
- 【剑指Offer】29、顺时针打印矩阵(打印)
- 【剑指Offer】39、数组中出现次数超过一半的数字(分治、摩尔投票法)
- 【剑指 Offer】40. 最小的k个数(排序算法、大根堆小根堆)
- 【剑指Offer】51、数组中的逆序对(归并排序)
- 【剑指 Offer】53 - I. 在排序数组中查找数字 I(二分查找缩小范围)
- 【剑指 Offer 】53 - II. 0~n-1中缺失的数字(二分查找缩小范围)
- 【剑指 Offer】 56 - I. 数组中数字出现的次数(二进制异或)
- 【剑指 Offer 】56 - II. 数组中数字出现的次数 II(二进制求和)
- 【剑指 Offer】 57、和为s的两个数字(首尾双指针)
【剑指 Offer】12、矩阵中的路径
【剑指 Offer】66、构建乘积数组
27、移除元素
26、删除有序数组中的重复项
75、颜色分类(双指针)
2.2 字符串操作基础
字符串的带参构造函数,实现传入char[]
//String(char[] ch)
new String(ch);
HashSet的带参构造函数
//HashSet(Collection<? extends E> c) 构造一个包含指定 collection 中的元素的新 set。
new HashSet<>(Arrays.asList('a','e','i','o','u','A','E','I','O','U'));
//Arrays.asList(T... a) 返回一个受指定数组支持的固定大小的 List<T>
Character数组的操作
if (Character.isLetterOrDigit(ch)) {
list.append(Character.toLowerCase(ch));
}
在字符数组中++操作的灵活运用
nums[i++] = a;//将nums[i] = a,并且i++
防止覆盖的数组后移操作
//从前往后挪的操作会导致数据的覆盖
for(int i = left;i<nums.length;i++){
nums[i+1] = nums[i];//nums[1,2,2,3,0,0]——>{1,2,2,2,2,2}
}
int last = m+n-1;
while(j>=0){
nums1[last--] = nums2[j--];
}
Arrays.copyOf()
,实现将集合转化成数组
//copyOf(int[] original, int newLength),复制指定的数组,截取或用 0 填充(如有必要),以使副本具有指定的长度
//copyOfRange(int[] original, int from, int to) ,将指定数组的指定范围复制到一个新数组。
//返回新的数组
Arrays.copyOfRange(res, 0, index+1);
ArrayList.toArray()
实现将对应的集合list转化成相同数据类型大小的数组
//list中已经存储了元素
Object[] obj = list.toArray();
//不带参数的toArray()方法,是构造的一个Object数组,然后进行数据copy
Integer[] integers = list.toArray(new Integer[list.size()]);
int[][] ans = list.toArray(new int[list.size()][]);//返回转化之后的int[][]二维数组
//带参数的toArray(T[] a) 方法,根据参数数组的类型,构造了一个对应类型的,长度跟ArrayList的size一致的数组
//源码
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
Lambda
表达式Arrays.sort
排序操作
Arrays.sort(arrays, (a, b) -> a[0] - b[0]);//谁小谁在前
//相当于
Arrays.sort(arrays, new Comparator<int[]>() {
@Override
public int compare(int[] a, int[] b) {
return a[0]-b[0];
}
});
2.3 删除数组元素
- 掌握数组删除元素的直接覆盖操作
- 双指针法
2.3.1 题库列表
27、移除元素 (快慢指针)
26、删除有序数组中的重复项 (快慢指针)
75、颜色分类(双指针,三色旗,小米笔试)
// 快慢指针移除元素
/**
* 27.移除元素
* 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于
* val 的元素,并返回移除后数组的新长度。
* @param arr
*/
public int remove(int[] arr,int val){
int slow = 0;
for (int fast = 0; fast < arr.length; fast++) {
if(arr[fast]!=val){
arr[slow] = arr[fast];
slow++;
}
}
return slow;
}
/**
* 26. 原地删除有序数组中的所有重复项
* 输入:nums = [1,1,2]
* 输出:2, nums = [1,2]
*
* 输入:nums = [0,0,1,1,1,2,2,3,3,4]
* 输出:5, nums = [0,1,2,3,4]
* @param arr
* @return
*/
public int removeDup(int[] arr){
int slow= 0;
for (int fast = 1; fast < arr.length; fast++) {
if(arr[slow]!=arr[fast]){
arr[++slow] = arr[fast];
}
}
return slow+1;
}
75、颜色分类
采用双指针:加深理解指针确定位置进行交换的操作
public void sortColors(int[] nums) {
int p = 0;//作为0的分割指针
for (int i = 0; i < nums.length; ++i) {//与i++功能一致但是性能相对更好
if(nums[i]==0){
int temp = nums[i];
nums[i] = nums[p];
nums[p] = temp;
++p;
}
}
// 2.在从p开始重现交换1 和 2 的位置
for (int i = p; i < nums.length; ++i) {
if(nums[i]==1){
int temp = nums[i];
nums[i] = nums[p];
nums[p] = temp;
++p;
}
}
}
2.4 双指针技巧
2.4.1 题库列表
88. 合并两个有序数组:如何将数组所有元素整体后移,防止数组覆盖?
167. 两数之和 II - 输入有序数组(有序数列的首尾双指针)
11. 盛最多水的容器:经典题目
209. 长度最小的子数组:滑动窗口
88 、合并两个有序数组
//逆向双指针解决合并两个排序数组
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m-1;
int j = n-1;
int last = m+n-1;
while(i>=0 && j>=0){
if(nums1[i]>nums2[j]){
nums1[last--] = nums1[i--];
}else{
nums1[last--] = nums2[j--];
}
}
// 剩下的部分直接复制过去
while(j>=0){
nums1[last--] = nums2[j--];
}
}
167 、两数之和 II - 输入有序数组
//给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target
/**
* 167. 两数之和 II - 输入有序数组
* 递增数列,输出何为目标值的下标
* @param numbers
* @param target
* @return
*/
public int[] twoSum(int[] numbers, int target) {
int left=0,right=numbers.length-1;
while(left<right){
int sum = numbers[left]+numbers[right];
if(sum==target) {
return new int[]{left+1,right+1};
}else if(sum<target){
left++;
}else{
right--;
}
}
return null;
}
125 、验证回文串
// "A man, a plan, a canal: Panama"——>只考虑字母和数字字符,忽略字母的大小写。
public boolean isPalindrome(String s) {
//1.字符串预处理
StringBuilder list = new StringBuilder();
int length = s.length();
for (int i = 0; i < length; i++) {
char ch = s.charAt(i);
if (Character.isLetterOrDigit(ch)) {
list.append(Character.toLowerCase(ch));
}
}
// 2.双指针遍历是否一致
int i = 0;
int j = list.length()-1;
while(i<=j){
if(list.charAt(i)!=list.charAt(j)){
return false;
}else{
i++;
j--;
}
}
return true;
}
345 、反转字符串中的元音字母
//1.将元音字符先存起来
private final static HashSet<Character> vowel = new HashSet<>(
Arrays.asList('a','e','i','o','u','A','E','I','O','U')
);
public String reverseVowels(String s) {
char[] ch = new char[s.length()];
//1.双指针前后遍历字符串
int left = 0;
int right = s.length()-1;
while(left<=right){
char cleft = s.charAt(left);
char cright = s.charAt(right);
//如果是非元音字符,则直接添加到新的字符数组
if(!vowel.contains(cleft)){
ch[left++]=cleft;
}else if(!vowel.contains(cright)){
ch[right--] = cright;
}else{
//如果是元音字符,则交换位置放
ch[left++] = cright;
ch[right--] = cleft;
}
}
return new String(ch);
}
11 、 盛最多水的容器
public int maxArea(int[] height) {
// 1.思路:双指针,移动小的那边
int left = 0,right = height.length-1;
int res = 0;
while(left<right){
res = height[left] < height[right] ?
Math.max(res, (right - left) * height[left++]):
Math.max(res, (right - left) * height[right--]);
}
return res;
}
209 、长度最小的子数组
//我们把数组中的右指针右移,直到总和大于等于 target 为止,记录个数。然后左指针右移,直到队列中元素的和小于 target为止,记录个数。重复,直到右指针到达队尾。
public int minSubArrayLen(int target, int[] nums) {
int i = 0,j=0,sum=0;
int res = Integer.MAX_VALUE;
while(j<nums.length){
//1.右指针滑动
sum+=nums[j++];
while(sum>=target){
//2.固定右指针,滑动左指针,求最小的子数组
res = Math.min(res,j-i);
sum-=nums[i++];
}
}
return res==Integer.MAX_VALUE?0:res;
}
2.5 数组类操作
56 、合并区间
//以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
public int[][] merge(int[][] intervals) {
// 1.创建一个集合存储答案
List<int[]> ans = new ArrayList<>();
// 2.判空直接返回
if (intervals == null || intervals.length == 0){
return ans.toArray(new int[0][]);
}
// 3.将集合按数组一维进行排序
Arrays.sort(intervals,(a,b)->{a[0]-b[0]});
for (int i = 0; i < intervals.length; i++) {
int start = intervals[i][0];
int end = intervals[i][1];
// 4.如ans中最后一个元素的end>遍历元素的start,要进行合并操作
if(ans.size()==0 ||ans.get(ans.size()-1)[1]<start){
ans.add(intervals[i]);
}else{
ans.get(ans.size()-1)[1] = Math.max(ans.get以上是关于一文通数据结构与算法之——数组+常见题型与解题策略+Leetcode经典题的主要内容,如果未能解决你的问题,请参考以下文章
一文通数据结构与算法之——链表+常见题型与解题策略+Leetcode经典题
一文通数据结构与算法之——回溯算法+常见题型与解题策略+Leetcode经典题
一文通数据结构与算法之——图+常见题型与解题策略+Leetcode经典题