算法通关村第三关—数组
1.LeetCode 896. 单调数列
题目地址:LeetCode
/**
* 一次遍历
*
* @param nums
* @return
*/
public boolean isMonotonic(int[] nums) {
boolean inc = true, dec = true;
int n = nums.length;
for (int i = 0; i < n - 1; i++) {
if (nums[i] > nums[i + 1]) {
inc = false;
}
if (nums[i] < nums[i + 1]) {
dec = false;
}
}
return inc || dec;
}
2.LeetCode 35. 搜索插入位置
题目地址:LeetCode
解题思路:
/**
* 二分查找
*
* @param nums
* @param target
* @return
*/
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
3.LeetCode 88. 合并两个有序数组
题目地址:LeetCode
/**
* 逆向双指针
*
* @param nums1
* @param m
* @param nums2
* @param n
*/
public void merge(int[] nums1, int m, int[] nums2, int n) {
int k = m + n;
for (int index = k - 1, nums1Index = m - 1, nums2Index = n - 1; index >= 0; index--) {
if (nums1Index < 0) {
//nums1已经取完,完全取nums2的值即可
nums1[index] = nums2[nums2Index--];
} else if (nums2Index < 0) {
//nums2已经取完,完全取nums1的值即可
break;
} else if (nums1[nums1Index] > nums2[nums2Index]) {
//nums1的元素值大于nums2,取nums1值
nums1[index] = nums1[nums1Index--];
} else {
//nums2的元素值大于nums1,取nums2值
nums1[index] = nums2[nums2Index--];
}
}
}
4.LeetCode 27. 移除元素
题目地址:LeetCode
解题思路:
- 初始化两个指针:
slow
和fast
。这两个指针都从数组的开头(索引 0)开始。 - 通过
fast
指针遍历数组,从第一个元素(索引 0)开始,向数组的末尾移动。 - 对于
fast
指针当前指向的元素,检查它是否等于指定的值val
。 - 如果当前元素不等于
val
,说明我们要保留这个元素在修改后的数组中(因为我们要移除所有的val
)。在这种情况下,将该元素复制到slow
指针的位置,然后将slow
指针移动到下一个位置。这一步骤有效地将非val
元素覆盖到数组中原来val
的位置。 - 如果当前元素等于
val
,则直接跳过,不做任何处理,然后将fast
指针移到下一个元素的位置,slow
指针保持不动。 - 重复步骤 4 和 5,直到
fast
指针到达数组的末尾。 - 此时
slow
指针的值表示修改后的新数组的长度,其中所有的val
元素已经被移除。 - 返回
slow
的值,即为修改后的数组中不包含val
的元素个数。
/**
* 快慢双指针
*
* @param nums 待处理数组
* @param val 待删元素
* @return 不重复元素个数
*/
public int removeElement(int[] nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
5.LeetCode 26. 删除有序数组中的重复项
题目地址:LeetCode
解题思路:
- 初始化两个指针:
slow
和fast
。其中,slow
表示可以放入新元素的位置,初始值为 1(因为索引为 0 的元素不需要管,可以保留在新数组中)。 - 使用循环来模拟快指针的移动,
fast
指针从索引 0 开始,向数组的末尾移动。 - 对于每个
fast
指针指向的元素,检查它是否与上一个位置(slow - 1
索引处)的元素相同。 - 如果当前元素与上一个元素不相同,说明该元素是新的不重复元素,应该放入新数组中。将该元素复制到
slow
指针的位置,然后将slow
指针移动到下一个位置。 - 如果当前元素与上一个元素相同,则跳过该元素,不将其放入新数组中,继续考察下一个元素。
- 重复步骤 4 和 5,直到
fast
指针遍历完整个数组。 - 此时
slow
指针的值表示新数组的长度,其中包含了不重复的元素。 - 返回
slow
的值作为新数组的长度。
/**
* 快慢双指针
*
* @param nums 待处理数组
* @return
*/
public static int removeDuplicates(int[] nums) {
//slow表示可以放入新元素的位置,索引为0的元素不用管
int slow = 1;
//循环起到了快指针的作用
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != nums[slow - 1]) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
6.LeetCode 905. 按奇偶排序数组
题目地址:LeetCode
解题思路:
- 创建两个指针
left
和right
,分别初始化为数组的开头和结尾。 - 目标是将所有偶数移到数组的左侧,将所有奇数移到数组的右侧。
- 进入一个
while
循环,循环条件是left
小于right
。 - 在循环中:
- 检查索引
left
处的数是否为奇数(nums[left] % 2 == 1
),以及索引right
处的数是否为偶数(nums[right] % 2 == 0
)。 - 如果上述条件为真,则交换索引
left
和right
处的数,这样就把偶数移到了左侧,把奇数移到了右侧。 - 如果索引
left
处的数是偶数,则将left
增加1;如果索引right
处的数是奇数,则将right
减少1。这样我们可以继续向数组中心移动,继续交换偶数和奇数。
- 检查索引
- 当
while
循环结束时,数组将按照奇偶性进行了分区,偶数位于数组的左侧,奇数位于数组的右侧。 - 返回排序后的数组。
/**
* 对撞型双指针
*
* @param nums
* @return
*/
public int[] sortArrayByParity(int[] nums) {
int left = 0, right = nums.length - 1;
while (left < right) {
if (nums[left] % 2 > nums[right] % 2) {
int temp = nums[right];
nums[right] = nums[left];
nums[left] = temp;
}
if (nums[left] % 2 == 0) left++;
if (nums[right] % 2 == 1) right--;
}
return nums;
}
7.LeetCode 189. 轮转数组
题目地址:LeetCode
解题思路:
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[end];
nums[end] = nums[start];
nums[start] = temp;
start++;
end--;
}
}
8.LeetCode 228. 汇总区间
题目地址:LeetCode
解题思路:
慢指针指向每个区间的起始位置,快指针从慢指针位置开始向后遍历直到不满足连续递增(或快指针达到数组边界),则当前区间结束;然后将 slow指向更新为 fast + 1,作为下一个区间的开始位置,fast继续向后遍历找下一个区间的结束位置,如此循环,直到输入数组遍历完毕
/**
* 快慢指针
*
* @param nums
* @return
*/
public List<String> summaryRanges(int[] nums) {
ArrayList<String> res = new ArrayList<>();
// slow 初始指向第 1 个区间的起始位置
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
// fast 向后遍历,直到不满足连续递增(即 nums[fast] + 1 != nums[fast + 1])
// 或者 fast 达到数组边界,则当前连续递增区间 [slow, fast] 遍历完毕,将其写入结果列表
if (fast + 1 == nums.length || nums[fast] + 1 != nums[fast + 1]) {
// 将当前区间 [slow, fast] 写入结果列表
StringBuilder sb = new StringBuilder();
sb.append(nums[slow]);
if (slow != fast) {
sb.append("->").append(nums[fast]);
}
res.add(sb.toString());
// 将 slow 指向更新为 fast + 1,作为下一个区间的起始位置
slow = fast + 1;
}
}
return res;
}
9.剑指 Offer 05. 替换空格
题目地址:剑指 Offer
/**
* 字符串拼接
*
* @param s
* @return
*/
public String replaceSpace(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') {
sb.append("%20");
} else {
sb.append(s.charAt(i));
}
}
return sb.toString();
}