第一题 删除有序数组的重复项
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
。
快慢指针法
class Solution {
public int removeDuplicates(int[] nums) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if (nums[fastIndex] != nums[slowIndex]) {
slowIndex++;
nums[slowIndex] = nums[fastIndex];
}
}
return slowIndex + 1;
}
}
暴力解法
双for嵌套,一层遍历数组元素,一层更新数组,时间复杂度为O(n ^ 2),不考虑使用
第二题 移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
快慢指针法一
非零值往前覆盖,最后在后面自行补零
class Solution {
public void moveZeroes(int[] nums) {
int slowIndex = 0;
int n = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if (nums[fastIndex] != 0) {
nums[slowIndex] = nums[fastIndex];
slowIndex++;
} else {
n++; // 0个数
}
}
while (n > 0) {
nums[nums.length - n] = 0;
n--;
}
}
}
快慢指针法二
左右指针交换位置,按顺序将非零值往前移动,零往后移动
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length, left = 0, right = 0;
while (right < n) {
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;
}
}
第三题 比较含退格的字符串
给定 s
和 t
两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true
。#
代表退格字符。
自解
双层循环,第一层逆序循环更新字符串,遇到'#'进入第二层循环,通过计数器记录需要跳过的字符个数,在跳过的过程中及时退出循环,不足之处是元素的顺序倒了
class Solution {
public boolean backspaceCompare(String s, String t) {
if (back(s).equals(back(t))) {
return true;
}
return false;
}
public static String back(String s) {
StringBuilder sb = new StringBuilder();
int i = s.length() - 1;
while (i >= 0) {
char c = s.charAt(i);
if (c == '#') {
for (int count = 2; count > 0; count--) {
i--;
if (i < 0) {
break;
}
if (s.charAt(i) == '#') {
count += 2;
}
}
} else {
sb.append(c);
i--;
}
}
return sb.reverse().toString();
}
}
官解一:栈处理
单层循环,如果遇到了退格符,那么就把栈顶元素弹出,非退格符的元素,就按顺序依次进栈
此方法虽然简单,但是实际运行时,时间效率和空间效率上都略逊于上一个答案
class Solution {
public boolean backspaceCompare(String S, String T) {
return build(S).equals(build(T));
}
public String build(String str) {
StringBuffer ret = new StringBuffer();
int length = str.length();
for (int i = 0; i < length; ++i) {
char ch = str.charAt(i);
if (ch != '#') {
ret.append(ch);
} else {
if (ret.length() > 0) {
ret.deleteCharAt(ret.length() - 1);
}
}
}
return ret.toString();
}
}
官解二:双指针法
逆序遍历,两个指针分别记录遇到的第一个无需删除的字符,使用计数器记录删除次数,然后比较记录的字符是否相同,不同返回false,相同则继续遍历
class Solution {
public boolean backspaceCompare(String S, String T) {
int i = S.length() - 1, j = T.length() - 1;
int skipS = 0, skipT = 0;
while (i >= 0 || j >= 0) {
while (i >= 0) {
if (S.charAt(i) == '#') {
skipS++;
i--;
} else if (skipS > 0) {
skipS--;
i--;
} else {
break;
}
}
while (j >= 0) {
if (T.charAt(j) == '#') {
skipT++;
j--;
} else if (skipT > 0) {
skipT--;
j--;
} else {
break;
}
}
if (i >= 0 && j >= 0) {
if (S.charAt(i) != T.charAt(j)) {
return false;
}
} else { // 有一方遍历完毕,小于零
if (i >= 0 || j >= 0) {
return false;
}
}
i--;
j--;
}
// 全部遍历完毕
return true;
}
}
第四题 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
简单做法:冒泡排序
时间效率很低
class Solution {
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
for (int i = 0; i < nums.length - 1; i++) {
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j + 1]) {
int ret = nums[j + 1];
nums[j + 1] = nums[j];
nums[j] = ret;
}
}
}
return nums;
}
}
简单做法:Arrays.sort方法
比冒泡稍微强一点
class Solution {
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
官解一:归并排序(双指针)
找到负数的最大值,非负数的最小值,由此可将原来的数组划分为两个数组(负数,正数的平方),因为平方后负数按从大到小的顺序排,正数按从小到大的顺序排,这时使用归并排序,使用两个指针指向两个数组,按最小数优先的顺序排序,当某一数组遍历完毕,添加另一数组数据
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int negative = -1;
for (int i = 0; i < n; ++i) {
if (nums[i] < 0) {
negative = i;
} else {
break;
}
}
int[] ans = new int[n];
int index = 0, i = negative, j = negative + 1; // 负数 i, 正数或0 j
while (i >= 0 || j < n) {
if (i < 0) { // 负数已经遍历完
ans[index] = nums[j] * nums[j];
++j;
} else if (j == n) { // 正数已经遍历完了,倒序添加负数平方
ans[index] = nums[i] * nums[i];
--i;
} else if (nums[i] * nums[i] < nums[j] * nums[j]) {
ans[index] = nums[i] * nums[i];
--i;
} else {
ans[index] = nums[j] * nums[j];
++j;
}
++index;
}
return ans;
}
}
官解二:双指针法
和上一个解答类似,不过是将正负数为界限遍历改成了分别从头尾遍历,优点是不用处理某一指针到边界的情况
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int[] ans = new int[n];
for (int i = 0, j = n - 1, pos = n - 1; i <= j;) {
if (nums[i] * nums[i] > nums[j] * nums[j]) {
ans[pos] = nums[i] * nums[i];
++i;
} else {
ans[pos] = nums[j] * nums[j];
--j;
}
--pos;
}
return ans;
}
}