Array Question Summary

2016-02-22  本文已影响104人  abrocod

Python Array Trick

High level thoughts:


Array Syntax Shortcut

S.sort()
reversed(a)  # reverse a list
>>> reversed(a)
<listreverseiterator object at 0x1005dd9d0>

Array index computation

assume array index start from 0
let start = 0
end = len(array) - 1
middle = (start + end)/2

Nested Array operation

if we want to modify array in place, should use:

matrix[:] = zip(*matrix[::-1]) 

This code will rotate matrix clockwise by 90 degree
or counter-clockwise:

zip(*original)[::-1] 

Questions

228. Summary Ranges

Three versions of the same algorithm, all take O(n) time.
Solution 1
Just collect the ranges, then format and return them.

def summaryRanges(self, nums): 
    ranges = [] 
    for n in nums: 
        if not ranges or n > ranges[-1][-1] + 1: 
            # when ranges = [], 'if ranges' will fail
            ranges += [], #ranges.append([]), see below
        ranges[-1][1:] = n, #r[1:] = [n]
    return ['->'.join(map(str, r)) for r in ranges]

Solution 2
A variation of solution 1, holding the current range in an extra variable r
to make things easier. Note that r contains at most two elements, so the in-check takes constant time.

def summaryRanges(self, nums): 
    ranges, r = [], [] 
    for n in nums: 
        if n-1 not in r: 
            r = [] 
            ranges += r, 
        r[1:] = n, 
    return ['->'.join(map(str, r)) for r in ranges]

About the commas :-)
Three people asked about them in the comments, so I'll also explain it here as well. I have these two basic cases:
ranges += [], r[1:] = n,

Why the trailing commas? Because it turns the right hand side into a tuple and I get the same effects as these more common alternatives:
ranges += [[]] or
ranges.append([])
r[1:] = [n]

Without the comma, ...
ranges += []
wouldn't add []
itself but only its elements, i.e., nothing.
r[1:] = n
wouldn't work, because my n
is not an iterable.

Why do it this way instead of the more common alternatives I showed above? Because it's shorter and faster (according to tests I did a while back).

189. Rotate Array

For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7]
is rotated to [5,6,7,1,2,3,4]

Note:
Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
Hint:
Could you do it in-place with O(1) extra space?
Related problem: Reverse Words in a String II

def rotate(self, nums, k): 
    while k > 0: 
        nums.insert(0, nums.pop()) 
        k -= 1
class Solution: 
    # @param nums, a list of integer 
    # @param k, num of steps 
    # @return nothing, please modify the nums list in-place. 
    def rotate(self, nums, k): 
        n = len(nums) 
        k = k % n # why this step ??? 
        nums[:] = nums[n-k:] + nums[:n-k]

A little important thing to be cautious:
nums[:] = nums[n-k:] + nums[:n-k]

can't be written as:
nums = nums[n-k:] + nums[:n-k]

on the OJ.
The previous one can truly change the value of old nums, but the following one just changes its reference to a new nums not the value of old nums.

# a little simplification
def rotate(self, nums, k): 
    k = k % len(nums) 
    nums[:] = nums[-k:] + nums[:-k] 

Multiple approaches in C++

1. Two Sum

Consider different scenarios:

Ideal right solution with hash table:

# O(n) time
def twoSum(self, nums, target):  
    if len(nums) <= 1: 
        return False 
    buff_dict = {} 
'''
how to better understanding this short algorithm: 
- think this single for loop as to 'process' each number within nums:
  - if 
'''
    for i in range(len(nums)): 
        # if nums[i] is the solution we look for, then just return
        # this algorithm will only return the first matched tuple
        if nums[i] in buff_dict: 
            return [buff_dict[nums[i]], i] 
        else: 
            # if nums[i] is not solution, then process it for future element of nums
            # in this way, it avoid error in next solution, like error for [3,2,4]
            # it also handle duplication automatically 
            buff_dict[target - nums[i]] = i

Classical WRONG solution:

