Leetcode题库 - 三数之和(java语言版)

题目描述:

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

     看到这道题就能想到我做过的第一道题:两数之和。根据两数之和的经验,这道题有三种思路。

     两数之和博客https://blog.csdn.net/weixin_37850160/article/details/86500847

     第一种思路:暴力for循环,直接三层for嵌套,判断输出即可,这个是最简便最容易想的的问题,但时间复杂度太高O(n^3),很容易超时,所以不推荐使用这种方法。

     第二种思路:就是用对撞指针法,这种方法前提是先排序,然后遍历一层循环,让除了一个元素的其他元素进行对撞指针,定义一个首指针和一个尾指针,然后判断,a+b+c=0,得到想要结果,重点 在于去重,就是前后两个元素重复了,已经比较过了就跳过,继续下一趟循环。

     第三种思路:就是用查找表,用map存储元素和其对应的下标,a+b+c=0,a+c=-c,利用这个思路来进行遍历判断,判断c是否在map中,在map中时就证明 找到了可以时这个满足加起来等于0的条件。重点还是在去重,和上面一样去重的思路。

    具体代码:

    第一种方法就不写代码了,所有人应该都会。这种结果提交也是会超时。

   第二种方法:

 第二种方法,对撞指针法(先排序),时间复杂度O(n^2),遍历数组,让除了第二个元素的其它元素,定义两个指针,一前一后,进行判断。
                       Arrays.sort (a);//重点必须先排序
            List<List<Integer>> list2 = new ArrayList <> (  );


            for(int i = 0;i<a.length-2;i++) {
//                除了每次第一个元素外,其余元素进行对撞指针的判断(切记,首指针要小于尾指针)
                int k=a.length - 1;
//                去重,当后一个元素和前一个元素相同时,就将i++,跳过重复的元素
                if (i != 0 && a[i] == a[i - 1]) {
                    continue;
                }
                    for (int j=i + 1; j < k; ) {
//                     加起来大于0,就将末尾的指针向前移动
                        if (a[j] + a[k] > -a[i]) {
                            k--;
//                          小于0,将前面的指针向后移动
                        } else if (a[j] + a[k] < -a[i]) {
                            j++;
                        } else {
//                          等于0得到结果,加入到list中
                            List <Integer> list=new ArrayList <> ( );
                            list.add ( a[i] );
                            list.add ( a[j] );
                            list.add ( a[k] );
                            list2.add ( list );
//                             去重,前后两个元素相同则,++跳过
                            while (j < k && a[i] == a[j + 1]) j++;
//                             去重,前后两个元素相同则,--跳过
                            while (j < k && a[k] == a[k - 1]) k--;
//                             去重复,添加到大列表中
//                             if (!list2.contains ( list )){
//                                 list2.add ( list );
//                             }
                            //                            去重
                            j++;
                            k--;

                        }
                    }

            }
        System.out.println(list2);

执行结果:

  执行用时:

   

第三种方法:

   第三种方法,查找表(有一个存数据的地方,便于查找),map,set等等
        Arrays.sort ( a );
        Map<Integer,Integer> map = new HashMap <> (  );
        Set<List<Integer>> set = new HashSet <> (  );
        List<List<Integer>> lists = new ArrayList <> (  );
//        先将所有元素添加到mpa中
        for (int i= 0;i<a.length;i++){
            map.put ( a[i],i );
        }
        for (int i=0;i<a.length;i++){
            if (i!=0&&a[i] == a[i-1]){
                    continue;
                }
            for (int j =i+1;j<a.length;j++){
//                   判断a+b+c=0---->c=-(a+b),c是否在map中,并且同一个元素不能颠倒使用
                  int rel = -a[i]-a[j];
                  if (map.containsKey (rel)&&map.get ( rel)>j&&map.get ( rel)>i){
                      List<Integer> list = new ArrayList <> (  );
                      list.add ( a[i] );
                      list.add ( a[j] );
                      list.add ( rel);
                      while (j<a.length-1&&a[j]==a[j+1]) j++;
//                      去重,用set,可以添加到set中的就方到list中
//                      if (set.add ( list )){
                            lists.add ( list );
//                      }
                  }
            }
        }
        System.out.println(lists);

执行结果:

执行用时:

      总结:暴力循环不推荐,推荐双指针和查找表,时间复杂度O(n^2),这道题两数之和特别像,只要抓住两数之和,沿着这个思路,将三数,四数,都转化为两数之和求解判断。

      2019-3-21

发布了43 篇原创文章 · 获赞 6 · 访问量 6662

猜你喜欢

转载自blog.csdn.net/weixin_37850160/article/details/88746720