两数之和
题目
我的思路
利用一个嵌套循环,外循环从下标0到length-1(不包括length-1),内循环从1到length(不包括length),判断是否等于target。
这种做法我感觉很low,效率会很低,同一个元素用了很多次。
public int[] twoSum(int[] nums, int target) {
int[] result = new int[2];
for(int i = 0;i<nums.length-1;i++){
for(int j = 1;j<nums.length;j++){
if(nums[i]+nums[j]==target){
result[0] = i;
result[1] = j;
break;
}
}
}
return result;
}
leetcode解析
我这属于暴力法。完全是最简单的思路。效率应该最低。
解析中有hashmap的解法,分别是两遍hash表和一遍hash表。
解析解法-两遍hash表
为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么?哈希表。
通过以空间换取速度的方式,我们可以将查找时间从 O(n) 降低到 O(1)。哈希表正是为此目的而构建的,它支持以 近似 恒定的时间进行快速查找。我用“近似”来描述,是因为一旦出现冲突,查找用时可能会退化到 O(n)。但只要你仔细地挑选哈希函数,在哈希表中进行查找的用时应当被摊销为 O(1)。
一个简单的实现使用了两次迭代。在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target - nums[i]target−nums[i])是否存在于表中。注意,该目标元素不能是 nums[i]nums[i] 本身!
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 complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
总结:这种方式没有用嵌套,没有用暴力破解的方法,摒弃了全拿过来挨个算一下的思想。采用的思想是,先逮住一个数,与目标值一起计算求差值,然后利用hash去定位是否存在这个差值,当然要想定位哦这个差值得做为key,然后要求输入的数组不能有重复的值,这都是一些前提条件。判定的时候,需要定位+非i,这两个条件一起成立才可以。典型的空间换时间,一定程度上提升了效率。
复杂度分析:
解析解法-一遍hash表
事实证明,我们可以一次完成。在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
两数相加
题目
我的做法
结果是做的稀烂,没做出来,贴出来支离破碎的代码:
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode listNodeResult = null;
ListNode currentNode = null;
int temp = 0;
while(true){
temp=l1.val+l2.val;
if(listNodeResult==null&&temp<10){
listNodeResult = new ListNode(temp);
currentNode = listNodeResult;
}else if(listNodeResult==null&&temp>=10){
listNodeResult = new ListNode(temp-10)
listNodeResult.next = new ListNode(1);
currentNode = listNodeResult;
}else{
currentNode = currentNode.next;
//下面是2次以后的处理
if(temp<10){
}else{//>=10
}
}
}
}
总结:完全是用if可能性的方法在思考,没有任何的数学逻辑在里面。所以写到最后写不进去了。拿到题,先要想好策略再下手,否则写着写着就写不下去了,这也算是教训。思路不清晰就先写注释列清楚逻辑。
leetcode解析
我们使用变量来跟踪进位,并从包含最低有效位的表头开始模拟逐位相加的过程。
图1,对两数相加方法的可视化: 342 + 465 = 807342+465=807,每个结点都包含一个数字,并且数字按位逆序存储。
请注意,我们使用哑结点来简化代码。如果没有哑结点,则必须编写额外的条件语句来初始化表头的值。 哑节点:头节点初始化为0
java代码
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
//看到这里,做题中想到的一个每一个链表都要有当前位置的想法是正确的。
ListNode p = l1, q = l2, curr = dummyHead;
//考虑一下进位 0 或者 1
int carry = 0;
//考虑链表不一样长
while (p != null || q != null) {
//考虑不一样长的情况,特殊处理x和y
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
//计算进去进位值
int sum = carry + x + y;
//计算是否有进位,最大不过1
carry = sum / 10;
//首节点为0的哑节点,统一为只考虑next
curr.next = new ListNode(sum % 10);
//三条链表统一往后移动
curr = curr.next;
if (p != null) p = p.next;
if (q != null) q = q.next;
}
//最后一位如果出现进位,则新增一个节点
if (carry > 0) {
curr.next = new ListNode(carry);
}
//返回的时候不包括哑节点就ok了
return dummyHead.next;
}
总结:利用算法和思想,逻辑就很清晰了,多学学算法很有必要呀。