双指针
说到双指针,其实最容易想到的是快速排序方法。使用两个指针,通过哨兵比较,形成局部有序。
第一题:有序数组的平方
java答案:
class Solution {
public int[] sortedSquares(int[] nums) {
int i=0,j=nums.length-1;
int flag=nums.length-1;
int[] bnums=new int[nums.length];
while(i<=j){
if(nums[i]*nums[i]>nums[j]*nums[j]){
bnums[flag]=nums[i]*nums[i];
i++;
}
else{
bnums[flag]=nums[j]*nums[j];
j--;
}
flag--;
}
return bnums;
}
}
当然,也不一定上来就比较数组第一个值和最后一个值的平方大小,也可以判断出正负数交接的下表,这样的话,出现全是正或者全是负比较快速一点。
第二题:旋转数组
题目描述:
第一种解法:
我们一般能想到使用另外的一个数组
class Solution {
public void rotate(int[] nums, int k) {
int[] bnums=new int[nums.length];
for(int i=0;i<nums.length;i++){
bnums[(i+k)%nums.length]=nums[i];
}
System.arraycopy(bnums, 0, nums, 0, nums.length);
}
}
-
时间复杂度: O(n)O(n),其中 nn 为数组的长度。
-
空间复杂度: O(n)O(n)。
需要讲讲System.arraycopy
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
其中参数含义:
Object src:the source array. 源数组
int srcPos:starting position in the source array. 在源数组中,开始复制的位置
Object dest:the destination array. 目标数组
int destPos:starting position in the destination data. 在目标数组中,开始赋值的位置
int length:the number of array elements to be copied. 被复制的数组元素的数量
要注意:数组越界,取原数组的时候,int srcPos和int length要注意不要超过原始数组的范围,同时目标数组destPos和length也要注意不要超过目标数组的范围。
System.arraycopy
为 JVM 内部固有方法,它通过手工编写汇编或其他优化方法来进行 Java 数组拷贝,这种方式比起直接在 Java 上进行 for 循环或 clone 是更加高效的。数组越大体现地越明显。
arraycopy的
深拷贝和浅拷贝
- 当数组为一维数组,且元素为基本类型或String类型时,属于深复制,即原数组与新数组的元素不会相互影响(String的特殊是因为它的不可变性)
- 当数组为多维数组,或一维数组中的元素为引用类型时,属于浅复制,原数组与新数组的元素引用指向同一个对象
第二种解法:
例如:当k=3时
class Solution {
public void rotate(int[] nums, int k) {
//一定不要忘了这一步,不然有可能会出现数组越界的错误,找了半天
k = k % nums.length;
reserver(nums,0,nums.length-1);
reserver(nums,0,k-1);
reserver(nums,k,nums.length-1);
}
public static int[] reserver(int[] nums,int low,int high){
for(;low<high;low++,high-- ){
int temp=nums[low];
nums[low]=nums[high];
nums[high]=temp;
}
return nums;
}
}
时间复杂度:O(n),其中 n 为数组的长度。每个元素被翻转两次,一共 n 个元素,因此总时间复杂度为 O(2n)=O(n)。
空间复杂度:O(1)。
踩坑提示:
说我数组越界,这一行报错,检查老久了,被自己蠢哭。
第三种解法:
上图中pre,next, a[next]=a[pre];count在下面的代码中没有体现,直接比对的是下标直接回到一个循环起始的地方
class Solution {
public void rotate(int[] nums, int k){
int n=nums.length;
//求公约数
int count = gcd(k, n);
for(int start=0;start<count;start++){
//pre记录的是拿去覆盖的值,例如a[2]=a[0],这里记录的是a[0]
int pre=nums[start];
int next=start;
do{
//记录下一次的位置,例如下标为1的值,会插入到1+k的位置;例如:k=2时,a[2]=a[0],next代表这个2
next=(next+k)%n;
//因为a[2]=a[0],a[2]的值会被覆盖,需要先把a[2]的值存起来
int temp=nums[next];
//这步实现a[2]=a[0]
nums[next]=pre;
//pre记录的是a[2]的值,pre记录的是去覆盖的值
pre=temp;
}while(start!=next);
}
}
public int gcd(int x, int y) {
return y > 0 ? gcd(y, x % y) : x;
}
}
这里需要注意公约数和循环的圈有几次