最短重复子数组,算法怎么优化,萌新求助大佬

嘉陵辣条江 2019-03-19 02:16:04
需求
最短重复子数组
给定一个数组,返回具有重复元素的最短子数组长度。
如果数组没有具有重复元素的子数组,则返回 -1。
样例 1:
输入:[1,2,3,1,4,5,4,6,8]
输出:3
解释:具有重复元素的最短子数组是 [4,5,4]。

样例 2:
输入:[1,1]
输出:2
解释:具有重复元素的最短子数组是 [1,1]。

============下面是我写的代码=============================

public int getLength(int[] arr) {
//定义集合存放重复子数组长度
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
for (int j = i+1; j < arr.length; j++) {
if(arr[i]==arr[j]){
list.add(j-i+1);
}
}
}
//如果没有重复子数组返回-1
if(list.size()==0){
return -1;
}else {
//返回最小的子数组长度
int min=list.get(0);
for (int i = 1; i < list.size(); i++) {
if(min>list.get(i)){
min=list.get(i);
}
}
return min;
}
}
===========下面是在LinCode上的测试结果===========================


你的代码运行时间超过了限制,检查你的时间复杂度。TLE通常是由死循环造成的,思考一下你的时间复杂度是否是最优

=========================================================

求助大佬们怎么优化!!!!!!
...全文
373 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
嘉陵辣条江 2019-03-21
  • 打赏
  • 举报
回复
感谢大佬解答
weixin_44749415 2019-03-21
  • 打赏
  • 举报
回复
楼主是不是说错了,找最长重复回文吧? 最短的话,只能是2或者3或者不存在 那还不简单吗 先判断第一个和第二个数是否相等,相等答案就是2 然后从第三个开始循环,判断它和前两个中任意一个相等,就得到结果 循环完了,还没有结果,就是不存在
qybao 2019-03-21
  • 打赏
  • 举报
回复
算法1 遍历全部找最小(改进:前后遍历+比较位置更新),你可以试试看改进后有无改善 int p,q, min = a.length; for (int i=0; i<a.length/2+a.length%2; i++) { p = i, q = a.length-i-1;//开始比较点位置 if (q>=p) beak;//前后比较点位置有交互,说明前后比较过了,可以不用比了 for (int j=i+1; j<a.length-i; j++) { if (a[p]==a[j]) {//从前面开始 if (j-p+1<min) min=j-p+1; p=j;//更换比较位置,因为隔开的重复数字的距离肯定比相邻的重复数字的距离大 } if (a[q]==a[a.length-j-1]) {//后面开始 if (q-(a.length-j-1)+1<min) min=q-(a.length-j-1)+1; q=a.length-j-1;//更换比较位置 } if (min==2) return min;//最小位置 } } if (min==a.length) return -1; return min; 从算法角度来说,10楼12楼都属于这个算法思路,全遍历找最小,只是实现方式不同,借助map,10楼是遍历过程中找最小,12楼是遍历完以后找最小,理论上10楼效率要高一些 算法2是9楼的思路,不需要遍历全部(算法1是遍历全部后才能确定min,比如当找到min为3的时候,不遍历完不知道还有没有更小的min是2),而算法2正好改善这个问题,从最短距离开始找,从效率上来说要高于算法1(极端情况下是一样的,都找不到的情况下都是全遍历)
qps2009 2019-03-20
  • 打赏
  • 举报
回复
引用 8 楼 tianfang 的回复:
第一步的代码上来了 ,试试继续写


	public static void main(String[] args) {

		int[]  srcList= {1,2,3,1,4,5,4,6,8}; 
		
		Map<Integer, List<Integer>> nums=new HashMap<Integer, List<Integer>>();
		
		for (int i=0;i<srcList.length;i++) {
			if (nums.containsKey(srcList[i])) {
				nums.get(srcList[i]).add(i);
			}else {
				List<Integer> list=new ArrayList<Integer>();
				list.add(i);
				nums.put(i, list);
			}
		}
		
	}
从这位朋友的代码中得到的思路,用map只循环一遍就能实现(貌似HashMap.containsKey()的复杂度只有O(1)?),用arr[i]的值作为map的key,位置i作为map的value。
public static int getLenght1(Integer[] arr) {
        Map<Integer,Integer> map=new HashMap<>();
        int min=arr.length;
        for(int i=0;i<arr.length;i++){
            if(map.containsKey(arr[i])){//遍历时判断map是否已经存了该key值,即判断是否有重复的元素
                if((i-map.get(arr[i]))+1<min){//将新的位置i同已经存在map中对应key值的value相减并与min比较,将较小值赋值给min
                    min=(i-map.get(arr[i]))+1;
                }
            }
                map.put(arr[i],i);
        }
        if(min==arr.length)//如果min还是等于数组长度,说明没有重复元素,直接返回-1
            return -1;
        else
            return min;
    }
用之前3楼的代码测试999999个数,花费时间为:105365ms 这个代码测试999999个数,花费时间为:78ms。这个代码的准确性我暂时还没测试,只是简单的测试了一下没问题
qq_39936465 2019-03-20
  • 打赏
  • 举报
回复
减少时间复杂度就是减少for循环的次数。

我觉得你只要最小间隔数,完全没必要全部历遍。

最短子字符串范围是2-length,所以第一层循环2<=i<=arr.length

最短字符串就是比较相邻2个数是否相等,那么以此类推 0<=j<(arr.length-i+1)

public static int getLength(int[] arr){
for (int i=2;i<arr.length+1;i++){
for(int j=0;j<arr.length-i+1;j++){
if(arr[j]==arr[j+i-1])return i;
}
}
return -1;
}
tianfang 2019-03-20
  • 打赏
  • 举报
回复
第一步的代码上来了 ,试试继续写


	public static void main(String[] args) {

		int[]  srcList= {1,2,3,1,4,5,4,6,8}; 
		
		Map<Integer, List<Integer>> nums=new HashMap<Integer, List<Integer>>();
		
		for (int i=0;i<srcList.length;i++) {
			if (nums.containsKey(srcList[i])) {
				nums.get(srcList[i]).add(i);
			}else {
				List<Integer> list=new ArrayList<Integer>();
				list.add(i);
				nums.put(i, list);
			}
		}
		
	}
tianfang 2019-03-20
  • 打赏
  • 举报
回复
上面的代码有个错误 nums.put(i, list); 应该为 nums.put(srcList[i], list); 直接上全部代码


public static void main(String[] args) {

		int[]  srcList= {1,2,3,1,4,5,4,6,8}; 
		
		Map<Integer, List<Integer>> nums=new HashMap<Integer, List<Integer>>();
		
		for (int i=0;i<srcList.length;i++) {
			if (nums.containsKey(srcList[i])) {
				nums.get(srcList[i]).add(i);
			}else {
				List<Integer> list=new ArrayList<Integer>();
				list.add(i);
				nums.put(srcList[i], list);
			}
		}
		
		Integer min=Integer.MAX_VALUE;
		
		int start=0, end=0,diff=0;
		
		for (Integer key: nums.keySet()) {
			 List<Integer> tmp=nums.get(key);
			 if(tmp.size()==1) continue;
			 for (int i=0; i<(tmp.size()-1);i++) {
				 diff=tmp.get(i+1)-tmp.get(i);
				 if (diff==1) {
					 start=tmp.get(i);
					 end=tmp.get(i+1);
					 min=1;
					 break;
				 }
				 if (diff<min) {
					 min=diff;
					 start=tmp.get(i);
					 end=tmp.get(i+1);
				 }
			 }
		}
		
	System.out.print("Min sublist length is: "+(min+1)+" from "+start + "to "+end);
		
		
	}
tianfang 2019-03-20
  • 打赏
  • 举报
回复
还可以优化 比如1的位置是{2,3,6},只要计算相邻的两个数差就够了。这样只要一级循环, 计算3-2,6-3,取最小。不需要二级循环
qybao 2019-03-19
  • 打赏
  • 举报
回复
前后一起找,遍历减半a.length/2 + a.length%2,试试看 if a[i]==a[j] || a[a.length-i-1] == a[a.length-j-1] 注意一下中间位置j>=a.length-j-1
嘉陵辣条江 2019-03-19
  • 打赏
  • 举报
回复
引用 3 楼 qps2009 的回复:
这是我的思路,相当于省去了从list里面去查找最小值这一步,但感觉还可以优化一下,只是暂时还没想到
public static int getLenght(int[] arr){
int min=arr.length;
for(int i=0;i<arr.length;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]==arr[j]){
if(j-i+1==2)//如果有重复元素,且长度为2,那么2就是最小长度,直接返回
return 2;
else if((j-i+1)<min)//遍历的过程中直接将最小的长度赋值给变量min
min=j-i+1;
}
}
}
if(min==arr.length)//如果min还是等于数组长度,说明没有重复元素,直接返回-1
return -1;
else
return min;
}

