LeetCode 上第一题和第二题你会讲么?

lzhlzh263
发布于 2023-1-11 14:24
浏览
0收藏

阿粉最近也在刷 LeetCode 上面的题,因为 LeetCode 的题,很多都是在注重算法的实践上,殊不知,阿粉在前几道题目上就写出了像是垃圾一样的代码。看了其他的代码,瞬间感觉为什么自己没有思考出来这么好的方法呢?

LeetCode 第一题

LeetCode 上面的第一道题,也是入门基础的题,求和。

LeetCode 上第一题和第二题你会讲么?-鸿蒙开发者社区

给定一个数组和一个目标和,从数组中找两个数字相加等于目标和,输出这两个数字的下标。


这个题是什么意思呢?相信大家肯定也都知道,给你个数组,数组里面有一堆的数据,然后再给你一个数,找出数组里面相加等于这个数字的元素,并且把元素位置输出出来。


阿粉看到这个基础题目,心里在想,非常简单呀,直接双层循环,然后计算求和相等就输出就完事了,尴尬了,阿粉写出了一种最暴力的写法,写法如下:

public int[] twoSum(int[] nums, int target) {
    //定义返回数组
    int []ans=new int[2];
    //第一次循环
    for(int i=0;i<nums.length;i++){
        //第二次循环
        for(int j=(i+1);j<nums.length;j++){
            if(nums[i]+nums[j]==target){
                ans[0]=i;
                ans[1]=j;
                return ans;
            }
        }
    }
    return ans;
}

这是阿粉第一时间想到的方案,阿粉当时也冥思苦想还有没有其他的方案来实现,但是阿粉太low ,没有想到其他的解决方案,于是阿粉就开始所有,然后看看大家都是怎么想的,于是乎,发现了自己的想法确实还和广大网友差距不小呀,确实,阿粉的实现方式没什么错误,因为题目就是求和,功能实现了,但是效率缺是真的低,因为两层 for 循环。

时间复杂度:两层 for 循环,O(n²)

求和方法二

我们第一种方法的关键在于代码的这个地方:

for(int j=(i+1);j<nums.length;j++){
    if(nums[i]+nums[j]==target){

这个地方,我们使用的是加法,那么有没有可能使用减法呢?

于是第二种算法就来了

 public int[] twoSum(int[] nums, int target) {

        for(int i = 0;i < nums.length;i++){

            int first = nums[i];
            //最后一个数字定义在了这里 代码更加简洁
            int lastNum = target - first;

            for(int j = i+1;j < nums.length;j++){
            
                if(lastNum == nums[j]){
                    int[] resArray = {i,j};
                    return resArray;
                }
            }
        }
        return null;
    }

阿粉当时就感觉,果然,人外有人,天外有天呀,这不看不知道,算法不单单有这个循环的方式呀,如果不用循环的话,我们能不能有方法来实现这个求和的算法呢?


有,而且还是我们经常见到的,而且也是面试里面会经常被问到的,那就是 HashTablle 了。


我们可以把数组的每个元素保存为 hash 的 key,下标保存为 hash 的 value 。


这样只需判断 sub 在不在 hash 的 key 里就可以了,而此时的时间复杂度仅为 O(1)!


需要注意的地方是,还需判断找到的元素不是当前元素,因为题目里讲一个元素只能用一次。

那么代码是应该怎么实现呢?

public int[] twoSum(int[] nums, int target) {
        //为了代码的简洁美观   new的时候泛型就不限制了 对象的类型可以省略
        Map<Integer, Integer> hashmap = new HashMap<>(); 
        
        for (int i = 0; i < nums.length; ++i) {
        
            if (hashmape.containsKey(target - nums[i])) {
                return new int[]{hashmap.get(target - nums[i]), i};
            }
            
            hashmap.put(nums[i], i);
        }
        return new int[0];
    }

时间复杂度:比解法一少了一个 for 循环,降为 O(n)


空间复杂度:所谓的空间换时间,这里就能体现出来, 开辟了一个 hashtable ,空间复杂度变为 O(n)


题目比较简单,毕竟暴力也可以解决,唯一的亮点就是从时间的复杂度变得稍微缓和了一点 ,对于集合hash的使用,如果是在面试环节,你写出了不一样的操作,相信会亮眼一波。

LeetCode 第二题

如果第一题是基础,阿粉相信第二题,绝对属于那种摸不着头脑的,就像 最近比较火的 “羊了个羊”,第一关秒杀,第二关直接凉透了。

问题如下:

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

LeetCode 上第一题和第二题你会讲么?-鸿蒙开发者社区

这直接从简单难度直线上升到高级难度了,其实这只是一个稍微比简单难度高了一点点的。

图示如下:

LeetCode 上第一题和第二题你会讲么?-鸿蒙开发者社区


链表最左边表示个位数,代表 342 + 465 =807


我们应该怎么思考这个问题:


在看到这道题时,第一想法是逐个遍历链表的节点,将每个节点的数据取出并存储到String类型的数据中,再将这些String类型的字符数据拼接起来反转后再成int类型的数据。但是后来发现这么做是不对的,应该先定义一条新的链表按节点来逐个存储两条链表相加的值,最后将这条新的链表返回。


创建一条新的链表,设置两个头指针current和result,在两条链表相加的过程中只移动current指针,result指针不移动,最后将result返回。两条链表节点的值在相加时会有进位的情况,因此要进位的数与不进位的数(即剩余的数)进行获取和存储,因此再定义用来存储需要进位的int类型的变量total和存储不需要进位的数值的int类型变量remainder,将两个变量的初值均定义为0。


代码如下:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 定义一个链表用来存储两条链表相加的结果,result和current为两个指针
  // current为头结点不进行移动,result为最后要输出的链表
  ListNode result = null, current = null;

  // 定义total变量为l1和l2两条链表相加后需要进位的数据
  int total = 0;
  // 如果l1和l2两条链表相加后需要进位,remainder变量为个位数的值
  // 如果l1和l2两条链表相加后不需要进位,remainder为两数相加后的值
  int remainder = 0;

  // 只要有一条链表不为空就继续进行遍历
  while (l1 != null || l2 != null) {
   int num1 = l1 != null ? l1.val : 0;
   int num2 = l2 != null ? l2.val : 0;
   // 定义sum为两数相加后再与进位数相加后的值
   int sum = num1 + num2 + total;

   // 对两数相加的结果进行整除,取出进位的值
   total = sum / 10;
   // 对两数相加的结果对10进行取余,取出不需要进位的值
   remainder = sum % 10;

   if (result == null) {
    result = current = new ListNode(remainder);
   } else {
    // 将当前不需要进位的值赋值给下一个节点要存储的值
    current.next = new ListNode(remainder);
    // 将current指针向后移动
    current = current.next;
   }

   // 将l1和l2节点同时向后移动
   if (l1 != null) {
    l1 = l1.next;
   }
   if (l2 != null) {
    l2 = l2.next;
   }
  }
  if (total > 0) {
   current.next = new ListNode(total);
  }
  return result;
    }

说实话阿粉确实是没有想到,后来完全是看了解析答案之后,才能完整的滤通这个代码,你们也是这样么?你也有刷题的习惯么?欢迎大家踊跃回答。


本文转载自公众号:java极客技术

分类
标签
已于2023-1-11 14:24:30修改
收藏
回复
举报
回复
    相关推荐