每天一道leetcode之两数之和

前言

毕业一段时间了,但是在工作中数据结构和算法好像不是使用的特别广泛,甚至夸张地说大多时候用不上,不过这又面试必考,而且也是作为一个程序猿必要掌握的技能,可以用不上但是不能不会,我自我觉得这方面能力差一些,为了锻炼自己的多语言代码能力,打算使用C++、Java、Scala、Python、Julia,从头开始刷一遍LeetCode。使用博客记录就当打卡了。当然水平有限,如果有不对的地方,希望大家不吝赐教。

两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

问题分析

如果是面试过程中我们遇到问题,第一应该做的是与面试官沟通各种边界情况,及问题的模棱两可的地方。等一切都确定了,再开始做。否则上来就做,做到一半的时候才发现各种问题,大大浪费时间,而且给人的印象也不好。
这道题说白了就是给你一个数,一个数组,这个数组里面有唯一的一对数的和等于之前的这个数。

暴力破解

对我来说这种题上来不管三七二十一,就先暴力来一发,从头到尾遍历数组,指定一个数后,拿目标值减去这个数得到差值,然后再遍历数组看是否有等于差值的元素,如果有得到答案返回结果,如果没有遍历下一个数重复执行此过程。

Java暴力解法

class Solution {
    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(nums[j] == target - nums[i]) {
                    return new int[]{i, j};
                }
            }
        }
        System.out.printf("输入数组没有两数之和等于目标数")
        return null;
    }
}

Python解法

class Solution:
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        for index_i, value_i in enumerate(nums):
            if target - value_i in nums and nums.index(target - value_i) != index_i:
                return [index_i, nums.index(target - value_i)]
            else:
                continue
        print("检查输入,没有目标结果")
        return None

Scala暴力解法

object Solution {
    def twoSum(nums: Array[Int], target: Int): Array[Int] = {
        for(i <- 0 until nums.length) {
            for (j <- i + 1 until nums.length) {
                if (target - nums(i) == nums(j))
                    return Array(i, j)
            }
        }
        return Array.empty
    }
}

优化

既然暴力解法已经完成,那我们考虑接下来的优化。我们之前是遍历数组,然后得到目标数减去这个数的差值,再去遍历数组找到这个差值的下标。两次循环。那么可不可以减少第二次查找差值的时间?就是可以在O(1)的复杂度下,完成第二次查找。什么数据结构可以在O(1)复杂度下查找?我的第一反应就是哈希表。用空间换时间,可是哈希表就有另一个问题。就是哈希冲突。知识点来了,解决哈希冲突的方法都有什么。1.开放地址法,2.拉链法。其中开放地址法又分为1.线性探测法2.线性探测补偿法3.伪随机法。具体这里就不展开说明了。好吧,写到这里,大家会发现,上面的python版本只有一次遍历,但是python的index方法内部实现是O(N)。

Java 第一次优化版本

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int i =0; i < nums.length; i++) {
            map.put(nums[i], i);
        }
        for(int i = 0; i < nums.length; i++) {
            int secondNum = target - nums[i];
            if (map.containsKey(secondNum) && map.get(secondNum) != i) {
                return new int[] { i, map.get(secondNum) };
            }
        }
        return null;
    }
}

再次优化

我们可以看到,我们事先将map建立完再去遍历,这个的空间复杂度是O(N),时间复杂度是O(N),但是我们可以在遍历的同时,一边建立map一边去map里面查。

终极优化版本

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        
        for(int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
            int secondNum = target - nums[i];
            if (map.containsKey(secondNum) && map.get(secondNum) != i) {
                return new int[] { i, map.get(secondNum) };
            }
        }
        return null;
    }
}

Golang版本

func TwoSumResForTarget(nums []int, target int) []int {
	tmpMap := make(map[int]int, len(nums))
	for i, v := range nums {
		tmpVal := target - v

		if index, ok := tmpMap[tmpVal]; ok {
			return []int{i, index}
		}
		tmpMap[v] = i
	}
	return nil
}

总结

我觉得面试遇到手写算法题(如果是这种简单地问题,还是很幸运的),最开始就不用先考虑那些优化的问题,就大力出奇迹就行,反正你写完了,还会让你优化的,和面试官交流的过程也是一个成长的过程,但是如果你上来在很短的时间内就写的明明白白的,而且是最优解,那么面试官会有两种想法,第一种:卧艹,牛逼啊,那我赶紧加大难度,要不然我岂不是很没有面子。第二种,这道题他做过。这两种想法对你来说都不是好事。

其中有参考LeetCode的实现,如果侵权请及时联系我。
谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值