一、数组的问题
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
//时间复杂度O(n),空间复杂度O(n)
class Solution {
public void moveZeroes(int[] nums) {
List<Integer> nonZeroList=new ArrayList<>();;
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
nonZeroList.add(nums[i]);
}
}
for(int i=0;i<nonZeroList.size();i++){
nums[i]=nonZeroList.get(i);
}
for(int i=nonZeroList.size();i<nums.length;i++){
nums[i]=0;
}
}
}
//空间复杂度O(1)
public void moveZeroes(int[] nums) {
int k = 0;//nums中,[0...k)的元素均为非0元素
//遍历到第i个元素后,保证[0...i]中所有非0元素
//都按照顺序排列在[0...k)中
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[k++] = nums[i];
}
}
//将剩余位置置为0
for (int i = k; i < nums.length; i++) {
nums[i] = 0;
}
}
相关题目26,27,80
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
//数组已排序,采用快慢双指针
int i = 0;//i慢,j快
for (int j = 1; j < nums.length; j++) {
if (nums[i]!=nums[j]){//由于数组已排序,如果2个值不相等时,跳过重复项的运行已经结束
i++;// i向后走
nums[i]=nums[j];//然后覆盖掉重复的元素,用后面的覆盖前面的
}
//只要 nums[i] = nums[j]我们就增加j 以跳过重复项。
}
return i+1;
}
}
class Solution {
public int removeElement(int[] nums, int val) {
//当我们遇到相等时,我们可以将当前元素与最后一个元素进行交换,并释放最后一个元素
int i = 0;
int n = nums.length;
while (i < n) {
if (nums[i] == val) {
nums[i] = nums[n - 1];
n--;
} else {
i++;
}
}
return n;
}
}
class Solution {
public int removeElement(int[] nums, int val) {
if (nums.length == 0) return 0;
int j = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val) {//当不等于要删除元素时候,将后面的覆盖前面的相等的元素
nums[j]=nums[i];
j++;
}
//数组元素等于要删除元素时候,向后移,跳过
}
return j ;
}
}
//复杂度O(n^2)
class Solution {
public int removeDuplicates(int[] nums) {
//增序排列数组,对于数组中的每个数字,若出现 2 个以上的重复项,就将多余的重复项从数组列表中删除。
int i = 1, count = 1, length = nums.length;
while (i < length) {//从第二个元素开始
if (nums[i] == nums[i - 1]) {//如果和前面的相同
count++;//计数+1
if (count > 2) {//如果出现 2 个以上的重复
for (int j = i + 1; j < length; j++) {//将后面的元素依次移到前面,覆盖掉多的重复的元素
nums[j - 1] = nums[j];
}
i--;//注意减去删掉的元素
length--;//数组长度减1
}
}else {
count = 1;//如果遇到了其他元素,重置计数
}
i++;//继续下一个元素
}
return length;
}
}
//复杂度O(N)
class Solution {
public int removeDuplicates(int[] nums) {
//利用双指针优化
int i = 1 ,j = 1, count = 1;
int length = nums.length;
while (i < length) {//从第二个元素开始
if (nums[i] == nums[i - 1]) {
count++;
} else {
count = 1;
}
if (count <= 2) {//则我们将 i 所指向的元素移动到 j 位置,并同时增加 i 和 j
nums[j++] = nums[i];
}
i++;
}
return j;
}
}
二、基础算法思路的应用
给定一个有n个元素的数组,数组中元素的取值只有0,1,2三种可能,为这个数组排序
public void sortColors(int[] nums) {
//计数排序:扫描一遍数组,计算出0,1,2有多少个,将他们在分别放回
//计数排序适用于元素个数有限的情况
//时间复杂度O(n)
//空间复杂度O(k),k为元素的取值范围
int count[] = new int[3];//存放0,1,2三个元素的频率
for (int i = 0; i < nums.length; i++) {
//assert(nums[i]>=0&&nums[i]<=2);
count[nums[i]]++;
}
int index = 0;
for (int i = 0; i < count[0]; i++) {
nums[index++] = 0;
}
for (int i = 0; i < count[1]; i++) {
nums[index++] = 1;
}
for (int i = 0; i < count[2]; i++) {
nums[index++] = 2;
}
}
使用一遍遍历完成操作
三路快速排序思路
每次选取一个标定点但是由于这个标定点有很多个和这个元素相等的值,所以将整个数组分成三份,<v, =v, >v,之后递归的对<v和>v的地方进行三路快排。
这道题目是由3个元素,只需要对整个数组进行一次三路快排
class Solution {
public void sortColors(int[] nums) {
int zero = -1;//nums[0...zero]==0;
int two = nums.length;//nums[teo...n-1]==2
for (int i = 0; i < two; ) {
if (nums[i] == 1) {
i++;
} else if (nums[i] == 2) {
two--;
int temp = nums[i];
nums[i] = nums[two];
nums[two] = temp;
} else { //nums[i]==0
zero++;
int temp = nums[zero];
nums[zero] = nums[i];
nums[i] = temp;
i++;
}
}
}
}
相关问题88,215
给定一个升序排列整型数组和一个数组target,在其中寻找2个元素,使其和为target,返回这两个数的索引。
如numbers=[2,7,11,15],target=9
返回数字2,7的索引1,2(索引从1开始计算)
- 思路1,暴力解法,双重循环,但是这样会O(n^2)超时
-
思路2,暴力解法没有充分利用原数组的性质——有序,通过有序可以联想到二分搜索。依次遍历每个数据nums[i]对于每个nums[i]在剩余的数组中寻找target-nums[i],每次遍历都是进行二分查找复杂度O(longn),所以这个思路的复杂度是O(nlogn)
-
思路3,由于我们要寻找到2个索引,他们对应的数字和为target,这两个索引子数组中一定是一左一右,所以在初始化的时候选择最左侧的数字i,最右侧的数字j,在初始的情况下看nums[i]+nums[j]是否==target,如果是就找到了答案。如果是nums[i]+nums[j]<target,此时将i++(因为数组是有序的,此时的nums[i]一定比前一个多一些),此时在判断是否等于target,如果此时nums[i]+nums[j]>target,那么就要j--(此时的nums[j]一定比前一个小一些)。复杂度O(n)
public int[] twoSum(int[] numbers, int target) {
int l = 0, r = numbers.length - 1;
while (l < r) {
if (numbers[l] + numbers[r] == target) {
int res[] = {l + 1, r + 1};
return res;
} else if (numbers[l] + numbers[r] < target) {
l++;
} else if (numbers[l] + numbers[r] > target) {
r--;
}
}
throw new IllegalArgumentException();
}
相关问题125,344,345,11
三、滑动窗口
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
如s = 7, nums = [2,3,1,2,4,3],输出: 2
- 暴力解法:遍历所有连续子数组[i...j],计算其和sum,验证sum>=s,时间复杂度O(n^3)
- 暴力解法的问题是包含大量的重复计算,当看到[i...j]这个连续子数组,如果这个和不到s,那么向后看一个数据,将他纳入到这个连续子数组中,[i...j+1],以此类推,直到某个时刻和大于s了就找到了那组条件的子数组,将他的长度记录下来,然后从i这边缩小数组,i--,在不断的缩小i的过程中,某个时刻和就小于s了,此时j继续向后找到一个连续子数组让和大于s,以此类推
import static java.lang.Math.min;
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int l = 0, r = -1;//nums[l...r]为滑动窗口
int sum = 0;
int res = nums.length + 1;
while (l < nums.length) {
if (r + 1 < nums.length && sum < s) {
sum += nums[++r];
} else {
sum -= nums[l++];
}
if (sum >= s) {
res = min(res, r - l + 1);
}
}
if (res == nums.length + 1) {
return 0;
}
return res;
}
}
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
- 依然使用滑动窗口来解决问题
import static java.lang.Math.max;
class Solution {
public int lengthOfLongestSubstring(String s) {
char[] c = s.toCharArray();
int[] freq = new int[256];//开始时所有子数组元素出现的频率为0
int l = 0, r = -1;
int res = 0;
while (l < c.length) {
if (r + 1 < c.length && freq[c[r + 1]] == 0) {
freq[c[++r]]++;
} else {
freq[c[l++]]--;
}
res = max(res, r - l + 1);
}
return res;
}
}
相关问题,438,76