LeetCode `Top Interview Questions` `Hard` Part I

524 阅读20分钟

4. Median of Two Sorted Arrays

There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0

Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

Solution

func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
	totalLength := len(nums1) + len(nums2)
	if totalLength == 0 {
		return 0.0
	}

	if totalLength%2 == 1 {
		return findKthElement(nums1, nums2, totalLength/2)
	} else {
		return (findKthElement(nums1, nums2, totalLength/2-1) + findKthElement(nums1, nums2, totalLength/2)) / 2.0
	}
}

func findKthElement(nums1 []int, nums2 []int, k int) float64 {
	if len(nums1) > len(nums2) {
		return findKthElement(nums2, nums1, k)
	}

	if len(nums1) == 0 {
		return float64(nums2[k])
	}
    
    if k == 0 {
        return float64(min(nums1[0], nums2[0]))
    }

	m := min(k/2, len(nums1)-1)
	n := k - 1 - m
	if nums1[m] == nums2[n] {
		return float64(nums1[m])
	} else if nums1[m] < nums2[n] {
		return findKthElement(nums1[m + 1:], nums2, k-m-1)
	} else {
		return findKthElement(nums1, nums2[n + 1:], k-n-1)
	}
}

func min(x, y int) int {
	if x < y {
		return x
	}

	return y
}

212. Word Search II

Given a 2D board and a list of words from the dictionary, find all words in the board.
Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

Example:

Input: 
board = [
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
words = ["oath","pea","eat","rain"]

Output: ["eat","oath"]

Note:

  1. All inputs are consist of lowercase letters a-z.
  2. The values of words are distinct.

Solution

type trieNode struct {
	isWord   bool
	children [26]*trieNode
	word     string
}

func findWords(board [][]byte, words []string) []string {
	if len(words) == 0 || len(words[0]) == 0 {
		return []string{}
	}
	root := &(trieNode{
		children: [26]*trieNode{},
		word:     "",
	})
	for _, word := range words {
		cur := root
		for i := 0; i < len(word); i++ {
			if cur.children[int(word[i]-'a')] == nil {
				cur.children[int(word[i]-'a')] = &(trieNode{
					children: [26]*trieNode{},
					word:     cur.word + word[i:i+1],
				})
			}
			cur = cur.children[int(word[i]-'a')]
		}

		cur.isWord = true
	}

	m := make(map[string]bool)
	visited := make([][]bool, len(board))
	for i := 0; i < len(board); i++ {
		visited[i] = make([]bool, len(board[0]))
	}
	for i := 0; i < len(board); i++ {
		for j := 0; j < len(board[0]); j++ {
			helper(board, root, visited, i, j, m)
		}
	}

	res := []string{}
	for k := range m {
		res = append(res, k)
	}

	return res
}

var directions = [][]int{
	[]int{1, 0},
	[]int{-1, 0},
	[]int{0, 1},
	[]int{0, -1},
}

func helper(board [][]byte, node *trieNode, visited [][]bool, i, j int, m map[string]bool) {
	if node.isWord {
		m[node.word] = true
	}

	if i < 0 || j < 0 || i >= len(board) || j >= len(board[0]) || visited[i][j] || node.children[int(board[i][j]-'a')] == nil {
		return
	}

	visited[i][j] = true
	for _, d := range directions {
		ii, jj := i+d[0], j+d[1]
		helper(board, node.children[int(board[i][j]-'a')], visited, ii, jj, m)
	}
	visited[i][j] = false
}

140. Word Break II

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.

Example 1:

Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
  "cats and dog",
  "cat sand dog"
]

Example 2:

Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
  "pine apple pen apple",
  "pineapple pen apple",
  "pine applepen apple"
]
Explanation: Note that you are allowed to reuse a dictionary word.

Example 3:

Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]

Solution

package main

import "fmt"

func wordBreak(s string, wordDict []string) []string {
	words := make(map[string]bool)
	for _, word := range wordDict {
		words[word] = true
	}

	raw := make(map[int][]string)
	res := dfs(s, words, raw, 0)
	fmt.Printf("%#v \n", raw)
	/*
		map[int][]string{
			0:[]string{"cat sand dog", "cats and dog"}, // 从0开始分割的结果
			3:[]string{"sand dog"}, //
			4:[]string{"and dog"},
			7:[]string{"dog"}
		}
	*/
	return res
}

