数组类算法
数组类算法 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台
移动零
解法1
双指针(快慢指针)
- 快慢指针初始化均指向0
- 快指针遍历数组
- 如果碰到非0的数则向慢指针所指向的位置赋值,并且慢指针自加
- 遍历完成后,对慢指针指向位置之后的位置赋0
class Solution {
public void moveZeroes(int[] nums) {
int left = 0;
int right;
for (right = 0; right < nums.length; right++) {
if (nums[right] != 0) {
nums[left] = nums[right];
left++;
}
}
for (int i = left; i < nums.length; i++) {
nums[i] = 0;
}
}
}
移除元素
解法1
双指针(快慢指针)
- 创建两个指针
- 循环为原数组赋值,如果碰到快指针的值等于val,则跳过
- return慢指针的值
class Solution {
public int removeElement(int[] nums, int val) {
int slow = 0;
int fast;
for (fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) {
nums[slow++] = nums[fast];
}
}
return slow;
}
}
删除排序数组中的重复项
解法1
双指针(快慢指针)
- 快慢指针初始指向数组第一个元素
- 快指针对数组进行循环
- 如果碰到快慢指针所指向的元素不一致,则慢指针自加1,然后把快指针的值赋给慢指针指向的位置
- 返回慢指针自加1后的整数
class Solution {
public int removeDuplicates(int[] nums) {
int left = 0;
int right;
for (right = 0; right < nums.length; right++) {
if (nums[right] != nums[left]) {
left++;
nums[left] = nums[right];
}
}
return ++left;
}
}
删除排序数组中的重复项Ⅱ
解法1
- 快慢指针
- fast和slow初始指向下标2(前俩数肯定不重复)
- fast遍历数组
- 判断fast指向的数字和slow指向的数字前两个数字是否相等
- 不相等则赋值(如果连续三个相等数,会导致slow前俩数字指向的结果相等)
- 返回slow
class Solution {
public int removeDuplicates(int[] nums) {
int slow = 2;
int fast;
for (fast = 2; fast < nums.length; fast++) {
if (nums[fast] != nums[slow - 2]) {
nums[slow++] = nums[fast];
}
}
return slow;
}
}
颜色分类
解法1
写着玩😂
class Solution {
public void sortColors(int[] nums) {
Arrays.sort(nums);
}
}
解法2
- 遍历把0移到左边,把2移到右边
class Solution {
public void sortColors(int[] nums) {
int left = 0;
int right = nums.length - 1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
int temp = nums[i];
nums[i] = nums[left];
nums[left] = temp;
left++;
} else if (nums[i] == 2) {
int temp = nums[i];
nums[i] = nums[right];
nums[right] = temp;
right--;
i--;
}
if (i >= right) {
break;
}
}
}
}
数组中的第k个最大元素
解法1
- sort
class Solution {
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length - k];
}
}
解法2
- 用一个长度为20001的数组来计数
- 反向寻找
class Solution {
public int findKthLargest(int[] nums, int k) {
int[] xxxx = new int[20001];
for (int i : nums) {
xxxx[i + 10000]++;
}
for (int i = 20000; i >= 0; i--) {
k -= xxxx[i];
if (k <= 0) {
return i - 10000;
}
}
return -1;
}
}
合并两个有序数组
解法1
- 尾插法
- m和n向前遍历,大的插入nums1的最后
- 循环结束,将nums1和nums2剩余的数组插入nums1前面位置
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int end = nums1.length - 1;
m -= 1;
n -= 1;
while (m >= 0 && n >= 0) {
if (nums1[m] > nums2[n]) {
nums1[end--] = nums1[m--];
} else {
nums1[end--] = nums2[n--];
}
}
while (m >= 0) {
nums1[end--] = nums1[m--];
}
while (n >= 0) {
nums1[end--] = nums2[n--];
}
}
}
两数之和Ⅱ-输入有序数组
解法1
暴力循环
- 遍历整个数组
- 如果两个数相加等于target,返回下标
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] result = new int[2];
for (int i = 0; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] == target) {
result[0] = i + 1;
result[1] = j + 1;
return result;
}
}
}
return result;
}
}
解法2
双指针
- 由于数组是从小到大排列的
- 一个指针指向数组头,零一个指针指向数组末尾,进行循环判断,两个指针指向的数的和是否等于target
- 如果相等,则返回下标
- 如果大于target,则后面的指针-1
- 如果小于target,则前面的指针+1
class Solution {
public int[] twoSum(int[] numbers, int target) {
int i = 0;
int j = numbers.length - 1;
int[] result = new int[2];
while (i < j) {
if (numbers[i] + numbers[j] == target) {
result[0] = i + 1;
result[1] = j + 1;
return result;
} else if (numbers[i] + numbers[j] > target) {
j--;
} else {
i++;
}
}
return result;
}
}
验证回文串
解法1
- 字符串的replaseAll方法,正则表达式匹配替换
- toLowerCase方法转小写
- 放入插入数组
- 双指针分别从前后遍历
- 如果不相等返回false,否则返回true
class Solution {
public boolean isPalindrome(String s) {
s = s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
char[] c = s.toCharArray();
int left = 0;
int right = c.length - 1;
while (left < right) {
if (c[left] != c[right]) {
return false;
}
left++;
right--;
}
return true;
}
}
解法2
- 解法1的基础上优化
- 不使用replaceAll方法,使用isLetterOrDigit方法,如果碰到非数字字母跳过
- 如果前后不相等返回false,否则返回true
class Solution {
public boolean isPalindrome(String s) {
char[] c = s.toCharArray();
int left = 0;
int right = c.length - 1;
while (left < right) {
if (!Character.isLetterOrDigit(c[left])) {
left++;
continue;
}
if (!Character.isLetterOrDigit(c[right])) {
right--;
continue;
}
if (Character.toLowerCase(c[left]) != Character.toLowerCase(c[right])) {
return false;
}
left++;
right--;
}
return true;
}
}
反转字符串中的元音字母
解法1
- 正向循环,将所有元音字母取出来放到一个数组
- 反向循环,将所有元音字母按数组顺序替换掉
- 返回字符串
class Solution {
public String reverseVowels(String s) {
char[] c = s.toCharArray();
char[] temp = new char[c.length];
int index = 0;
for (int i = 0; i < c.length; i++) {
if (c[i] == 'a' || c[i] == 'e' || c[i] == 'i' || c[i] == 'o' || c[i] == 'u' || c[i] == 'A' || c[i] == 'E' || c[i] == 'I' || c[i] == 'O' || c[i] == 'U') {
temp[index++] = c[i];
}
}
index = 0;
for (int i = c.length - 1; i >= 0; i--) {
if (c[i] == 'a' || c[i] == 'e' || c[i] == 'i' || c[i] == 'o' || c[i] == 'u' || c[i] == 'A' || c[i] == 'E' || c[i] == 'I' || c[i] == 'O' || c[i] == 'U') {
c[i] = temp[index++];
}
}
return new String(c);
}
}
盛水最多的容器
解法1
- 能盛多少水和最短板有关
- 那么就固定高度为最短板的长度,向中间循环
- 遍历所有情况,如果遇到更多的水,就替换result记录的结果
- 返回result
class Solution {
public int maxArea(int[] height) {
int left = 0;
int right = height.length - 1;
int tall = 0;
int len = 0;
int result = 0;
while (left < right) {
len = right - left;
tall = height[left] < height[right] ? height[left++] : height[right--];
if (len * tall > result) {
result = len * tall;
}
}
return result;
}
}
长度最小的子数组
解法1
双指针(快慢指针)
- 快指针遍历数组,sum跟随快指针遍历加每个元素
- 如果sum大于target,则快指针固定,开始向右移动慢指针
- 跟随慢指针的移动sum自减每个元素,当sum小于target时,切换回移动快指针
- 如果快慢指针之间的距离小于min的记录(min初始为一个足够大的数),则更新min
- 最后如果快指针移动到最后,sum也小于target,则跳出循环
- return结果
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int right = 0;
int min = 99999999;
int sum = 0;
while (!(right == nums.length && sum < target)) {
if (sum >= target) {
if (right - left < min) {
min = right - left;
}
sum -= nums[left++];
} else {
sum += nums[right++];
}
}
if (min == 99999999) {
return 0;
}
return min;
}
}