前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LeetCode 上第一题和第二题你会讲么?

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

作者头像
Java极客技术
发布2022-12-04 14:10:50
3400
发布2022-12-04 14:10:50
举报
文章被收录于专栏:Java极客技术Java极客技术

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

LeetCode 第一题

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

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

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

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

代码语言:javascript
复制
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²)

求和方法二

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

代码语言:javascript
复制
for(int j=(i+1);j<nums.length;j++){
    if(nums[i]+nums[j]==target){

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

于是第二种算法就来了

代码语言:javascript
复制
 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)!

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

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

代码语言:javascript
复制
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 开头。

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

图示如下:

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

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

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

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

代码如下:

代码语言:javascript
复制
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;
    }

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java极客技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • LeetCode 第一题
  • 求和方法二
  • LeetCode 第二题
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档