#  A WRONG solution: 
    def twoSum(self, nums, target):
        '''
        This solution is WRONG !!! 
        e.g. {2, 2, 11, 15}, target 4, Hashmap will be overriden for this input. How do you handle this?
        - problem 1: When adding a number to the hashmap should check if the key is already in . If true you have to check whether the double of the number is equal with the target.

        - problem 2: Also if you have (3,2,4) target 6. You will get 0,0 wich is not correct. (and this is even trickier to solve) 
        
        --- JUST don't use this approach !! 
        '''
        if len(nums) <= 1:
            return False
        map = {}
        for i in range(len(nums)):
            # better: for (i, num) in emuerate(nums):
            map[nums[i]] = i
        for i in range(len(nums)):
            if (target - nums[i]) in map.keys():
                print target - nums[i]
                print map[(target - nums[i])]
                return [ i, map[(target - nums[i])] ]

Use sort ! O(nlog(n)) time complexity
Sort method: sorting the input gives the array a good property to keep and good indication for moving the head and tail pointers. Here is the detailed steps.

88. Merge Sorted Array

Start from back of list

def merge(self, nums1, m, nums2, n): 
    while m > 0 and n > 0: 
        if nums1[m-1] >= nums2[n-1]: 
            nums1[m+n-1] = nums1[m-1] 
            m -= 1 
        else: 
            nums1[m+n-1] = nums2[n-1] 
            n -= 1 
    if n > 0: 
        nums1[:n] = nums2[:n]

78. Subsets

# DFS recursively 
def subsets1(self, nums): 
    res = [] 
    self.dfs(sorted(nums), 0, [], res) 
    return res

def dfs(self, nums, index, path, res): 
    res.append(path) 
    for i in xrange(index, len(nums)): 
        self.dfs(nums, i+1, path+[nums[i]], res)
# Bit Manipulation 
def subsets2(self, nums): 
    res = [] 
    nums.sort() 
    for i in xrange(1<<len(nums)): 
        tmp = [] 
        for j in xrange(len(nums)): 
            if i & 1 << j:  # if i >> j & 1: 
                tmp.append(nums[j]) 
        res.append(tmp) 
    return res
# Iteratively
def subsets(self, nums): 
    res = [[]] 
    for num in sorted(nums): 
        res += [item+[num] for item in res] #<--- Remember this way of using list comprehension!!!
    return res

90. Subsets II

???
Given a collection of integers that might contain duplicates, nums, return all possible subsets.

def subsetsWithDup(self, S): 
    res = [[]] 
    S.sort() 
    for i in range(len(S)): 
        if i == 0 or S[i] != S[i - 1]: 
            l = len(res) 
        for j in range(len(res) - l, len(res)): 
            res.append(res[j] + [S[i]]) 
    return res

54. Spiral Matrix

def spiralOrder(self, matrix): 
    ret = [] 
    while matrix: 
        ret += matrix.pop(0) 
        if matrix and matrix[0]: 
            for row in matrix: 
                ret.append(row.pop()) 
        if matrix: 
            ret += matrix.pop()[::-1] 
        if matrix and matrix[0]: 
            for row in matrix[::-1]: 
                ret.append(row.pop(0)) 
    return ret

59. Spiral Matrix II

Three python solutions

Solution 3: Walk the spiral - 52 ms, 9 lines (a very smart approach!!, can also be used to print spiral matrix problem)

def generateMatrix(self, n): 
    A = [[0] * n for _ in range(n)] 
    i, j, di, dj = 0, 0, 0, 1 
    for k in xrange(n*n): 
        A[i][j] = k + 1 
        if A[(i+di)%n][(j+dj)%n]: 
            di, dj = dj, -di 
        i += di 
        j += dj 
    return A