感谢,但是实际测试结果还是差不多
嘉陵辣条江 2019-03-19
  • 打赏
  • 举报
回复
引用 2 楼 qybao的回复:
循环的时候你直接保存min就可以了,没必要循环结束后再查找min
int min = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i+1; j < arr.length; j++) {
if(arr[i]==arr[j]){
if (j-i+1 < min) {
min = j-i+1;
}
}
}
}
if (min == Integer.MAX_VALUE) return -1; //没有重复
return min;
感谢大佬,但是我测试运行之后还是超时,3321ms,更我之前的差不多
嘉陵辣条江 2019-03-19
  • 打赏
  • 举报
回复
引用 1 楼 tianfang的回复:
1. 先找到重复数字 记录为 1:{2,3,6}后面为1在数组中的位置 2. 从1的位置(2,3,6)中找到差最小的一对,2,3,其他数值同样计算。如果得到差为1直接返回 3. 比较全部最小差,取最小
没看懂,感觉自己好弱鸡,大佬可不可以上下代码啊
qps2009 2019-03-19
  • 打赏
  • 举报
回复
这是我的思路,相当于省去了从list里面去查找最小值这一步,但感觉还可以优化一下,只是暂时还没想到
public static int getLenght(int[] arr){
        int min=arr.length;
        for(int i=0;i<arr.length;i++){
            for(int j=i+1;j<arr.length;j++){
                if(arr[i]==arr[j]){
                    if(j-i+1==2)//如果有重复元素,且长度为2,那么2就是最小长度,直接返回
                        return 2;
                    else if((j-i+1)<min)//遍历的过程中直接将最小的长度赋值给变量min
                        min=j-i+1;
                }
            }
        }
        if(min==arr.length)//如果min还是等于数组长度,说明没有重复元素,直接返回-1
            return -1;
        else
            return min;
    }
qybao 2019-03-19
  • 打赏
  • 举报
回复
循环的时候你直接保存min就可以了,没必要循环结束后再查找min
int min = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i+1; j < arr.length; j++) {
if(arr[i]==arr[j]){
if (j-i+1 < min) {
min = j-i+1;
}
}
}
}
if (min == Integer.MAX_VALUE) return -1; //没有重复
return min;
tianfang 2019-03-19
  • 打赏
  • 举报
回复
1. 先找到重复数字 记录为 1:{2,3,6}后面为1在数组中的位置 2. 从1的位置(2,3,6)中找到差最小的一对,2,3,其他数值同样计算。如果得到差为1直接返回 3. 比较全部最小差,取最小

67,515

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