两数之和,两数相加

1,两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

自主独立写法,只考虑回答正确

思路:两个for循环遍历把所有的两数之和来与targer比较,没有考虑性能优化,只负责回答正确

public int[] twoSum(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
        for (int j = i+1; j < nums.length; j++) {
            if(target == (nums[i] +nums[j])){
                return new int[]{i,j} ;
            }
        }
    }
    return  null ;
}

执行用时:54 ms, 在所有 Java 提交中击败了**27.36%的用户
内存消耗:41.9 MB, 在所有 Java 提交中击败了
16.24%**的用户
通过测试用例:57 / 57

时间复杂度:O(n^2)

考虑时间复杂度,用到Map

思路:用map中的键值对来进行存储,只用遍历一次

public int[] twoSum(int[] nums, int target) {
        int[] ints = new int[2];
        if (nums == null || nums.length==0){
            return ints ;
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int temp = target - nums[i] ;
            //到map中匹配另一半,匹配成功则找到了两数
            if (map.containsKey(temp)){
                ints[0] = i ;
                ints[i] = map.get(temp) ;
            }
            //将数值的值和下标都存储到map中
            map.put(nums[i] , i) ;
        }
        return ints ;
    }

执行用时:5 ms, 在所有 Java 提交中击败了**48.60%的用户
内存消耗:42.6 MB, 在所有 Java 提交中击败了
5.03%**的用户
通过测试用例:57 / 57

时间复杂度:O(n)

继续优化采用双指针思想

思路:在用while循环对数值从头尾两边进入map

    public int[] twoSum(int[] nums, int target) {
        int star = 0 , end = nums.length -1 ;
        HashMap<Integer, Integer> map = new HashMap<>();
        while (star < end){
            if (map.containsKey(target - nums[star])){
                return  new int[]{star , map.get(target -nums[star])} ;
            }else {
                map.put(nums[star] , star) ;
            }
            if (map.containsKey(target - nums[end])){
                return  new int[]{end , map.get(target -nums[end])} ;
            }else {
                map.put(nums[end] , end) ;
            }
            star++ ;
            end-- ;
        }
        return null ;
    }

执行用时:0 ms, 在所有 Java 提交中击败了**100.00%的用户
内存消耗:41.7 MB, 在所有 Java 提交中击败了
34.61%**的用户
通过测试用例:57 / 57

优点:提升了速度

一些问题
  • 为什么要用map,不用set等其他集合

2,两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

常规方法:迭代方法

实现思路:

我们可以遍历两个链表,并且同时从头开始对每个节点进行加法运算。由于链表是按照逆序存储的,因此我们可以从链表头开始遍历,将每个节点的值相加,然后构造一个新的链表来存储结果。如果两个链表的长度不相等,我们可以认为长度短的链表的缺失位为0

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            //创建一个零节点
            ListNode listNode = new ListNode(0);
            ListNode p = l1 ,q = l2 ,curr = listNode ;
            //进位数,用来记录进位
            int carry = 0 ;
            //循环两个链表直到两个都为空为止
            while (p!=null || q!=null){
                //将俩个链表的数相加
                int a = (p !=null)? p.val : 0 ;
                int b = (q != null) ? q.val :0 ;
                int sum  = a + b + carry ;
                //保存进位
                carry = sum / 10 ;
                //创建新节点用来存储相加后的数,并接在curr节点后面
                curr.next = new ListNode(sum % 10);
                curr = curr.next ;
                //在将初始的l1,l2移到下一个节点
                if (p != null) p = p.next ;
                if (q !=null) q = q.next ;
            }
            //最后carry进位,在往后接一个节点
            if (carry>0){
                curr.next = new ListNode(carry) ;
            }
            return listNode.next ;
        }

算法时间复杂度:O(max(m,n)),取决于两个链表的最大长度

方法二:递归方法
public  ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        return addNewTwoNumbers(l1 ,l2 ,0) ;
    }


    private ListNode addNewTwoNumbers(ListNode l1, ListNode l2 , int carry){
        //设置递归结束条件
        if (l1==null && l2==null && carry==0)
            return null ;

         int sum =  (l1 !=null ? l1.val :0) + (l2 !=null ? l2.val :0) + carry;
         carry = sum / 10 ;
         ListNode node = new ListNode(sum % 10);
        node.next = addNewTwoNumbers(l1!=null ? l1.next:null , l2!=null ? l2.next:null ,carry) ;
        return node;
    }
一些问题
  • 迭代和递归的区别(chatgpt)
    迭代是指通过**循环结构**来反复执行一段代码,以达到某种目的。迭代的优点是能够明确控制循环次数,能够在循环过程中方便地修改循环变量的值,并且能够在循环结束后直接返回结果。缺点是代码结构可能比较繁琐,容易出现深度嵌套,不易读写和维护。

递归是指通过**函数的调用**来反复执行一段代码,以达到某种目的。递归的优点是能够简化算法的实现,使代码更加清晰易读,并且能够处理一些复杂的问题,如树的遍历、图的搜索等。缺点是递归需要消耗大量的系统资源,容易导致堆栈溢出等问题,并且可能会出现死循环等错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值