Solution 1: Build it inside-out - 44 ms, 5 lines
Start with the empty matrix, add the numbers in reverse order until we added the number 1. Always rotate the matrix clockwise and add a top row:
(don't understand it)

def generateMatrix(self, n): 
    A, lo = [], n*n+1 
    while lo > 1: 
        lo, hi = lo - len(A), lo 
        A = [range(lo, hi)] + zip(*A[::-1]) # zip(* list) used to unzip a list
        #zip(*matrix [::-1]) :Rotate the matrix by 90 degrees (clockwise). 
    return A

zip()
in conjunction with the * operator can be used to unzip a list:

**Solution 4:Recursion **
???

#zip(*matrix [::-1]) :Rotate the matrix by 90 degrees (clockwise). 
def generateMatrix(self, n): 
    def gen(l, w, b): 
        #Generate a l*w Matrix. the begin number is b.  
        return l * [[]] and [range(b, b+l)] + map(list,zip(*gen(w-1, l, b+l)[::-1]))
    return gen(n, n, 1)

75. Sort Colors

A rather straight forward solution is a two-pass algorithm using counting sort.
First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's.

Could you come up with an one-pass algorithm using only constant space?

# this approach is not as good as the cpp ones below
def sortColors(self, nums): 
    i = j = 0 
    for k in xrange(len(nums)): 
        v = nums[k] 
        nums[k] = 2 #this is key step! so we don't need to swap
        if v < 2: 
            nums[j] = 1 
            j += 1 
        if v == 0: 
            nums[i] = 0 
            i += 1

The idea is to sweep all 0s to the left and all 2s to the right, then all 1s are left in the middle.

class Solution { 
    public: 
    void sortColors(int A[], int n) { 
        int second=n-1, zero=0; 
        for (int i=0; i<=second; i++) { 
            while (A[i]==2 && i<second) swap(A[i], A[second--]); 
            while (A[i]==0 && i>zero) swap(A[i], A[zero++]); 
            /* this second line is not really exclusive to first line, coz after first line swap, A[i] can be zero again. 
            This is trick to understand: need to use while to keep swap 0 and 2 forward or backward
*/
        } 
    } 
};

33. Search in Rotated Sorted Array (Hard)

    def search(self, nums, target):
        if not nums:
            return -1
        low, high = 0, len(nums) - 1 # index, not value

        while low <= high:
            mid = (low + high) / 2
            if target == nums[mid]:
                return mid

            if nums[low] <= nums[mid]:
                # Key idea: make comparison test with the half array that don't contain turning point
                # first subarray is in order (turning point in second half):
                if nums[low] <= target <= nums[mid]:
                    # if target is in first half:
                    high = mid - 1
                else:
                    low = mid + 1
            else:
                # first half array has turning point
                if nums[mid] <= target <= nums[high]:
                    low = mid + 1
                else:
                    high = mid - 1

        return -1

4. Median of Two Sorted Arrays (Hard)

-- don't understand how to solve it !!!

11. Container With Most Water

In the first thought, we need to have nested for loop to scan over the array in order to find the minimal combination. However, by some clever thought, we can discover the minimal combination by using single one pass for loop.

Review the note and homework in CS341. I have think quite deep for this kind problems.

Proof
Here is the proof. Proved by contradiction:
Suppose the returned result is not the optimal solution. Then there must exist an optimal solution, say a container with aol and aor (left and right respectively), such that it has a greater volume than the one we got. Since our algorithm stops only if the two pointers meet. So, we must have visited one of them but not the other. WLOG, let's say we visited aol but not aor. When a pointer stops at a_ol, it won't move until
The other pointer also points to aol. In this case, iteration ends. But the other pointer must have visited aor on its way from right end to aol. Contradiction to our assumption that we didn't visit aor.

The other pointer arrives at a value, say arr, that is greater than aol before it reaches aor. In this case, we does move aol. But notice that the volume of aol and arr is already greater than aol and aor (as it is wider and heigher), which means that aol and aor is not the optimal solution -- Contradiction!

Both cases arrive at a contradiction.

https://leetcode.com/discuss/37631/simple-and-clear-proof-explanation

def maxArea(self, height): 
    i, j = 0, len(height) - 1 
    water = 0 
    while i < j: 
        water = max(water, (j - i) * min(height[i], height[j])) 
        '''
        The smaller one of first and last line doesn't support a higher water level and can thus be safely removed from further consideration.
        Note: the reasoning here is, we already keep record of current_max. 
        A shorter line don't have the potential of support higher future water level, therefore throw it out. 
        '''
        if height[i] < height[j]: 
            i += 1 
        else: 
            j -= 1 
    return water

15. 3Sum

Find all unique triplets in the array which gives the sum of zero.
Note:

The idea is to sort an input array and then run through all indices of a possible first element of a triplet. For each possible first element we make a standard bi-directional 2Sum sweep of the remaining part of the array. Also we want to skip equal elements to avoid duplicates in the answer without making a set or smth like that. Time complexity: O(n^2)

# Time complexity: O(n^2)
def threeSum(self, nums):  
    """ :type nums: List[int] :rtype: List[List[int]] """ 
    nums.sort() 
    res = [] 
    for i in xrange(len(nums)): 
        if i != 0 and nums[i] == nums[i-1]: continue 
        # if i==0, then we don't have nums[i-1]
        # this is to avoid d
        target = -nums[i] 
        l, r = i+1, len(nums)-1 
        while l < r: 
            if nums[l] + nums[r] == target: 
                res.append((nums[i], nums[l], nums[r])) 
                while l < r and nums[l] == nums[l+1]: l += 1 
                while l < r and nums[r] == nums[r-1]: r -= 1 
                l += 1 
                r -= 1 
            elif nums[l] + nums[r] < target: 
                l += 1 
            else: r -= 1 
                return res

48. Rotate Image

Input:[[1,2],[3,4]]
Expected:[[3,1],[4,2]]

Utilize python's syntax:

# Rotate the matrix by 90 degrees (clockwise):  zip(*matrix[::-1])
# or counter-clockwise:  zip(*matrix)[::-1]
def rotate(self, matrix):
    matrix[:] = zip(*matrix[::-1])

Without relying on python's syntax:

/*
 * clockwise rotate
 * first reverse up to down, then swap the symmetry 
 * 1 2 3     7 8 9     7 4 1
 * 4 5 6  => 4 5 6  => 8 5 2
 * 7 8 9     1 2 3     9 6 3
*/
void rotate(vector<vector<int> > &matrix) {
    reverse(matrix.begin(), matrix.end());
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = i + 1; j < matrix[i].size(); ++j)
            swap(matrix[i][j], matrix[j][i]);
    }
}

