兩數之和(Go,LeetCode)

目錄

題目描述

解決方案

方案一:二重循環檢索

方案二:使用Map和集合查找

代碼

代碼走讀

傳送門


 

題目描述

給定一個整數數組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!
        }

 

如果找到滿足條件的值,就講對應元素的下標收集起來。最終將所有滿足條件的下標所組成的列表返回。

但是這個方案有一個致命的缺點:時間複雜度爲O(n^{2}),也就是說大量的時間被浪費在重複的計算上,因此不是最優解。

 

方案二:使用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)
}

 

傳送門

LeetCode試題鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章