目錄
題目描述
給定一個整數數組nums和一個目標值target,請你在該數據中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素不能使用兩遍。
輸入/輸出示例:
輸入條件 | 給定nums = [2, 7, 11, 15], target = 9 |
返回值 | [0, 1] |
解釋 | 因爲nums[0] + nums[1] = 2 + 7 = 9,所以返回[0, 1] |
解決方案
方案一:二重循環檢索
在二重循環裏查找是否存在兩個值的和與target相等。
for _, i := range nums:
for _, j := range nums:
if i + j == target {
// find!
}
如果找到滿足條件的值,就講對應元素的下標收集起來。最終將所有滿足條件的下標所組成的列表返回。
但是這個方案有一個致命的缺點:時間複雜度爲,也就是說大量的時間被浪費在重複的計算上,因此不是最優解。
方案二:使用Map和集合查找
集合Set是一個不重複的序列。在Go中由於不存在Set類型,我們可以使用Map來構造Set。
減少無意義的循環嵌套是提高運行效率的方法。將數組切片存放在Map中,以數值爲key,以索引爲值,就可以使用兩個疊加循環得解:
for index, value := range nums {
numsMap[value] = index
}
for value := range nums {
_, ok := numsMap[target - value]
if ok {
// find
}
}
考慮到數組切片中可能出現多個相同的數值,因此字典的value應該是一個集合。即集合保存了數組中某個值的所有索引。
代碼
package main
import "fmt"
type Set struct {
Base map[int]string
}
func CreateSet() *Set {
set := new(Set)
set.Base = make(map[int]string)
return set
}
func (s *Set) Add(data int) {
s.Base[data] = ""
return
}
func (s *Set) Adds(data... int) {
for _, d := range data {
s.Base[d] = ""
}
}
func (s *Set) Exist(data int) bool {
_, ok := s.Base[data]
return ok
}
func (s *Set) Pop() (int, error) {
if s.Length() <= 0 {
return 0, fmt.Errorf("can not pop element: set is empty")
}
for key := range s.Base {
value := key
delete(s.Base, value)
return value, nil
}
return 0, nil
}
func (s *Set) Length() int {
return len(s.Base)
}
func (s *Set) ToSlice() []int {
result := make([]int, 0)
for data := range s.Base {
result = append(result, data)
}
return result
}
func twoSum(nums []int, target int) []int {
numsMap := make(map[int]*Set)
for index, num := range nums {
_, ok := numsMap[num]
if !ok {
numsMap[num] = CreateSet()
}
numsMap[num].Add(index)
}
result := CreateSet()
for value := range numsMap {
_, ok := numsMap[target - value]
if !ok {
continue
}
index1, err := numsMap[value].Pop()
if err != nil {
continue
}
if numsMap[value].Length() <= 0 {
delete(numsMap, value)
}
_, ok = numsMap[target - value]
if !ok {
continue
}
index2, err := numsMap[target - value].Pop()
if err != nil {
continue
}
if numsMap[target - value].Length() <= 0 {
delete(numsMap, target-value)
}
result.Adds(index1, index2)
}
return result.ToSlice()
}
代碼走讀
package main
import "fmt"
// ---------- 定義集合結構 ----------
// 本質上集合使用map來存儲數據
type Set struct {
Base map[int]string
}
// 創建一個集合
func CreateSet() *Set {
set := new(Set)
set.Base = make(map[int]string)
return set
}
// 向集合中添加一個元素
func (s *Set) Add(data int) {
s.Base[data] = ""
return
}
// 向集合中添加多個元素
func (s *Set) Adds(data... int) {
for _, d := range data {
s.Base[d] = ""
}
}
// 判斷元素是否在集合中存在
func (s *Set) Exist(data int) bool {
_, ok := s.Base[data]
return ok
}
// 將集合中的一個隨機元素彈出,並返回該元素
func (s *Set) Pop() (int, error) {
if s.Length() <= 0 {
return 0, fmt.Errorf("can not pop element: set is empty")
}
for key := range s.Base {
value := key
delete(s.Base, value)
return value, nil
}
return 0, nil
}
// 求集合長度
func (s *Set) Length() int {
return len(s.Base)
}
// 將集合轉換爲切片
func (s *Set) ToSlice() []int {
result := make([]int, 0)
for data := range s.Base {
result = append(result, data)
}
return result
}
// LeetCode定義解決函數
func twoSum(nums []int, target int) []int {
// 將數組切片轉換爲Map,Map的key保存數組切片的值,value是一個Set, 保存了值對應在切片中的所有
// 索引下標
numsMap := make(map[int]*Set)
for index, num := range nums {
_, ok := numsMap[num]
if !ok {
numsMap[num] = CreateSet()
}
numsMap[num].Add(index)
}
// 將所有找到的結果放入result中
result := CreateSet()
for value := range numsMap {
_, ok := numsMap[target - value]
if !ok {
// 如果沒有找到,則進行下次遍歷
continue
}
// 找出第一個元素的下標,如果集合爲空,則將該值從Map中刪除
index1, err := numsMap[value].Pop()
if err != nil {
continue
}
if numsMap[value].Length() <= 0 {
delete(numsMap, value)
}
// 需要再次判斷的原因是數組切片中存在某個數恰巧是target值的一半,且該數值只存在一個,沒有
// 配對的另一個值。因此index1會獲取到下標,但是會將該value的鍵值對刪除
_, ok = numsMap[target - value]
if !ok {
continue
}
index2, err := numsMap[target - value].Pop()
if err != nil {
continue
}
if numsMap[target - value].Length() <= 0 {
delete(numsMap, target-value)
}
// 將結果放入result中
result.Adds(index1, index2)
}
// 轉換成數組切片輸出
return result.ToSlice()
}
// 自測用例
func main() {
r := twoSum([]int{2, 7, 11, 15}, 9)
fmt.Println(r)
}