func dfs(s string, words map[string]bool, record map[int][]string, pos int) []string {
	// 如果当前子数组有解,则返回
	if result, ok := record[pos]; ok {
		return result
	}
	var result []string

	for i := pos+1; i <= len(s); i++ {
		// 从pos:i这段单子是否在结果中,如果不是则继续排查
		if words[s[pos:i]] {
			if i != len(s) {
				rest := dfs(s, words, record, i)
				for _, r := range rest {
					// 所有的可能性组合
					result = append(result, s[pos:i] + " " + r)
				}
			} else {
				// 指针已经排查到底,排查结束
				result = append(result, s[pos:i])
				break
			}
		}
	}

	record[pos]  = result
	return result
}

42. Trapping Rain Water

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!


The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

Example:

Input: [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6

Solution

import "fmt"

func trap(height []int) int {
    if len(height) == 0 {
        return 0
    }
    rightMax := make([]int, len(height))
	rightMax[0] = height[0]

	leftMax := make([]int, len(height))
	leftMax[len(height)-1] = height[len(height)-1]

	for i := 1; i < len(height); i++ {
		rightMax[i] = max(height[i], rightMax[i-1])
	}
	for i := len(height) - 2; i >= 0; i-- {
		leftMax[i] = max(height[i], leftMax[i+1])
	}
	result := 0
	for i := 0; i < len(height); i++ {
		if height[i] > min(leftMax[i], rightMax[i]) {
			continue
		} else {
			result = result + min(leftMax[i], rightMax[i]) - height[i]
		}
	}
	return result
}

// n * n
func trap2(height []int) int {
	if len(height) == 0 {
		return 0
	}

	result := 0
	for i := 0; i < len(height); i++ {
		maxLeft := 0
		maxRight := 0
		for j := 0; j < i; j ++ {
			maxLeft = max(maxLeft, height[j])
		}
		for j := i; j < len(height); j++ {
			maxRight = max(maxRight, height[j])
		}
		if  min(maxLeft, maxRight) < height[i] {
			continue
		} else {
			result = result + min(maxLeft, maxRight) - height[i]
		}
	}
	return result
}

func min(x int, y int) int {
	if x < y {
		return x
	}
	return y
}

func max(x int, y int) int  {
	if x > y {
		return x
	}
	return y
}

124. Binary Tree Maximum Path Sum

Given a non-empty binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.

Example 1:

Input: [1,2,3]

       1
      / \
     2   3

Output: 6

Example 2:

Input: [-10,9,20,null,null,15,7]

   -10
   / \
  9  20
    /  \
   15   7

Output: 42

Solution

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

func maxPathSum(root *TreeNode) int {
    if root == nil {
        return 0
    }
    answer := root.Val
    // helper返回的是经过root节点的最大值,但是最大值不一定经过root节点
    _ = helper(root, &answer)
    return answer
}

/*
   1. 当前的路径计算必须经过当前的节点
   2. 向parent返回只能取两条边中的一条
   3. answer 是存储的全局的最优解
*/
func helper(node *TreeNode,  answer *int) int {
    if node == nil {
        return 0
    }
    left := max(0, helper(node.Left, answer))
    right := max(0, helper(node.Right, answer))
    // 计算以当前值为root的子数的路径的Sum
    curSum := left + right + node.Val
    // 如果当前局部有比全局最优解更好的解,那么更新全局最优解
    *answer = max(*answer, curSum)
    // 向父节点返回两条子树中Sum较大的那一条
    return max(left, right) + node.Val
}

func max(x int, y int) int {
    if x > y {
        return x
    }
    return y
}

315. Count of Smaller Numbers After Self

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Input: [5,2,6,1]
Output: [2,1,1,0] 
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.

Solution

// 暴力搜索,n*n 时间复杂度
func countSmaller1(nums []int) []int {
	if len(nums) == 0 {
		return []int{}
	}
	rear := len(nums)
	answer := make([]int, rear)
	answer[rear-1] = 0
	for i := rear-2; i >=0; i-- {
		if nums[i] > nums[i+1] {
			answer[i] = answer[i+1] + 1
			continue
		}
		answer[i] = answer[i+1]
	}
	return answer
}

type Node struct {
	num int // 当前节点的大小
	left int // 数组元素向左比当前数字小的总数
	index int // 数组元素下表
}

// 使用归并排序完成
func countSmaller(nums []int) []int {
	if len(nums) == 0 {
		return []int{}
	}
	rear := len(nums)
    // 初始化
	nodes := make([]Node, len(nums))
	for i := 0; i < rear; i++ {
		nodes[i] = Node{
			num:   nums[i],
			left:  0,
			index: i,
		}
	}
	countSmallerHelper(nodes, 0, len(nums))
	answer := make([]int, len(nodes))
	for i := 0; i < len(nodes); i++ {
		answer[nodes[i].index] = nodes[i].left
	}
	return answer
}

// 归并排序思路完成
func countSmallerHelper(numNodes []Node, start int, end int) {
    // 递归返回思路
	if start >= end -1 {
		return
	}
    
    // 分割位置
	mid := start + (end-start)/2
	// 递归partition
	countSmallerHelper(numNodes, start, mid)
	countSmallerHelper(numNodes, mid, end)
    
    // 计算当前元素在原数组中向右比他小的元素的和
	j := end -1
	for i := mid - 1; i >= start; i-- {
		for j >= mid && numNodes[j].num >= numNodes[i].num {
			j--
		}
		numNodes[i].left += j - mid + 1
	}

    // 归并排序,当前归并的长度为end-start
	temp := make([]Node, end - start)
	t := 0
	x, y := start, mid
	for x < mid && y < end {
		if numNodes[x].num >= numNodes[y].num {
			temp[t] = numNodes[y]
			t++
			y++
		} else {
			temp[t] = numNodes[x]
			t++
			x++
		}
	}

	for x < mid {
		temp[t] = numNodes[x]
		t++
		x++
	}

	for y < end {
		temp[t] = numNodes[y]
		t++
		y++
	}

    // 将该段的内容替换到原来的元素串中
	for i := 0; i < end-start; i++ {
		numNodes[i+start] = temp[i]
	}
}

41. First Missing Positive

Given an unsorted integer array, find the smallest missing positive integer.

Example 1:

Input: [1,2,0]
Output: 3

Example 2:

Input: [3,4,-1,1]
Output: 2

Example 3:

Input: [7,8,9,11,12]
Output: 1

Note:

Your algorithm should run in O(n) time and uses constant extra space.

Solution

func firstMissingPositive(nums []int) int {
    // 核心思想: 我们只关心[1..n]中的1...n数是否能与其下标对应上
    for i := 0; i < len(nums); i++ {
        temp := nums[i]
        // 将范围为[1..n]以内的数字都移动到与下标一一对应的地方
        // 之后第一个不相等的情况就是当前的情况
        for (temp > 0 && temp < len(nums) && nums[temp - 1] != temp) {
			cur := nums[temp-1]
			nums[temp-1] = temp
			temp = cur
			nums[i] = cur            
        }
        
    }
    
    for i := 0; i < len(nums); i++ {
        if nums[i] != i+1 {
            return i + 1
        }
    }
    
    return len(nums) + 1
}

10. Regular Expression Matching

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.

  • '.' Matches any single character.
  • '*' Matches zero or more of the preceding element.
    The matching should cover the entire input string (not partial).

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like . or *.

Example 1:

Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

Example 2:

Input:
s = "aa"
p = "a*"
Output: true
Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".

Example 4:

Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".

Example 5:

Input:
s = "mississippi"
p = "mis*is*p*."
Output: false

Solution

func isMatch(s string, p string) bool {

	dp := make([][]bool, len(s)+1)
	for i := 0; i <= len(s); i++ {
		inner := make([]bool, len(p)+1)
		dp[i] = inner
	}

	slen := len(s)
	plen := len(p)
	/*
	   dp[i][j]: s中第i个元素和p中第j个元素之间的匹配关系;
	   三种情况:
	   第一种, s[i] == p[j] 且 s[j] 为正常字符串;
	       此时dp[i][j] = true && dp[i-1][j-1]
	   第二种, s[i] == p[j] 且 p[j] == '.'
	       此时dp[i][j] = true && dp[i-1][j-1]
	   第三种, s[i] == p[j] 且 p[j] == '*'
	       此时dp[i][j] =  dp[i][j-2]||            // 回溯dp中p前两位情况 (c**)
	                      dp[i][j-1]||            // 回溯dp中p前一位情况 (c*)
	                      ( (  dp[i-1][j-1] ||    
	                           dp[i-1][j])  &&    
	                         ( p[j-1-1]=='.'||   
	                           p[j-1-1]==s[i-1])
	                       )
	   dp矩阵边界情形:
	       case1:
	           s="" p="" -> dp[0][0] = true
	       case2:
	           s="" p="." -> dp[0][0] = true, dp[0][1] = true
	       case3:
	           s="" p=".*" -> dp[0][0] = true, dp[0][1] = true dp[0][2] = true

	       case4:
	           s="" p=".*a" -> dp[0][0] = true,
	                           dp[0][1] = true,
	                           dp[0][2] = true,
	                           dp[0][3] = false.
	*/
	dp[0][0] = true
	for j := 1; j <= plen; j++ {
		// 正则中*不能在开头出现
		if p[j-1] == '*' {
			// *位是否能匹配成功,依赖的前面两列的结果
			dp[0][j] = dp[0][j-1] || dp[0][j-2]
		} else {
			dp[0][j] = false
		}
	}

	for i := 1;i <=len(s);i++{
		dp[i][0] = false
	}

	for i := 1; i <= slen; i++ {
		for j := 1; j <=plen; j++ {
			if p[j-1] == '*' {
				// 要向前回溯两位,原因: 查看需要重复的字符串,查看是否是通配符'.*'
				dp[i][j] = dp[i][j-2] || dp[i][j-1] || ((dp[i-1][j-1]||dp[i-1][j]) && (p[j-1-1]=='.'||p[j-2]==s[i-1]))

			} else if p[j-1] == '.' {
				dp[i][j] = dp[i-1][j-1]

			} else if p[j-1] == s[i-1] {
				dp[i][j] = dp[i-1][j-1]

			} else if p[j-1] != s[i-1] {
				dp[i][j] = false
			}
		}
	}
	//fmt.Printf("%v \n", dp)

	return dp[slen][plen]
}

218. The Skyline Problem

leetcode.com/problems/th…

Solution

func getSkyline(buildings [][]int) [][]int {
	points := make([]*RoofPoint, len(buildings)*2)
    // 右侧堆节点标记
	rightToLeftMap := make(map[*RoofPoint]*RoofPoint)

    // 将整体的点都录入到points中,区分左入点和右出点,录入完成后构成一个初始化堆
	for i := 0; i < len(buildings); i++ {
        // 左侧
		points[i*2] = &RoofPoint{IsLeft: true, X: buildings[i][0], Height: buildings[i][2]}
        // 右侧
		points[1+i*2] = &RoofPoint{IsLeft: false, X: buildings[i][1], Height: buildings[i][2]}
        // 原始左右点对应的map
		rightToLeftMap[points[1+i*2]] = points[i*2]
	}
    // 将该堆按照索引大小从左到右进行排序
	sort.Sort(ByXAsc(points))

    // 高度堆
	pq := make(PQ, 0)
	heap.Init(&pq)

	var result [][]int
    
    // 基于x轴下标遍历points
	for i := 0; i < len(points); i++ {
		point := points[i]
        // 左测点入库
		if point.IsLeft {
			heap.Push(&pq, point)
			if len(result) == 0 {
                // 第一个到达点左侧放入结果中
				result = append(result, []int{point.X, point.Height})
				continue
			}

            // 堆排序(大根堆)
			highest := pq[0]
            // 当前的左侧节点被低于最近的转折点,相当于被覆盖,忽略
			if result[len(result)-1][1] >= highest.Height {
				continue
			}

            // 与最近点转折点在横坐标相同
			if result[len(result)-1][0] == point.X {
                // 最近压入点转折点高度更新为当前所有节点中最高点
				result[len(result)-1][1] = highest.Height
                // 如果次一级点转折点的内容等于最近的转折点,那么直接抛弃最近的录入的转折点
				if len(result) > 1 && result[len(result)-2][1] == highest.Height {
					result = result[:len(result)-1]
				}
			} else {
                // 如果坐标不同,那么将左边节点压入结果中,并且高度为当前最高的高度
				result = append(result, []int{point.X, highest.Height})
			}
			continue
		}

        
        // 如果是右侧出边界,则移除对应的左侧入边界
		heap.Remove(&pq, rightToLeftMap[point].HeapIdx)
        // 如果堆为空哦
		if len(pq) == 0 {
            // 坐标在最近转折点之后,即向下折,那么是新的转折点
			if result[len(result)-1][0] < point.X {
				result = append(result, []int{point.X, 0})
            // 坐标在最近的转折之前,那么最近转折点已经到最底下了,置位为0
			} else {
				result[len(result)-1][1] = 0
			}
			continue
		}

        // 如果堆不为空,
		highest := pq[0]
		if result[len(result)-1][1] > highest.Height {
			if result[len(result)-1][0] < point.X {
				result = append(result, []int{point.X, highest.Height})
			} else {
				result[len(result)-1][1] = highest.Height
			}
		}
	}
	return result
}

// 节点基本信息
type RoofPoint struct {
	HeapIdx int
	IsLeft  bool
	X       int
	Height  int
}

// 用来对节点进行排序
type ByXAsc []*RoofPoint

// 重写 Len Less Swap 函数实现Sort排序
func (points ByXAsc) Len() int {
	return len(points)
}

func (points ByXAsc) Swap(i, j int) {
	points[i], points[j] = points[j], points[i]
}

func (points ByXAsc) Less(i, j int) bool {
	return points[i].X < points[j].X
}

type PQ []*RoofPoint

func (points PQ) Len() int {
	return len(points)
}

func (points PQ) Swap(i, j int) {
	points[i], points[j] = points[j], points[i]
	points[i].HeapIdx = i
	points[j].HeapIdx = j
}

func (points PQ) Less(i, j int) bool {
	if points[i].Height == points[j].Height {
		return !points[i].IsLeft
	}
	return points[i].Height > points[j].Height
}

// 重写Push & Pop 实现堆排序
func (points *PQ) Push(item interface{}) {
	newP := item.(*RoofPoint)
	newP.HeapIdx = len(*points)
	*points = append(*points, newP)
}

func (points *PQ) Pop() interface{} {
	tmp := (*points)[len(*points)-1]
	tmp.HeapIdx = -1
	*points = (*points)[:len(*points)-1]
	return tmp
}

295. Find Median from Data Stream

Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
For example,

  • [2,3,4], the median is 3
  • [2,3], the median is (2 + 3) / 2 = 2.5 Design a data structure that supports the following two operations:
    void addNum(int num) - Add a integer number from the data stream to the data structure.
    double findMedian() - Return the median of all elements so far.

Example:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2

Follow up:

  • If all integer numbers from the stream are between 0 and 100, how would you optimize it?
  • If 99% of all integer numbers from the stream are between 0 and 100, how would you optimize it?

Solution

import "container/heap"

type MedianFinder struct {
	size    int
	minHeap MinHeap
	maxHeap MaxHeap
}

/** initialize your data structure here. */
func Constructor() MedianFinder {
	return MedianFinder{
		size:    0,
		minHeap: MinHeap{},
		maxHeap: MaxHeap{},
	}
}

func (this *MedianFinder) AddNum(num int) {
	if this.size == 0 {
		heap.Push(&this.maxHeap, num)
		heap.Push(&this.minHeap, num)
	} else if this.size%2 == 0 {
		if this.minHeap[0] <= num {
			heap.Push(&this.maxHeap, this.minHeap[0])
			heap.Push(&this.minHeap, num)
		} else if this.maxHeap[0] >= num {
			heap.Push(&this.minHeap, this.maxHeap[0])
			heap.Push(&this.maxHeap, num)
		} else {
			heap.Push(&this.maxHeap, num)
			heap.Push(&this.minHeap, num)
		}
	} else {
		if this.minHeap[0] <= num {
			heap.Pop(&this.minHeap)
			heap.Push(&this.minHeap, num)
		} else {
			heap.Pop(&this.maxHeap)
			heap.Push(&this.maxHeap, num)
		}
	}

	this.size++
}

func (this *MedianFinder) FindMedian() float64 {
	return float64(this.minHeap[0]+this.maxHeap[0]) / 2.0
}

type MinHeap []int

func (h MinHeap) Len() int                { return len(h) }
func (h MinHeap) Less(i, j int) bool      { return h[i] < h[j] }
func (h MinHeap) Swap(i, j int)           { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(value interface{}) { *h = append(*h, value.(int)) }
func (h *MinHeap) Pop() interface{} {
	min := (*h)[len(*h)-1]
	*h = (*h)[:len(*h)-1]
	return min
}

type MaxHeap []int

func (h MaxHeap) Len() int                { return len(h) }
func (h MaxHeap) Less(i, j int) bool      { return h[i] > h[j] }
func (h MaxHeap) Swap(i, j int)           { h[i], h[j] = h[j], h[i] }
func (h *MaxHeap) Push(value interface{}) { *h = append(*h, value.(int)) }
func (h *MaxHeap) Pop() interface{} {
	min := (*h)[len(*h)-1]
	*h = (*h)[:len(*h)-1]
	return min
}

84. Largest Rectangle in Histogram

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.

Example:

Input: [2,1,5,6,2,3]
Output: 10

Solution

func largestRectangleArea(heights []int) int {
	if len(heights) == 0 {
		return 0
	}

	if len(heights) == 1 {
		return heights[0]
	}

	// 存入的是对应的下标
	stack := []int{}

	area := 0
	for i := 0; i < len(heights); i++ {
		for len(stack) > 0 && heights[stack[len(stack)-1]] > heights[i] {
			height := heights[stack[len(stack)-1]]

			stack = stack[:len(stack)-1]
			// pop 出相同高度的栈元素
			for len(stack) > 0 && heights[stack[len(stack)-1]] == height {
				stack = stack[:len(stack)-1]
			}

			var width int
			if len(stack) > 0 {
				width = i - stack[len(stack)-1] - 1
			} else {
				width = i
			}
			area  = max(area, width * height)
		}
		stack = append(stack, i)
	}

	// 清空栈,计算基于最后一个高度的面积
	for len(stack) > 0 {
		height := heights[stack[len(stack)-1]]

		stack = stack[:len(stack)-1]
		// pop 出相同高度的栈元素
		for len(stack) > 0 && heights[stack[len(stack)-1]] == height {
			stack = stack[:len(stack)-1]
		}

		var width int
		if len(stack) > 0 {
			width = len(heights) - stack[len(stack)-1] - 1
		} else {
			width = len(heights)
		}
		area  = max(area, width * height)
	}
	return area
}
    
func max(x, y int) int {
	if x < y {
		return y
	}
	return x
}

128. Longest Consecutive Sequence

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
Your algorithm should run in O(n) complexity.

Example:

Input: [100, 4, 200, 1, 3, 2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

Solution

func longestConsecutive(nums []int) int {
	numsMap := make(map[int]bool)
	for _, num := range nums {
		numsMap[num] = true
	}
	ans := 0
	for show := range numsMap {
		if !numsMap[show-1] {
			currentNum := show
			currentL := 1
			for numsMap[currentNum+1] {
				currentNum++
				currentL++
			}
			ans = max(ans, currentL)
		}
	}
	return ans
}

func max(i int, j int) int {
    if i > j { return i }
    return j
}

329. Longest Increasing Path in a Matrix

Given an integer matrix, find the length of the longest increasing path.
From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed).

Example 1:

Input: nums = 
[
  [9,9,4],
  [6,6,8],
  [2,1,1]
] 
Output: 4 
Explanation: The longest increasing path is [1, 2, 6, 9].

Example 2:

Input: nums = 
[
  [3,4,5],
  [3,2,6],
  [2,2,1]
] 
Output: 4 
Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.

Solution

Method 1

func longestIncreasingPath(matrix [][]int) int {
	if len(matrix) == 0 {
		return 0
	}
	m := len(matrix)
	n := len(matrix[0])
	m1 := m + 2
	n1 := n + 2

	newMatrix := make([][]int, m1)
	newMatrix[0] = make([]int, n1)
	newMatrix[m1-1] = make([]int, n1)
	for i := range matrix {
		dest := make([]int, n1)
		arrayCopy(&matrix[i], 0,  &dest, 1, n)
		newMatrix[i+1] = dest
	}

	// cal outdegree
	outdegree := make([][]int, m1)
	outdegree[0] = make([]int, n1)
	outdegree[m1-1] = make([]int, n1)
	direction := [][]int{{1, 0}, {0, 1}, {-1, 0}, {0, -1}}
	for i := 1; i <= m; i++ {
		item := make([]int, n1)
		for j := 1; j <= n; j++ {
			for _, dir := range direction{
				if newMatrix[i][j] < newMatrix[i + dir[0]][j + dir[1]] {
					item[j]++
				}
			}
		}
		outdegree[i] = item
	}

	var leaves [][]int
	for i := 1; i < m1 - 1; i++ {
		for j := 1; j < n1 - 1; j++ {
			if outdegree[i][j] == 0 {
				leaves = append(leaves, []int{i, j})
			}
		}
	}

	height := 0
	// remove leaves level by level in topological order
	for len(leaves) > 0 {
		height++
		var newLeaves [][]int
		for _, node := range leaves {
			for _, dir := range direction {
				x := node[0] + dir[0]
				y := node[1] + dir[1]
				if newMatrix[node[0]][node[1]] > newMatrix[x][y] {
					outdegree[x][y]--
					if outdegree[x][y] == 0 {
						newLeaves = append(newLeaves, []int{x, y})
					}
				}
			}
		}
		leaves = newLeaves
	}
	return height
}

func arrayCopy(src *[]int, srcPos int, dest *[]int, destPos int, nums int) {
	j := destPos
	i := srcPos
	for c := 0; i < nums; c++ {
		(*dest)[j] = (*src)[i]
		i++
		j++
	}
}

Method 2

func longestIncreasingPath(matrix [][]int) int {
	if len(matrix) == 0 {
		return 0
	}

	direction := [][]int{{1, 0}, {0, 1}, {-1, 0}, {0, -1}}
	m := len(matrix)
	n := len(matrix[0])
	dp := make([][]int, m)
	for i := 0; i < len(dp); i++ {
		dp[i] = make([]int, n)
	}

	ans := 0
	for i := 0; i < len(matrix); i++ {
		for j := 0; j < len(matrix[0]); j++ {
			ans = max(ans, dfs(i, j, m, n, &direction, matrix, &dp))
		}
	}
	return ans
}

func dfs(i int, j int, m int, n int, direction *[][]int, matrix [][]int, M *[][]int) int {
	if (*M)[i][j] != 0 { return (*M)[i][j] }

	for k := 0; k < 4; k++ {
		x := (*direction)[k][0] + i
		y := (*direction)[k][1] + j
		if 0 <= x && x < m && 0 <= y && y < n && matrix[x][y] > matrix[i][j] {
			(*M)[i][j] = max((*M)[i][j], dfs(x, y, m, n, direction, matrix, M))
		}

	}
	(*M)[i][j] = (*M)[i][j] + 1
	return (*M)[i][j]

}

func max(x int, y int) int {
	if x > y {
		return x
	}
	return y
}

149. Max Points on a Line

Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.

Example 1:

Input: [[1,1],[2,2],[3,3]]
Output: 3
Explanation:
^
|
|        o
|     o
|  o  
+------------->
0  1  2  3  4

Example 2:

Input: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
Output: 4
Explanation:
^
|
|  o
|     o        o
|        o
|  o        o
+------------------->
0  1  2  3  4  5  6
NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature.

Solution

type Vector struct {
	x, y int
}

type Point struct {
	x, y int
}

func maxPoints(points [][]int) int {
	if len(points) == 0 {
		return 0
	}
	result := 1

	for _, pointSlice := range points {
		point := Point{pointSlice[0], pointSlice[1]}

		vectors := make(map[Vector]int)
		id := 0

		for _, otherSlice := range points {
			other := Point{otherSlice[0], otherSlice[1]}

			// 一穿N
			if point == other {
				id += 1
				continue
			}

			dX := other.x - point.x
			dY := other.y - point.y

			if dX < 0 {
				dX *= -1
				dY *= -1
			}

			unifier := gcd(abs(dX), abs(dY))
			// 在一条直线上的条件 (y3-y1)*(x2-x1) == (y2-y1)*(x3-x1)
			vector := Vector{dX / unifier, dY / unifier}
			vectors[vector] += 1
		}

		result = max(result, id)
		for _, v := range vectors {
			result = max(result, v + id)
		}
	}

	return result
}

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

func gcd(x, y int) int {
	if x == 0 {
		return y
	}

	if y == 0 {
		return x
	}

	for y > 0 {
		x, y = y, x % y
	}

	return x
}

func max(a, b int) int {
	if a < b {
		return b
	}

	return a
}

76. Minimum Window Substring

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

Example:

Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"

Note:

  • If there is no such window in S that covers all characters in T, return the empty string "".
  • If there is such window, you are guaranteed that there will always be only one unique minimum window in S.

Solution

Easy To Understand

func minWindow(s string, t string) string {
	if len(s) == 0 || len(t) == 0 || len(s) < len(t) {
		return ""
	}
	tMap := make(map[byte]int)
	for i := 0; i < len(t); i++ {
		_, ok := tMap[t[i]]; if ok {
			tMap[t[i]]++
		} else {
			tMap[t[i]] = 1
		}
	}
	fmt.Printf("tMap: %v \n", tMap)
	fmt.Printf("tMap length: %d \n", len(tMap)) // tMap keyword

	subMap := make(map[byte]int)
	start := 0
	end := 0
	//flag := false
	length := len(s)
	minS := 0
	minE := len(s) - 1
	isFound := false
	for ;end < len(s); end++ {
		// 当前字符在子字符串中,则添加进入map
		_, ok := tMap[s[end]]; if ok {
			_, ok := subMap[s[end]]; if ok {
				subMap[s[end]]++
			} else {
				subMap[s[end]] = 1
			}
		}
		// 如果当前的切片包含了匹配串, 那么start开始向右移动
		for contains(subMap, tMap) {
			isFound = true
			// 首先更新最小长度
			newLength := end - start + 1
			if length > newLength {
				length = newLength
				minS = start
				minE = end
			}

			fmt.Printf("current start: %d, current end: %d minLength: %d \n", start, end, length)

			// 向右移动start指针
			// 如果subMap在Map中,那么出现频率-1
			_, ok := subMap[s[start]]; if ok {
				fmt.Printf("Before: subMap:%v tMap: %v \n", subMap, tMap)
				subMap[s[start]]--
				fmt.Printf("After: %v tMap: %v \n", subMap, tMap)
			}

			if start < end && start < len(s) {
				start += 1
			} else {
				break
			}
		}

	}

	if !isFound {
		return ""
	}

	fmt.Printf("final start: %d, final end: %d \n", minS, minE)
	return s[minS:minE+1]
}

func contains(sMap map[byte]int, tMap map[byte]int) bool {
	for k, v := range tMap {
		// 不存在
		_, ok := sMap[k]; if !ok {
			return false
		}
		// 非包含关系
		if sMap[k] < v {
			return false
		}
	}
	return true
}

Advanced Condition & Data Structure

func minWindow(s string, t string) string {
	switch {
	case len(s) == 0 || len(t) == 0 || len(s) < len(t):
		return ""
	case len(s) == len(t) && s == t:
		return s
	}

	var need [128]int // Its enough for ASCII, for UTF-8 we have to use make(map[rune]int).
	for _, ch := range t {
		need[ch]++
	}

	formed := 0 // 包含子字符串中字符的总数
	minWindow := ""
	for l, r := 0, 0; r < len(s); r++ {
		ch := s[r]
		need[ch]--

		if need[ch] >= 0 {
			formed++
		}

		// 当前切片包含匹配字符串
		for l <= r && formed == len(t) {
			currWindow := s[l:r+1]
			if minWindow == "" || len(currWindow) < len(minWindow) {
				minWindow = currWindow
			}

			// 记住左指针位置
			ch = s[l]
			// 复原map中昌吉ing
			need[ch]++        // Return back the previously removed char count.
			// 如果大于0说明在t中出现过
			if need[ch] > 0 { // > 0 means we need to char ch from s to form a valid window.
				// 有效字符串总数-1
				formed--
			}
			l++
		}
	}
	return minWindow
}

239. Sliding Window Maximum

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.

Follow up:

Could you solve it in linear time?

Example:

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7] 
Explanation: 

Window position                Max
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

Constraints:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

Solution

func maxSlidingWindow(nums []int, k int) []int {
    l := len(nums)
    result := make([]int, l-k+1)
    queue := [][]int{}
    for i := 0; i < l; i++ {
        for len(queue) > 0 && queue[len(queue)-1][1] < nums[i] {
            queue = queue[:len(queue)-1]
        }
        queue = append(queue, []int{i, nums[i]})
        if i >= k - 1 {
            result[i-k+1] = queue[0][1]
            if queue[0][0] == i-k+1 {
                queue = queue[1:]
            }
        }
    }
    return result
}