数组的问题有哪些
- 排序:选择排序,插入排序,归并排序,快速排序
- 二分查找法
- 数据结构:栈,队列,堆
- 注意加,乘的时候会不会有整形溢出
使用hash表
leetcode_1 两数之和
这道题的解题思路是一边遍历一边向hash表中添加元素。在查hash表然后再在hash表中添加数据,防止结果是数组中相同下标的元素。
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target-nums[i])){
return new int[]{i,map.get(target-nums[i])};
}
// 先验证完在加入元素
map.put(nums[i],i);
}
return new int[0];
}
双指针
leetcode_11 盛最多水的容器
求最大的面积。从长度最长的2端开始遍历。在长在不断缩小的情况下,由于木桶效应,要找高度变高的边界,面积才有可能变大。所以是高度比较小的指针进行移动。
public int maxArea(int[] height) {
int left = 0, right = height.length - 1;
int maxArea = 0;
while (left < right) {
int area = Math.min(height[left], height[right]) * (right - left);
maxArea = Math.max(maxArea, area);
if (height[left] <= height[right]) {
left++;
} else {
right--;
}
}
return maxArea;
}
leetcode_283 移动零
将数组中的零移动到最后,并且非零元素的相对位置保持不变。使用2个指针left和right。left的右边和right的左边是0。可以理解成left是非零的个数,如果right的位置是非零,那么它的实际位置应该是在left上。(还需要在理解)
public void moveZeroes(int[] nums) {
int left = 0, right = 0;
while (right < nums.length - 1) {
if (nums[right] != 0) {
swap(nums, left, right);
left++;
}
right++;
}
}
public void swap(int[] nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
滑动窗口
- 滑动窗口的2个指针只能朝一个方向走,不能反复横跳。
- 滑动指针适用于连续的字串
- 滑动窗口right指针是向结果中加数据,l是从结果中减数据,l减的时候要使用while循环。我感觉438号题更能体现这个思想
leetcode_209 长度最小的子数组
先确定好每个变量的定义。left是左指针,right是右指针,[left,right]表示成立,也就是说都是闭区间。当right向右移动后条件成立,那么可以缩减窗口,left也向右移动,看条件是否成立。left移动的时候要用while循环,因为要一直移动到条件不成立,然后跳出循环。
public int minSubArrayLen(int target, int[] nums) {
int left = 0, right = 0; // left是左边的指针,right是右边的指针[left,rigth]
int sum = 0;
int minLength = Integer.MAX_VALUE;
while (right < nums.length) {
sum += nums[right];
if (sum >= target) {
while (left <= right) {
minLength = Math.min(minLength, right - left + 1);
if (sum - nums[left] < target) {
break;
}
sum = sum - nums[left];
left++;
}
}
right++;
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
leetcode_3 无重复字符的最长子串
也是定义了left,right2个指针,并且也是闭区间。这里也是使用的滑动窗口,在一个map或者数组里面right中所对应的数值,当right上的值再次出现时,那么此时最长的无重复字符串就是上次值出现和这次出现的中间的长度
public int lengthOfLongestSubstring(String s) {
if (s == null) {
return 0;
}
int[] arr = new int[256];
char[] chars = s.toCharArray();
int max = 0;
int start = 0;
int end = 0;
while (end < chars.length) {
while (arr[chars[end]] != 0) {
arr[chars[start]]--;
start++;
}
arr[chars[end]]++;
max = Math.max(max, end - start + 1);
end++;
}
return max;
}
leetcode_438 找到字符串中所有字母异位词
这个思想是我从right中一直想结果中加数据,然后判断结果中的数据是否大于需要的数据,如果大于的话,那么left指针就开始移动,一直移动到结果等于。然后判断现在的字符串长度和需要的是否一样。
public List<Integer> findAnagrams(String s, String p) {
int[] need = new int[125];
int[] result = new int[125];
for (char c : p.toCharArray()) {
need[c]++;
}
List<Integer> resultList = new ArrayList<Integer>();
int left = 0, right = 0, plen= p.length();
char[] chars = s.toCharArray();
while (right < chars.length){
result[chars[right]]++;
while (result[chars[right]] > need[chars[right]]) {
result[chars[left]]--;
left++;
}
if (right - left + 1 == plen) {
resultList.add(left);
}
right++;
}
return resultList;
}
leetcode_76 最小覆盖子串
这道题借鉴了438题的思路。这里面有2个判断条件的。当主串中的字符在子串中不存在或者多时,就认为子串中存在这个字符,并将长度增长。当子串中确实存在这个字符,子串长度不+1;
public String minWindow(String s, String t) {
int[] needArr = new int[256];
int[] resultArr = new int[256];
for (char c : t.toCharArray()) {
needArr[c]++;
}
int minLeft = -1, minRight = -1, minLength = s.length() + 1;
int left = 0, right = 0, tLen = t.length();
char[] chars = s.toCharArray();
while (right < s.length()) {
resultArr[chars[right]]++;
if (resultArr[chars[right]] > needArr[chars[right]]) {
tLen++;
}
while (right - left + 1 == tLen) {
if (tLen < minLength) {
minLeft = left;
minRight = right;
minLength = tLen;
}
if (resultArr[chars[left]] > needArr[chars[left]]) {
resultArr[chars[left]]--;
left++;
tLen--;
} else {
break;
}
}
right++;
}
return minLeft == -1 ? "" : s.substring(minLeft, minRight + 1);
}