这是我的第一篇刷题处女作,写作的目的主要有个:
第一:记录自己所学的知识,加深印象,巩固基础。
第二:分享自己的知识经验,希望能够帮助广大的互联网小伙伴们。
第一次写算法,没有经验,希望小伙伴们能够指出我的错误,让我继续成长。
第一题:给定一个序列,找出其中出现了超过一半的数。
第一题比较简单,可以使用方法也很多,下面介绍三中方法来解决这个问题:
- 排序
public int getNumMoreHalf(int[] a){
//快速排序,这里最好是将快排写出来,非非比较懒,就没有写了。
Arrays.sort(a);
//我们将一个数组排序之后,那么a.length/2这个位置必然是出现超过一半的数。
return a[a.length/2];
}
时间复杂度:O(nlg2n)
空间复杂度:O(lg2n),主要体现在递归上。
- HashMap计数
public int getNumMoreHalf(int[] a){
//key:a[i] value:a[i]出现的次数
Map<Integer,Integer> map=new HashMap<>();
//统计计数
for(int i=0;i<a.length;i++){
//这个方法用来计数统计真的很方便呢,如果Map中有a[i],就把原来的值+1,否则就是0+1。
map.put(a[i],map.getOrDefault(map.get(a[i]),0)+1);
}
int num=0;
//找到结果
for(Map.Entry<Integer,Integer> m:map.entrySet()){
if(m.getValue()>a.length/2){
num=m.getKey();
}
}
return num;
}
时间复杂度:O(n)
空间复杂度:O(n)
- 摩尔投票(2个不同的数相互抵消,那么最后剩下的一定是那个超过一半的数)
public int getNumMoreHalf(int[] nums ){
int count=0,target=0;
for(int i=0;i<nums.length;i++){
if(count==0) target=nums[i];
count+=nums[i]==target?1:-1;
}
//如果可能不存在超过一半的数,那么我们最后还需要判断这个数字出现的次数知否超过一半,
//因为摩尔投票算法只能求出超过一半的数,并不能求出数学上说的众数(出现次数最多的数)
return target;
}
摩尔投票法:
- 票数和: 由于众数出现的次数超过数组长度的一半;若记 众数 的票数为 +1 ,非众数 的票数为 −1 ,则一定有所有数字的 票数和 > 0 。
- 票数正负抵消: 设数组 nums 中的众数为 x ,数组长度为 n 。若 nums 的前 a 个数字的 票数和 = 0 ,则 数组后 (n−a) 个数字的 票数和一定仍 >0 ,即后(n−a) 个数字的 众数仍为 x 。
- 示意图如下
以上说的众数即时出现超过一般的数。
时间复杂度:O(n)
空间复杂度:O(1)
第二题:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s,找出一组即可。
这一题也是比较简单的,同样介绍四种方法来解决这个问题:
- 暴力破解
public int[] getTargetNum(int[] a,int target){
int sum=0;
for(int i=0;i<a.length;i++){
for(int j=i+1;j<a.length;j++){
if((a[i]+a[j])==target){
return new int[]{a[i],a[j]};
}
}
}
return new int[]{};
}
这个就不用写注释了吧,相信小伙伴都能看的懂的。
时间复杂度:O(n^2)
空间复杂度:O(1)
- 二分法
public int[] getTargetNum(int[] a,int target){
for(int i=0;i<a.length;i++){
int left=i+1,right=a.length-1,num=target-a[i];
//在暴力破解的基础上,第二层循环使用二分法
while(left<=right){
int middle=left+(right-left)/2;
if(a[middle]>num){
right=middle-1;
}else if(a[middle]<num){
left=middle+1;
}else {
return new int[]{a[i],a[middle]};
}
}
}
return new int[]{};
}
在暴力破解的基础上,利用数组有序的特性在第二层循环使用了二分法,提高了效率。
时间复杂度:O(nlog2n)
空间复杂度:O(1)
- HashMap法
public int[] getTargetNum(int[] a,int target){
Set<Integer> set=new HashSet<>();
for(int i=0;i<a.length;i++){
//在将每一个元素a[i]加入到HashMap的时候,我们使用O(1)的时间的复杂度查看target-a[i]是否也在HashMap中。
if(set.contains(target-a[i])){
return new int[]{target-a[i],a[i]};
}
set.add(a[i]);
}
return new int[]{};
}
在将每一个元素a[i]加入到HashMap的时候,我们使用O(1)的时间的复杂度查看target-a[i]是否也在HashMap中。,由于HashSet的底层实现是Hash Map,于是我在这个就将其称呼为HashMap。
时间复杂度:O(n)
空间复杂度:O(n)
- 双指针
public int[] getTargetNum(int[] a,int target){
//定义2个指针,一个指向数组的左边,一个指向数组的右边。
int i=0,j=a.length-1;
//将2个指针向右向左移动来获得最终的答案。
while(i<j){
if(a[i]+a[j]<target){
i++;
}else if(a[i]+a[j]>target){
j--;
}else{
return new int[]{a[i],a[j]};
}
}
return new int[]{};
}
示意图如下:
时间复杂度:O(n)
空间复杂度:O(1)
以上2道题都是比较简单的题,但是都很好的体现了算法技巧与思维,实在是入门必刷的题。