/*
 * anticlockwise rotate
 * first reverse left to right, then swap the symmetry
 * 1 2 3     3 2 1     3 6 9
 * 4 5 6  => 6 5 4  => 2 5 8
 * 7 8 9     9 8 7     1 4 7
*/
void anti_rotate(vector<vector<int> > &matrix) {
    for (auto vi : matrix) reverse(vi.begin(), vi.end());
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = i + 1; j < matrix[i].size(); ++j)
            swap(matrix[i][j], matrix[j][i]);
    }
}

34. Search for a Range

???
https://leetcode.com/discuss/22788/search-position-target-target-simple-python-with-little-trick

283. Move Zeroes (S)

class Solution(object):
    # sol1: iterate forward, complicated
    def moveZeroes(self, nums):
        # it is faster if I scan from back. In that case I don't need num_operation
        i = 0
        num_operation = 0
        while i < len(nums) and num_operation < len(nums):
            num_operation += 1
            print nums[i]
            if nums[i]==0:
                nums.pop(i)
                nums.append(0)
            else:
                i += 1
                
    # sol2: don't use index to iterate, wrong:
    def moveZeroes(self, nums):
        for i in reversed(nums):
            print '--',i
            if i==0:
                print '==',i
                nums.pop(i)  # pop requires index, so we need to iterate with index
                nums.append(0)
                
    # sol3: iterate backward, simple:          
    def moveZeroes(self, nums):
        i = len(nums) - 1
        while i >= 0:
            if nums[i] == 0:
                nums.pop(i)
                nums.append(0)
            i -= 1

136. Single Number (M)

def singleNumber1(self, nums):
    dic = {}
    for num in nums:
        dic[num] = dic.get(num, 0)+1
    for key, val in dic.items():
        if val == 1:
            return key

def singleNumber2(self, nums):
    res = 0
    for num in nums:
        res ^= num
    return res

def singleNumber3(self, nums):
    return 2*sum(set(nums))-sum(nums)

def singleNumber4(self, nums):
    return reduce(lambda x, y: x ^ y, nums)

def singleNumber(self, nums):
    return reduce(operator.xor, nums)       
上一篇下一篇

猜你喜欢

热点阅读