1. 删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
java答案 :
1. 双指针,一个指向没有重复数的数组的下标,一个指向原始数组的下标
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
}
时间复杂度是o(n),空间复杂度是o(1)
JavaScript答案:
1. 双指针
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function(nums) {
if(nums.length==0){
return 0;
}
var i=0;
for(let j=1;j<nums.length;j++){
if(nums[j]!=nums[i]){
nums[++i] = nums[j];
}
}
return i+1;
};
2. 使用数组的splice()方法直接删除数组
var removeDuplicates = function(nums) {
if(nums.length==0){
return 0;
}
for(var i=0;i<nums.length-1;i++){
while(nums[i]==nums[i+1] && (i+1)<nums.length){
nums.splice(i,1);
}
}
return nums.length;
};
2. 三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
java答案 :
1. 先排序,然后使用双指针
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);//先排序
List<List<Integer>> res = new ArrayList<>();
for(int k = 0; k < nums.length - 2; k++){
if(nums[k] > 0) break; //若最小的数是大于0的,就直接break
if(k > 0 && nums[k] == nums[k - 1]) continue; //确保不会出现重复的list集合
int i = k + 1, j = nums.length - 1;
while(i < j){
int sum = nums[k] + nums[i] + nums[j];
if(sum < 0){
while(i<j && nums[i] == nums[++i]) {}
} else if (sum > 0) {
while(i < j && nums[j] == nums[--j]) {}
} else {
res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
while(i<j && nums[i] == nums[++i]) {}
while(i < j && nums[j] == nums[--j]) {}
}
}
}
return res;
}
}
时间复杂度是o(n^2)
2. 暴力法,三层for循环,但是要保证最后list集合中没有重复的数据
注意:
- java.util.Arrays.asList(Object):将需要转化的数组或数组元素转换为List.
1、参数类型是数组元素的class,参数必须是对象或者对象数组,而原生数据类型不是对象,当传入一个原生数据类型数组时,asList 的真正得到的参数就不是数组中的元素,而是数组对象本身!
原生数据类型,比如 int,short,long等,是没有这个属性的,具有 class 属性的是它们所对应的包装类 Integer,Short,Long。
2、 用 asList 方法产生的 List 是固定大小的,这也就意味着任何改变其大小的操作都是不允许的。
可以创建一个真正的ArrayList: List myList = new ArrayList(Arrays.asList(myArray));
JavaScript答案:
var threeSum = function(nums) {
var alist = [];
nums.sort((a, b) => a - b);
for(let i=0;i<nums.length-2;i++){
if(nums[i]>0){
break;
}
if(i>0 && nums[i]===nums[i-1]){
continue;
}
let x=i+1;
let y = nums.length-1;
while(x<y){
var sum = nums[i]+nums[x]+nums[y];
if(sum<0){
while(x<y && nums[x]==nums[++x]){}
}else if(sum>0){
while(x<y && nums[y]==nums[--y]){}
}else{
//var list = [];
//list.push(nums[i],nums[x],nums[y]);
alist.push([nums[i],nums[x],nums[y]]);
while(x<y && nums[x]===nums[++x]){}
while(x<y && nums[y]===nums[--y]){}
}
}
}
return alist;
};
注意:
有重复数的数组排序: nums.sort((a, b) => a - b);
3. 最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
java答案:
1. 先排序,然后使用双指针
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int ans = nums[0] + nums[1] + nums[2];
for (int k = 0; k < nums.length - 2; k++) {
if(k > 0 && nums[k] == nums[k - 1]) continue;
int start = k+1, end = nums.length - 1;
while(start < end) {
int sum = nums[start] + nums[end] + nums[k];
if(Math.abs(target - sum) < Math.abs(target - ans))
ans = sum;
if(sum > target)
end--;
else if(sum < target)
start++;
else
return ans;
}
}
return ans;
}
}
时间复杂度是o(n^2)
2. 暴力法
class Solution {
public int threeSumClosest(int[] nums, int target) {
if(nums.length<3)
return 0;
int min = nums[0]+nums[1]+nums[2];
for(int i=0;i<nums.length-2;i++){
for(int j=i+1;j<nums.length-1;j++){
for(int x =j+1;x<nums.length;x++){
int sum = nums[i]+nums[j]+nums[x];
if(Math.abs(target - sum) <= Math.abs(target - min)){
min= sum;
}
}
}
}
return min;
}
}
时间复杂度是o(n^3)
JavaScript答案:
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function(nums, target) {
nums.sort((a,b)=>a-b);
let min = nums[0]+nums[1]+nums[2];
for(var i=0;i<nums.length-2;i++){
if(i>0 && nums[i]===nums[i-1]){
continue;
}
var start=i+1;
var end = nums.length-1;
while(start<end){
var sum = nums[i]+nums[start]+nums[end];
if(Math.abs(target-sum)< Math.abs(target-min)){
min = sum;
}
if(sum>target){
end--;
}else if(sum<target){
start++;
}else{
return min;
}
}
}
return min;
};
注意:
- Math.abs(a); //求一个数的绝对值
- Math.sqrt()//计算平方根
- Math.cbrt()//计算立方根
- Math.pow(a, b)//计算a的b次方
- Math.max( , );//计算最大值
- Math.min( , );//计算最小值
- Math.ceil(a);//返回大的整数
- Math.floor(a)//返回小的整数
- Math.rint(a);// 四舍五入,返回double值 ,注意.5的时候会取偶数
- Math.round(a);//四舍五入,float时返回int值,double时返回long值
4. 盛最多水的容器
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
java答案:
1. 快排,双指针,一个在前往后走,一个在后往前走,谁小谁走
class Solution {
public int maxArea(int[] height) {
if(height.length<2){
return 0;
}
int max =0;
int i=0;
int j=height.length-1;
int count=height.length-1;
while(i<j){
int sum=0;
if(height[i]<=height[j]){
sum=height[i]*count;
i++;
count--;
}else{
sum = height[j]*count;
j--;
count--;
}
if(max<sum){
max = sum;
}
}
return max;
}
}
或
public class Solution {
public int maxArea(int[] height) {
int maxarea = 0, l = 0, r = height.length - 1;
while (l < r) {
maxarea = Math.max(maxarea, Math.min(height[l], height[r]) * (r - l));
if (height[l] < height[r])
l++;
else
r--;
}
return maxarea;
}
}
时间复杂度是o(n),空间复杂度是o(1)
2. 暴力法
public class Solution {
public int maxArea(int[] height) {
int maxarea = 0;
for (int i = 0; i < height.length; i++)
for (int j = i + 1; j < height.length; j++)
maxarea = Math.max(maxarea, Math.min(height[i], height[j]) * (j - i));
return maxarea;
}
}
时间复杂度是o(n^2),空间复杂度是o(1)
JavaScript答案:
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
if(height.length<2){
return 0;
}
var max =0;
var i=0;
var j=height.length-1;
var count=height.length-1;
while(i<j){
var sum=0;
if(height[i]<=height[j]){
sum=height[i]*count;
i++;
count--;
}else{
sum = height[j]*count;
j--;
count--;
}
if(max<sum){
max = sum;
}
}
return max;
};
5. 除自身以外数组的乘积
给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
示例:
输入: [1,2,3,4]
输出: [24,12,8,6]
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
java答案:
1. 不能使用除法,且时间复杂度是o(n),就不能使用双层for循环了,只能在乘积时记录
除自身之外的数组乘积,就是这个数左边的乘积乘以右边的乘积,先计算左边乘积记录在最终返回的数组中,再计算右边的乘积
class Solution {
public int[] productExceptSelf(int[] nums) {
int[] result = new int[nums.length];
if(nums.length<=1){
return result;
}
result[0]=1;
for(int i=1;i<nums.length;i++){
result[i] = result[i-1]*nums[i-1];
}
int rightSum =1;
for(int i=nums.length-1;i>=0;i--){
result[i] = result[i]*rightSum;
rightSum=nums[i]*rightSum;
}
return result;
}
}
2. 上三角,下三角
class Solution {
public int[] productExceptSelf(int[] nums) {
int[] res = new int[nums.length];
int p = 1, q = 1;
for (int i = 0; i < nums.length; i++) {
res[i] = p;
p *= nums[i];
}
for (int i = nums.length - 1; i > 0 ; i--) {
q *= nums[i];
res[i - 1] *= q;
}
return res;
}
}
JavaScript答案:
1. 上三角,下三角
/**
* @param {number[]} nums
* @return {number[]}
*/
var productExceptSelf = function(nums) {
var res=[];
if(nums.length<=1){
return res;
}
var i=1,j=1;
for(var x=0;x<nums.length;x++){
res[x]=i;
i=i*nums[x];
}
for(var x=nums.length-1;x>=0;x--){
j=j*nums[x];
res[x-1] = j*res[x-1];
}
return res;
};
2. 分治法,一分为二
var productExceptSelf = function(nums) {
if(nums.length == 0) return nums;
let res = new Array(nums.length); //用来保存结果的数组
res.fill(1);
let merge = (mul1, mul2, l, r, mid)=>{ //左右两个部分的数组相互作积,并返回当前区域数组的积
for(let i = l; i <= mid; i++){
res[i] = res[i]*mul2;
}
for(let j = mid+1; j <= r; j++){
res[j] = res[j]*mul1;
}
return mul1*mul2;
}
let partation = (l, r)=>{ //划分数组,返回当前区域数组的积
if(l == r){
return nums[l];
}
let mid = Math.floor((l+r)/2);
return merge(partation(l, mid), partation(mid+1, r), l, r, mid); //递归调用划分函数
}
partation(0, nums.length-1);
return res;
};
或
var res;
var merge = function(num1,num2,l,mid,r){
for(var i=l;i<=mid;i++){
res[i] = res[i]*num2;
}
for(var i=mid+1;i<=r;i++){
res[i]=res[i]*num1;
}
return num1*num2;
};
var partation = function(nums,l,r){
if(l==r){
return nums[l];
}
let mid = Math.floor((l+r)/2);
return merge(partation(nums,l,mid),partation(nums,mid+1,r),l,mid,r);
};
var productExceptSelf = function(nums) {
res=new Array(nums.length);
if(nums.length<=1){
return res;
}
res.fill(1);
partation(nums,0,nums.length-1);
return res;
};
注意:
- js中创建数组:var res = new Array(); //或var res=[];
- fill() 方法用于将一个固定值替换数组的元素。res.fill(1); 因为数组在新创建出来没有赋值时,都是undefined.