每日一题之两数相加(没有哈希算法)
原题:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。原题链接(力扣)
结果示例
示例1
这里是引用输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]
示例2
输入:nums = [3,2,4], target = 6
输出:[1,2]
### 示例3
输入:nums = [3,3], target = 6
输出:[0,1]
方法一(时间复杂度为Q(n^2))
思路介绍:
这里所使用的就是我们正常来说的暴力遍历,利用两个循环,将数组中的数据
两两相加再用来比较两个数的和是否等于我们需要的值,如果等于就将下标给保存再我们定
义的一个新的数组里面,最后返回这个数组即可。
public int[] twoSum(int[] nums, int target) {
int[] numidex = new int[2];
for (int i = 0; i < nums.length; i++) {
//这里用一个数来保存一个数
for (int j = 0; j < nums.length; j++) {
if (i == j) {
continue;
} else if (i != j && nums[i] + nums[j] == target) {
numidex[0]=i;
numidex[1]=j;
return numidex;
}
}
}
return null;
}
该方法的的时间复杂度如下
O(N*N)=O(N^2)
方法二(时间复杂度为O(n*logN))
思路介绍
注:本题使用上面的方法是没有任何问题的,那也算是力扣里面的常规解法,但接下来的这个方法,我不确定是否是优解,但是,再算法中计算,我的时间复杂度确确实实是O(n*logN))<O(N^2),下面我就分享分享我的解法(力扣上面的大佬使用哈希算法可以达到O(N)的时间复杂度,由于我才疏学浅,(好吧,我菜,我不会😂😂😂),想了大半天,想了这个方法,感谢阅读,不好的地方请指出)
思路来源->二分法
我们都知道,二分法可以用来查找,那么我是不是可以采用二分法,将一段数组分成两段呢,让后不停的查找两个数,它用来查找,我用来求和,也是可以把任意两个数放在一起求和的呀。于是说干就干,我就开始研究我的代码了,首先这个数组,我们需要用到二分法,我得先对数组排序,可是正常的排序,比如冒泡排序法,选择排序法,,二分法排序时间复杂度都为O(N^2)呀,于是他们都被我排除在外了,我就想到了快速排序法
快速排序法(如果不理解快速排序法的可以参考一下我下面的这个链接)
快速排序法博客链接
public static void paixu(int[] arr){
sort_kuaishu(arr,0,arr.length-1);
}
public static void sort_kuaishu(int[] arr,int start,int end){
if(start>end){
return;
}
int i=start,j=end,index=arr[start],temp;
while (i<j){
while(i<j && arr[j]>index){
j--;
}
while (i<j && arr[i]<=index){
i++;
}
if(i<j){
temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
temp=arr[i];
arr[i]=arr[start];
arr[start]=temp;
//递归
sort_kuaishu(arr,start,i-1);
sort_kuaishu(arr,i+1,end);
}
有人问排序有什么用呢,那么用处就来了
我们采用排序后的数组,再对数据进行取值相加,就采用二分法的原理,将数组分成两段,从数组的两端往中间算,大了就把小的放大,小了就把小的放大(听起来是不是很简单,那么我们看看实现的代码)
public int[] twoSum(int[] nums, int target) {
int[] nums_1= Arrays.copyOf(nums,nums.length);//因为涉及到要排序,我们不要改变原来的数组,复制给一个新数组
paixu(nums_1);//对数组进行从小到大的排序
int j , i;
int[] numidex=new int[2];//设置一个两位的数组,分别用来存储对应数组下标
for (i = 0, j=nums_1.length-1; i<j;) {//将两个数组加到中间的两个位置就截止,不至于整个循环,降低时间复杂度
if(nums_1[i]+nums_1[j]==target){
numidex[0]=i;
numidex[1]=j;
break;//当我们找到了适合结果的数字下标,直接退出循环即可
}
if(nums_1[i]+nums_1[j]<target){
i++;//既然和小了,那我们就把数加大
continue;//这个contiune可要可不要,要它是因为节省一点,这里比较了,便不用去下一个if循环了
}
if(nums_1[i]+nums_1[j]>target){
j--;//同理,既然和大了,就将大的数减小一点
}
}
if(nums_1[numidex[0]]+nums_1[numidex[1]]!=target){
return null;//当我们循环到最后,加入都没有满足条件的,循环出来的i和j也会有一个值,加一个if判断,若没有直接判空即可
}
int count=1,count1=1;
for(i=0;i<nums_1.length;i++){
if(nums_1[numidex[0]]==nums[i]){//因为排序过后的数组和原本的数组下标不一样,所以需要再原数组找到那两个数,并返回下标
if(count==1){
numidex[0]=i;//保存原数组的下标
count++;//这里的count和后面的count1意义一样,有效的防止了类似[3,3]这样有一样的数字,返回的下标一样的错误信息
continue;//既然这个数已经判断出来了,就没必要进入下一个if判断了
}
}
if(nums_1[numidex[1]]==nums[i]){
if(count1==1){
numidex[1]=i;
count1++;
continue;
}
}
}
return numidex;//最后返回我们保存的最终下标结果
}
该方法时间复杂度如下
O(nlogN+nlogN+nlogN)=O(3nlogN)=O(n*logN)
力扣测试结果如下
由于出现一些不完善,所以报错多次,最后修改数组下标问题时解决