首页

算法-两数之和&相加&无重复最长子串

今日提交三个算法题:两数之和、两数相加和最长无重复字符子串。

一、两数之和

难度:简单

题目传送门:两数之和

题目

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

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  1. 2 <= nums.length <= 10^4
  2. -10^9 <= nums[i] <= 10^9
  3. -10^9 <= target <= 10^9
  4. 只会存在一个有效答案

思路

将number放到map中,然后遍历数组,找target-数组元素是否存在于map中。命中条件:存在,且下标不相同。

代码

func twoSum(nums []int, target int) []int {
    m := make(map[int][]int, len(nums))
	for i, n := range nums {
		if a, ok := m[n]; ok {
			m[n] = append(a, i)
		} else {
			m[n] = []int{i}
		}
	}
	for i, n1 := range nums {
		if js, exist := m[target-n1]; exist {
			for _, j := range js {
				if j == i {
					continue
				}
				return []int{i, j}
			}
		}
	}
	return nil
}

二、两数相加

难度:中等

题目传送门:两数相加

题目

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

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

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

提示:

  1. 每个链表中的节点数在范围 [1, 100] 内
  2. 0 <= Node.val <= 9
  3. 题目数据保证列表表示的数字不含前导零

思路

对应位置相加,进位保留在nextNode中,结束条件为:进位为0,且两个列表均遍历完成。

代码

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
	nextA, nextB := l1, l2
	nextNode := new(ListNode)
	begin := nextNode

	for {
		var a, b int
		if nextA != nil {
			a = nextA.Val
			nextA = nextA.Next
		}
		if nextB != nil {
			b = nextB.Val
			nextB = nextB.Next
		}
		preVal := nextNode.Val
		nextNode.Val = (preVal + a + b) % 10

		next := new(ListNode)
		next.Val = (preVal + a + b) / 10

		if nextA == nil && nextB == nil && next.Val == 0 {
			break
		}
		nextNode.Next = next
		nextNode = next
	}
	return begin
}

三、无重复字符的最长子串

难度:中等

题目传送门:无重复字符的最长子串

题目

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是"wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke"是一个子序列,不是子串。

提示:

  1. 0 <= s.length <= 5 * 104
  2. s 由英文字母、数字、符号和空格组成

思路

设置滑动窗口,窗口为不重复的子串。每个子串需要计算最大长度,选最大的保留下来。

代码

func lengthOfLongestSubstring(s string) int {
    if s == "" {
		return 0
	}

	// 用来存储字符的unicode编码对应的最新出现的位置
	// 128仅能存放size为一个字节的字符,如果超过,则需要设置更大一些
	last := make([]int, 128)
	for i := range last {
		last[i] = -1
	}

	max, start := 0, 0
	for i := 0;i<len(s);i++ {
		r := s[i]
		if last[r] > -1 {
			// 字符在前面出现过
			if last[r]+1 > start {
				start = last[r] + 1
			}
		}

		if i-start+1 > max {
			max = i - start + 1
		}
		last[r] = i
	}

	return max
}

感想

这个题做了50分钟,提交第一次正确答案花费了20分钟,成绩为:156ms 6.7MB.

序号 执行用时(ms) 内存消耗(MB) 提交时间
1 156 6.7 23:23
2 20 6 23:37
3 4 2.7 23:53
4 <1 2.3 23:55

最开始的想法是:使用哈希表来存储字串的子符,每次需要重新计算子串时,循环清空哈希表。这样导致多次多处循环,且内存消耗大。

后续改变思路,通过unicode编码在数组中的映射,直接记录字符出现的位置,避免用哈希表等存储临时字符串的的问题。极大的优化了CPU和内存消耗。