初練演算法,比較演算法之美

熱愛coding的稻草發表於2019-03-31

作為一名coder,演算法不僅要會懂會寫,在保證結果正確的同時,還要求效能足夠高,才稱得上優秀的演算法。

初練演算法,比較演算法之美

本文比較了本人用 golang 初練演算法的一些 demo,以期不斷進步,假以時日,寫出更好的演算法。

1. 求眾數(在陣列中出現次數大於 n/2 的元素)

a. 本人寫法:

func majorityElement1(nums []int) int {
  n := len(nums)

  for i := 0; i < n; i++ {
    equalNum := 1
    for j := 0; j < n; j++ {
      if nums[i] == nums[j] && i != j {
        equalNum++
      }

      if equalNum > n/2 {
        return nums[i]
      }
    }
  }

  return nums[0]
}
複製程式碼

b. 別人優秀寫法:

func majorityElement2(nums []int) int {
  middle := len(nums) / 2
  equalMap := make(map[int]int, middle)

  for _, v := range nums {
    equalMap[v] += 1

    if equalMap[v] > middle {
      return v
    }
  }

  return nums[0]
}
複製程式碼

2. 只出現一次的數字

a. 本人寫法:

func singleNumber1(nums []int) int {
  n := len(nums)
  for i := 0; i < n; i++ {
    for j := 0; j < n; j++ {
      if nums[i] == nums[j] && i != j {
        break
      }

      if j == (n - 1) {
        return nums[i]
      }
    }
  }

  return nums[n-1]
}
複製程式碼

b. 別人優秀寫法:

func singleNumber2(nums []int) int {
  // 零和任何數異或都等於任何數, 一個數異或兩次就等於0
  // 本題中除一個之外每個元素都出現兩次
  // 所以用迴圈異或所有數就等於 只出現一次的那個數
  ret := 0
  for _, v := range nums {
    ret ^= v
  }

  return ret
}
複製程式碼

3. 搜尋二位矩陣中的值

a. 本人寫法:

func searchMatrix1(matrix [][]int, target int) bool {
  ret := false
  for i, _ := range matrix {
    if ret {
      break
    }
    for _, inValue := range matrix[i] {
      if inValue == target {
        ret = true
        break
      }
    }
  }

  return ret
}
複製程式碼

b. 別人優秀寫法:

func searchMatrix2(matrix [][]int, target int) bool {
  if len(matrix) == 0 {
    return false
  }
  row, col := len(matrix), len(matrix[0])
  i, j := 0, col-1
  for i < row && j >= 0 {
    if matrix[i][j] == target {
      return true
    }

    if matrix[i][j] > target {
      j--
    } else if matrix[i][j] < target {
      i++
    }
  }

  return false
}
複製程式碼

4. 合併兩個有序陣列

a. 本人寫法:

func merge1(nums1 []int, m int, nums2 []int, n int) {
  nonZero := 0
  for i, _ := range nums1 {
    if nums1[i] != 0 {
      nonZero = i
      break
    }
  }

  length := len(nums1)
  for i := 0; i < length; i++ {
    if nums1[i] == 0 {
      if length == 1 {
        nums1 = []int{}
        break
      }
      if i > nonZero {
        nums1 = append(nums1[:i], nums1[i+1:]...)
        i--
      }
      length = len(nums1)
    }
  }

  m = len(nums1)

  if m <= 0 || len(nums1) == 0 {
    nums1 = append(nums1, nums2...)
  } else {
    for i := 0; i < m; i++ {
      if len(nums1) == m+n {
        break
      }
      for index := 0; index < n; index++ {
        // corner case
        if nums2[0] >= nums1[m-1] && i == 0 && index == 0 {
          nums1 = append(nums1, nums2...)
          break
        }
        if nums2[n-1] <= nums1[0] && i == 0 && index == 0 {
          nums1 = append(nums2, nums1...)
          break
        }

        if nums2[index] > nums1[i] {
          if i == len(nums1)-1 {
            nums1 = append(nums1, nums2[index:]...)
            break
          }

          i++
          if index != n-1 {
            index--
          } else {
            nums1 = append(nums1, nums2[n-1])
            break
          }
          continue
        }
        if nums2[index] == nums1[i] {
          rear := append([]int{}, nums1[i+1:]...)
          nums1 = append(nums1[0:i+1], nums2[index])
          nums1 = append(nums1, rear...)

          i++
          continue
        }
        if nums2[index] < nums1[i] {
          if index == 0 && i == 0 {
            nums1 = append([]int{nums2[index]}, nums1...)
          } else {
            rear := append([]int{}, nums1[i:]...)
            nums1 = append(nums1[0:i], nums2[index])
            nums1 = append(nums1, rear...)
          }

          i++
          continue
        }
      }
    }
  }

  fmt.Println(nums1)
}
複製程式碼

b. 別人優秀寫法:

func merge2(nums1 []int, m int, nums2 []int, n int) {
  for i := 0; i < n; i++ {
    nums1[m+i] = nums2[i]
  }

  sort.Sort(sort.IntSlice(nums1))

  fmt.Println(nums1)
}
複製程式碼

這個題目,自己花了很多時間才寫出來,看了別人的寫法後,反思自己怎麼沒有想到用 golang 自帶的排序函式 : (

所以做事情之前要先想到是否別人已經有造好的輪子(當然輪子要是已經被廣泛認可的)可用,站在巨人的肩膀上才能走的更遠。

小結:

從以上例子看出,在寫演算法,我目前的思路總是想著迴圈巢狀去實現,雖然能計算出正確的結果,但效能很低,所以需要不斷學習別人優秀的演算法思路,不斷動手練習,期望以後也可以隨手寫出優秀的演算法。

祝大家週末愉快~

初練演算法,比較演算法之美

相關文章