Life sucks, but you're gonna love it.

0%

参考网页:

https://www.cs.cmu.edu/~adamchik/15-121/lectures/Binary%20Heaps/heaps.html

Binary Heap的定义

这个堆又分为最小堆和最大堆,所以这里直接用Binary Heap来代替,叫二分堆(?)

Binary heap是一个完整的二叉树,并且满足堆的排序性质,即每次都可以pop出来最大或者最小值,由于这种性质,我们也将堆称作priority queue

完整二叉树说的是除了最后行,其他的分支都被填满的树

根据堆的排序标准,它主要可以被分为两种:

  • 最小堆min-heap: 对于每一个节点来说,他都大于或者等于它的父节点
  • 最小堆max-heap:对于每一个节点来说,他都小于或者等于它的父节点

这里我们只用最小堆minheap来举例。

Array Implementation

在返回堆代表的序列时,是按照遍历每一层的顺序,因此最小堆结构的整体并不是有序的结构,只可能是部分有序,且第一项是最小值。

Screen Shot 2020-07-05 at 13.43.19

Insert

往最小堆中插入一个数字,是先将该数字放在最小堆的末尾,然后和它的parent相比较,如果大于它的parent那就正好不用移动,如果小于它的parent那么就将它和parent换位置,直到到达符合小于parent的位置。

Delete

删除最小值的时候是先将root(最上面的parent,也就是我们的最小值)和堆的最后一位换位置,然后把它换下来。换在最上面的值会和左右两边的parent进行比较,然后往下放,直到换到一个合适的位置(符合最小堆性质)。

Heap sort

就是按照刚才delete的顺序,不听去掉root的值然后进行排序,最差的时长是 O(nlogn)

Python中的heapq包

常用的函数

注意heapq里面是按照最小堆来进行整合的,所以我们每次pop出来的都是最小值。

  • heapq.heappush(*heap*, *item*)

    Push the value item onto the heap, maintaining the heap invariant.

  • heapq.heappop(*heap*)

    Pop and return the smallest item from the heap, maintaining the heap invariant. If the heap is empty, IndexError is raised. To access the smallest item without popping it, use heap[0].

  • heapq.heapify(*x*)

    Transform list x into a heap, in-place, in linear time.

如果我们想使用最大堆的话,可以再输入堆的数字前面加上负号,然后输出的时候再赋予负号

Python中的heapq包也同样有

heapq._heapify_max(heap) 这样的函数,但是目测,如果使用最大堆的话,需要一直使用隐函数的最大堆max的函数,不然的话,heap的顺序就会乱掉。

比如一开始用heapq._heapify_max(nums) 排列了序列,然后再 heapq.heappop(nums) 这样的话输出的是最大值,且pop之后的序列的最小值会换上来,所以中间的序列顺序会被打乱。

【LeetCode】- July Challenge

0701 - Leetcode441 - Arranging coins - Easy

0702 - Leetcode107 - Binary Tree Level Order Traversal II

补充 - Leetcode104 - Binary Tree Level Order Traversal

0703 - Leetcode957 - Prison Cells After N days

0704 - Leetcode264 - Ugly Number II

补充 - Leetcode263 - Ugly Number

0705 - Leetcode461 - Hamming Distance

补充 - Leetcode477 - Total Hamming Distance - Medium

0706 - Leetcode66 - Plus One - Easy

0707 - Leetcode463 - Island Perimeter - Easy

0708 - 3Sum - Medium

0711 - Leetcode78 - Subsets - Medium

0713 - Leetcode100 - Same Tree - Easy

0714 - Leetcode1344 - Angle Between Hands of a Clock - Medium

0717 - Leetcode - Top K Frequent Elements - Medium

0718 - Leetcode210 - Course Scheduel II - Medium

[0719 - Leetcode64 - ]

0720 - Leetcode - Remove Linked List Elements - Medium

0722 - Leetcode103 - Binary Tree Zigzag Level Order Traversal - Medium

0724 - Leetcode797 - All Paths from source to Target - Medium

-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

0724 - Leetcode797 - All Paths From Source to Target

Given a directed, acyclic graph of N nodes. Find all possible paths from node 0 to node N-1, and return them in any order.

The graph is given as follows: the nodes are 0, 1, …, graph.length - 1. graph[i] is a list of all nodes j for which the edge (i, j) exists.

1
2
3
4
5
6
7
8
9
Example:
Input: [[1,2], [3], [3], []]
Output: [[0,1,3],[0,2,3]]
Explanation: The graph looks like this:
0--->1
| |
v v
2--->3
There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3.

解题思路:

把edge储存为 : {到达点:[上一个可能点]} 的字典格式,如果上一个可能的点存在0的话就跳出循环,否则的话就一直循环下去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution(object):
def allPathsSourceTarget(self, graph):
"""
:type graph: List[List[int]]
:rtype: List[List[int]]
"""
edge = defaultdict(list)
for i in range(len(graph)):
link = graph[i]
for k in link:
edge[k] += [i]

ans = []
self.dfs(edge, len(graph) - 1, [], ans)

return ans

def dfs(self, edge, lastnode, path, ans):

if lastnode == 0:
path = path + [0]
ans.append( path[::-1] )
return

for node in edge[lastnode]:
self.dfs(edge, node, path + [lastnode], ans)

0722 - Leetcode103 - Binary Tree Zigzag Level Order Traversal

Given a binary tree, return the zigzag level order traversal of its nodes’ values. (ie, from left to right, then right to left for the next level and alternate between).

For example:
Given binary tree [3,9,20,null,null,15,7],

1
2
3
4
5
  3
/ \
9 20
/ \
15 7

return its zigzag level order traversal as:

1
2
3
4
5
[
[3],
[20,9],
[15,7]
]

解题思路

BFS + 正反向输出 + flag决定

BFS输出,然后设置flag,每次都*-1 可以控制正反向输出。

0720 - Leetcode - Remove Linked List Elements

Remove all elements from a linked list of integers that have value val\.

Example:

1
2
Input:  1->2->6->3->4->5->6, val = 6
Output: 1->2->3->4->5

解题思路:

设置dummy node,查看next.val是否和要去掉的val相同,如果相同,定义next = next.next. 指针不动;如果不同,指针后移一位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def removeElements(self, head, val):
"""
:type head: ListNode
:type val: int
:rtype: ListNode
"""
dummy = ListNode(0)

dummy.next = head
head = dummy

while head.next:

if head.next.val == val:
if head.next.next:
head.next = head.next.next
else:
head.next = None
else:
head = head.next

return dummy.next

0718 - Leetcode210 - Course Scheduel II

There are a total of n courses you have to take, labeled from 0 to n-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.

There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.

解题思路:

  1. DFS

  2. Node in degree(入度)

    当我们把pre课程当做出发点,而现在要选的课程当做目的地

0717 - Leetcode347 - Top K Frequent Elements

0714 - Leetcode1344 - Angle Between Hands of a Clock

Given two numbers, hour and minutes. Return the smaller angle (in degrees) formed between the hour and the minute hand.

解题思路:

就直接写,然后钝角转化为 360 - 本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def angleClock(self, hour, minutes):
"""
:type hour: int
:type minutes: int
:rtype: float
"""
angle_h = (hour%12) * 30 + minutes * 0.5

angle_m = minutes * 6

# print(angle_h, angle_m)
if abs(angle_h - angle_m) > 180:
return 360 - abs(angle_h - angle_m)
return abs(angle_h - angle_m)

0713 - Leetcode100 - Same Tree

Given two binary trees, write a function to check if they are the same or not.

Two binary trees are considered the same if they are structurally identical and the nodes have the same value.

解题思路:

Recursive - 分别判断每个节点是否相等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def isSameTree(self, p, q):
"""
:type p: TreeNode
:type q: TreeNode
:rtype: bool
"""
return self.checkNode(p, q)

def checkNode(self, node1, node2):


if not node1 and not node2:
return True

if not node1 or not node2 or node1.val != node2.val:
return False

return self.checkNode(node1.left, node2.left) and self.checkNode(node1.right, node2.right)

0711 - Leetcode78 - Subsets

Given a set of distinct integers, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

解题思路:

用recursive吧,因为不希望出现重复项,所以新加进来的数可以视为前一个subset 加上 一个新的数字,而这个新的数字是subdet最后一位数字往后的。所以可以用resurcive。

比如说

  • [] 是初始解,那么[] +[1], [] + [2], []+[3] 就是第一层解
  • [1] + [2], [1]+[3] 就要继续下去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
ans = []
sub = []
ans.append(sub)
self.newsub(sub, nums, 0, ans)

return ans


def newsub(self, sub, nums, i, ans):

while i < len(nums):
ans.append(sub + [nums[i]])
self.newsub(sub + [nums[i]], nums, i+1, ans)
i += 1
return

0708 - 3Sum

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:

The solution set must not contain duplicate triplets.

Example:

1
2
3
4
5
6
7
Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]

解题思路:

先排序,再固定指针 i ,然后从这个指针向右的序列确定两个指针 left, right, 寻找nums[i] = -(nums[left]+nums[right])。

如果左右两指针相加大于 -(nums[left]+nums[right]),right -= 1, 否则 left+=1.如果相等就把者三个数的list放在ans中。

注意!!!

这个题直接这样写会超时,所以要考虑很多特殊的情况。

  1. if nums[i] > 0, break 当最小值都大于零,右边的nums[left]和nums[right]肯定都大于零,就不可能存在三个数相加为0的情况;

  2. 判断是否有重复: if nums[left - 1] > i and nums[left - 1] == nums[left]: 这时候left可以直接+1忽略

    同理,可以跳过重复的右指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums.sort()
ans = []
for i in range(len(nums)):
left = i + 1
right = len(nums) - 1

if i > 0 and nums[i] == nums[i-1]: continue

while left < right:
if left - 1 > i and nums[left] == nums[left-1]:
left += 1
continue
if right+1 < len(nums) and nums[right] == nums[right+1]:
right -= 1
continue

if nums[left] + nums[right] == -nums[i]:
ans.append([nums[i], nums[left], nums[right]])
left += 1

elif nums[left] + nums[right] > -nums[i]:
right -= 1
else:
left += 1
return ans

0707 - Leetcode463 - Island Perimeter

You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water.

Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells).

The island doesn’t have “lakes” (water inside that isn’t connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don’t exceed 100. Determine the perimeter of the island.

Example:

1
2
3
4
5
6
7
Input:
[[0,1,0,0],
[1,1,1,0],
[0,1,0,0],
[1,1,0,0]]

Output: 16

解题思路:

很简单,遍历grid中的每一项,如果是1,那么他就会有4条边,然后减去临边也为1的数量,然后就可以得到小岛的周长。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def islandPerimeter(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
row = len(grid)
col = len(grid[0])
count1 = 0
edge = 0
for i in range(row):
for j in range(col):
if grid[i][j] == 1:
count1 += 1
edge = edge + (self.countLand(grid, i+1, j) +self.countLand(grid, i-1, j)+self.countLand(grid, i, j+1)+self.countLand(grid, i, j-1))


return 4*count1 - edge


def countLand(self, grid, i, j):
land = 0
if i >= 0 and i < len(grid) and j >= 0 and j < len(grid[0]):
land += grid[i][j]
return land

补充 - Leetcode695 - Max Area of Island

Given a non-empty 2D array grid of 0’s and 1’s, an island is a group of 1‘s (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.

Find the maximum area of an island in the given 2D array. (If there is no island, the maximum area is 0.)

解题思路:

  1. DFS。寻找最大岛屿面积,遍历每一个点,然后从每一个点延伸开始寻找它周围的是1的点,遍历过的点改标记为0.

    之前有一个题,也是遍历每一个点,然后遍历的时候改为-1,之后遍历完又换回来。这种是 解跟解之间独立的,比如是某条路径(我实在找不到那道题了),还要换回来因为可能此路不通,我们还要走下一条路。但是这种求面积的,包括数岛屿个数的题,我们在换了标记之后表示我们已经count了这个grid[i][j]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def maxAreaOfIsland(self, grid):
    """
    :type grid: List[List[int]]
    :rtype: int
    """
    max_area = 0

    for i in range(len(grid)):
    for j in range(len(grid[0])):
    max_area = max(max_area, self.dfs(grid, i, j))

    return max_area


    def dfs(self, grid, i, j):

    if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] == 0:
    return 0

    grid[i][j] = 0

    return self.dfs(grid, i+1, j) + self.dfs(grid, i-1, j) + self.dfs(grid, i, j+1) + self.dfs(grid, i, j-1) + 1

0706 - Leetcode66 - Plus One

Given a non-empty array of digits representing a non-negative integer, plus one to the integer.

The digits are stored such that the most significant digit is at the head of the list, and each element in the array contain a single digit.

You may assume the integer does not contain any leading zero, except the number 0 itself.

Example 1:

1
2
3
Input: [1,2,3]
Output: [1,2,4]
Explanation: The array represents the integer 123.

解题思路:

  1. 硬做。这道题就是在问给出的list如果代表的是一个数字,那么这个数字加上1之后,得到的list会是什么样子。给出的例子都很简单,简直是迷惑人。因为有几种特殊的情况,一种是最后一位为9的时候,它加上1之后要进位的,另一种情况是最高位同样为9的时候,它加上一是要变多一位的。所以单纯对list里面的数字进行加1处理远远不够。 我一开始想的就是把给的list转化为数字,然后+1,然后再拆解成list。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def plusOne(self, digits):
    """
    :type digits: List[int]
    :rtype: List[int]
    """
    num = 0
    i = 0
    while i < len(digits):
    num = 10*num + digits[i]
    i += 1
    num = num+1
    plus = []
    while num > 0:
    plus.append(num % 10)
    num //= 10

    return plus[::-1]
  2. recursion

    迭代去做考虑的因素就不需要分开考虑究竟最高位是不是9,那我们返回的值就是当我们所处的这一位是9的话,就把这个数字改换为0,然后对前面的数字进行+1处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def plusOne(self, digits):
    """
    :type digits: List[int]
    :rtype: List[int]
    """
    if len(digits) == 0:
    return [1]
    if digits[-1] < 9:
    digits[-1] += 1
    return digits
    elif digits[-1] == 9:
    return self.plusOne(digits[:-1]) + [0]

0705 - Leetcode461 - Hamming Distance

The Hamming distance between two integers is the number of positions at which the corresponding bits are different.

Given two integers x and y, calculate the Hamming distance.

Note:
0 ≤ x, y < 231.

Example:

1
2
3
4
5
6
7
8
Input: x = 1, y = 4

Output: 2

Explanation:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑

解题思路:

我一开始是想着移位运算有没有取最后一位然后取异或的,但是没想起来该怎么写。于是就用了最原始的求数字二进制的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def hammingDistance(self, x, y):
"""
:type x: int
:type y: int
:rtype: int
"""
count = 0
while True:
if x == 0 and y == 0:
break

count += (x%2) ^ (y%2)

x = x // 2
y = y // 2

return count

后面查了一下确实有直接算二进制的:

bin(x) 可以返回x的二进制表示形式,但是返回的值是一个string

int(x, 16) 可以将16进制下的字符串类型x转化为一个十进制的int形式的数字

补充 - Leetcode477 - Total Hamming Distance

The Hamming distance between two integers is the number of positions at which the corresponding bits are different.

Now your job is to find the total Hamming distance between all pairs of the given numbers.

Example:

1
2
3
4
5
6
7
Input: 4, 14, 2

Output: 6

Explanation: In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just
showing the four bits relevant in this case). So the answer will be:
HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6.

解题思路:

  1. 和上面一题不同,这道题如果每两对都要计算一下hamming distance再相加的话,就会造成很多重复的计算。所以我是直接想用数学方法写的,没有用到算法的技巧。

    假设我们的 list 中一共有 N 个数字,因为我们要求这些数字的二进制表示下的位数的差异,所以我们需要把这些数字都用0 1表示。那么对于这N个数字而言,拿出每两个数字然后逐位进行比较就相当于,对于每一位而言,我们把这N个0或1的数字拿出来,然后看看能有几个不一样。所以用到了排列组合的思想。

    对于每一位而言,N个数字一共会有个 $2 \choose N$ 种结果,$ {2 \choose N } = {N-2 \choose N} = (N-1)*N$, 而在这一位上面,我们只有两种选择,要么0 要么1,所以我们再利用一个count list来统计一下每一位1的个数count[i]。最后对于每一位而言,两两数字不同的可能性就有 ${2 \choose N} - {2 \choose count[i]} - {2 \choose N-count[i]}$.

    扫了一眼讨论区,哦,我好蠢,两两不同的可能性就是 $count[i] \times (N - count[i])$。大部分也是通过数学计算来解决这个问题的。

    又学了一招 "{0:b}".format(num) 可以把num转化为二进制字符串

    print("bin: {0:b}, oct: {0:o}, hex: {0:x}".format(12))

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def totalHammingDistance(self, nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    count = [0] * 32
    for num in nums:
    i = 0
    while num != 0:
    count[i] += num % 2
    num /= 2
    i += 1
    N = len(nums)
    total_ham = 0

    for i in range(len(count)):
    if count[i] > 0:
    total_ham += N * (N - 1)/2 - count[i]*(count[i]-1)/2 - (N - count[i])*(N - count[i] - 1)/2

    return total_ham

0704 - Leetcode264 - Ugly Number II

Write a program to find the n-th ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.

Example:

1
2
3
Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.

解题思路:

  1. 最小堆帮助排序:主要还是明确Ugly Number的定义吧,指的就是factor只有2,3,5的数字。那么就意味着我所有的数字只能是ugly number的乘积。所以就从1开始,之后所有的数字都是ugly number里面的数字 2 3 *5.

    一开始想用动态规划,但这里存在一个排序的问题,第一个数字乘 2 3 5和第二个数字乘2 3 5,哪个应该放在哪个后面。但后来想到用min heap这个结构,会把最小的放在上面,每次我们pop出来最小的数字放在我们的sorted ugly number lsit中,然后再把它乘以2 3 5的数字放进我们的min heap中,就行了。如果pop出来的数字已经在我们的sorted ugly number里面了,那我们就跳过这个数字。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def nthUglyNumber(self, n):
    """
    :type n: int
    :rtype: int
    """
    sorted_ugly = []
    heap = [1]
    heapq.heapify(heap)

    while len(sorted_ugly) < n:
    next_ugly = heapq.heappop(heap)
    if next_ugly not in sorted_ugly:
    sorted_ugly.append(next_ugly)
    heapq.heappush(heap, next_ugly*2)
    heapq.heappush(heap, next_ugly*3)
    heapq.heappush(heap, next_ugly*5)
    return sorted_ugly[-1]
  2. 动态规划

    把2 3 5需要乘的数字表示为指针,然后依次升高,进行排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def nthUglyNumber(self, n):
    """
    :type n: int
    :rtype: int
    """
    dp = [1]
    pointer2 = 0
    pointer3 = 0
    pointer5 = 0
    while len(dp) < n:
    dp = dp + [min(dp[pointer2]*2, dp[pointer3]*3, dp[pointer5]*5)]

    if dp[-1] == dp[pointer2]*2:
    pointer2 += 1
    if dp[-1] == dp[pointer3]*3:
    pointer3 += 1
    if dp[-1] == dp[pointer5]*5:
    pointer5 += 1

    return dp[-1]

补充 - Leetcode263 - Ugly Number

Write a program to check whether a given number is an ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.

Example 1:

1
2
3
Input: 6
Output: true
Explanation: 6 = 2 × 3

解题思路:

更直接一点,只用循环除以2 3 5 就可以了。注意小于0的数字和 =0 的数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def isUgly(self, num):
"""
:type num: int
:rtype: bool
"""

if num == 1: return True
if num == 0: return False

while num != 1:

if num % 2 == 0:
num = num //2
elif num%3 == 0:
num = num//3
elif num%5 == 0:
num = num//5
else:
return False
return True

0703 - Leetcode957 - Prison Cells After N days

There are 8 prison cells in a row, and each cell is either occupied or vacant.

Each day, whether the cell is occupied or vacant changes according to the following rules:

  • If a cell has two adjacent neighbors that are both occupied or both vacant, then the cell becomes occupied.
  • Otherwise, it becomes vacant.

(Note that because the prison is a row, the first and the last cells in the row can’t have two adjacent neighbors.)

We describe the current state of the prison in the following way: cells[i] == 1 if the i-th cell is occupied, else cells[i] == 0.

Given the initial state of the prison, return the state of the prison after N days (and N such changes described above.)

解题思路:

  1. 规律总结,我是总结规律来的。因为如果写一个方程然后运行N次感觉时间复杂度会比较高。总结规律之后,14变换为一轮回,然后求余数就可以了。但是这个应该是有更底层的解释,为什么14个一个轮回。还有看到的规律是,几次之后,后面的模式和前面相比是前半部分和后半部分调换了位置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # Time: O(1), Space: O(1)
    def prisonAfterNDays(self, cells, N):
    """
    :type cells: List[int]
    :type N: int
    :rtype: List[int]
    """
    if N == 0: return cells
    temp = [0]*8
    for i in range(1, len(cells) - 1):
    temp[i] = 1 - cells[i-1] ^ cells[i+1]

    cells = temp

    re = (N - 1) % 14
    while re > 0:
    temp = [0]*8
    for i in range(1, len(cells) - 1):
    temp[i] = 1 - cells[i-1] ^ cells[i+1]
    cells[1:8] = temp[1:8]
    re -= 1
    return cells

  2. 常规做法:将pattern保存在dictionary中,然后看几个一循环。(因为有的时候可能循环周期不同

    需要注意的是保存在字典里的数字是我们接下来要找的余数,余数是从 0 到 T-1(T为周期),所以N在计算的时候需要做-1.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # Time: O(1) Space: O(1)

    def prisonAfterNDays(self, cells, N):
    """
    :type cells: List[int]
    :type N: int
    :rtype: List[int]
    """
    pattern_dict = {}
    # cells = self.nextPattern(cells)
    n = 0
    while True:
    n += 1
    cells = self.nextPattern(cells)
    if cells in pattern_dict.values():
    break
    else:
    pattern_dict[n-1] = cells
    T = n - 1

    return pattern_dict[(N-1) % T]

0702 - Leetcode107 - Binary Tree Level Order Traversal II (104同理)

Given a binary tree, return the bottom-up level order traversal of its nodes’ values. (ie, from left to right, level by level from leaf to root).

For example:
Given binary tree [3,9,20,null,null,15,7],

1
2
3
4
5
  3
/ \
9 20
/ \
15 7

return its bottom-up level order traversal as:

1
2
3
4
5
[
[15,7],
[9,20],
[3]
]

解题思路:

普通的BFS每层拿出,遍历,再将每层的left right放入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def levelOrderBottom(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
if not root: return []

queue = [root]
ans = []
while len(queue) > 0:
nodes = []
while len(queue) > 0:
nodes.append(queue.pop(0))
# print(nodes)
tmp = [n.val for n in nodes]
ans.append(tmp)

for n in nodes:
if n.left:
queue.append(n.left)
if n.right:
queue.append(n.right)
return ans[::-1]

上述如果返回的是 ans 那就是不bottom up的 level traverse 遍历二叉树

1
2
3
4
5
6
# 学到了deque 读(deck)

from collections import deque

deque.pop()
deque.popleft() # 和append组合相当于queue的FIFO

0701 - Leetcode441 - Arranging coins

You have a total of n coins that you want to form in a staircase shape, where every k-th row must have exactly k coins.

Given n, find the total number of full staircase rows that can be formed.

n is a non-negative integer and fits within the range of a 32-bit signed integer.

Example 1:

1
2
3
4
5
6
7
8
n = 5

The coins can form the following rows:
¤
¤ ¤
¤ ¤

Because the 3rd row is incomplete, we return 2.

解题思路:

  1. 看到这道题首先想到的是斐波那契数列,因而想到了用动态规划来写。这道题不是斐波那切数列啊!就是看到这个排列想到了。如果用动态规划写的话:

    • dp[i] 表示的就是当第i行排满之后,总共需要几个coin
    • 状态转移方程 dp[i] = dp[i-1] + i
    • 实质上也是等差数列求和:求一个 int(k) 使得 $\frac{k(k+1)}{2} <= n$, 得出的解是 $k = \frac{-1+\sqrt{1+8n}}{2} = \sqrt{2n + 0.25} - 0.5$
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # Time: O(sqrt(n)) Space: O(sqrt(n))
    # 这样写会TLE
    def arrangeCoins(self, n):
    """
    :type n: int
    :rtype: int
    """

    if n == 0: return 0
    dp = []
    dp = dp+[0]
    count = 1
    while n >= dp[-1]:
    dp = dp + [count + dp[-1]]
    count += 1

    return count - 2
  2. 写的时候直接用n减去每次的循环序号(i.e. 第一次减去1,第二次减去2)当n小于0的时候就跳出循环,返回循环的次数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # Time: O(sqrt(n))(?是这样吗) Space: O(1)

    def arrangeCoins(self, n):
    """
    :type n: int
    :rtype: int
    """
    if n == 0: return 0
    if n <= 2: return 1

    count = 0
    while n >= 0:
    count += 1
    n -= count

    return count - 1
  3. 看了别人写的之后还有Binary Search. 二叉树的时候更新左右两个端点总是会忽略的一个问题是,如果你要保左端点,那在算中值的时候需要+1,这样才能保证不陷入死循环。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def arrangeCoins(self, n):
    """
    :type n: int
    :rtype: int
    """
    left = 0
    right = n
    while left < right:
    # print(left, right)
    mid = (left + right + 1)/2

    if mid*(mid+1)/2 < n:
    left = mid
    if mid*(mid+1)/2 > n:
    right = mid - 1

    if mid*(mid+1)/2 == n:
    return mid

    return left

参考网页: https://www.cs.cmu.edu/~adamchik/15-121/lectures/Trees/trees.html

二叉树的定义

我们将Linked Data Structure的结构拓展,使得一个节点可以和两个node相连接。二叉树的结构由节点构成,有左节点和右节点并且和它本身的值。最顶层的node被称为根root

每一个node(包括root在内)都只和另一个node相连,它们之间的连接成为edge,而该node被称为parent。另一方面来讲,一个node可以和任意多个node相连,每一个node都是他的children(就是在说一个node可以有多个children但是只能有一个parent),没有children的node被称作leaves, 处在中间的node都叫内部节点,拥有相同parent的node被称为siblings.

二叉树的遍历

前序遍历 preorder

遍历顺序是:parent-left-right

  • 给定Binary Tree, 求前序遍历得到的数值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #Recursive

    def preorder(self, root):
    ans = []
    self.helper(root, ans)
    return ans

    def helper(self, root, ans):

    if root:
    ans.append(root.val)
    self.helper(root.left, ans)
    self.helper(root.right, ans)

    # Iterative

    stack = []
    ans = []

    while root or len(stack) > 0:
    while root:
    ans.append(root.val)
    stack.append(root.right)
    root = root.left
    root = stack.pop()

    return ans


中序遍历 inorder

遍历顺序是:left-parent-left

  • 给定Binary Tree, 求中序遍历得到的数值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    # Recursive

    def inorder(self, root):
    ans = []

    self.helper(root, ans)
    return ans

    def helper(self, root, ans):

    if root:
    self.helper(root.left)
    ans.append(root.val)
    self.helper(root.right)

    # Iteration

    def inorder(self, root):
    ans, stack = [], []
    while root or len(stack) > 0:

    while root:
    stack.append(root)
    root = root.left

    root = stack.pop()
    ans.append(root.val)
    root = root.right

    return ans


后序遍历 postorder

遍历顺序是:left-right-parent

  • 给定Binary Tree,求后序遍历输出的数列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    # Recursive

    def postorder(self, root):

    ans = []
    return self.helper(self, root, ans)

    def helper(self, root, ans):

    if root:
    self.helper(root.left)
    self.helper(root.right)
    ans.append(root.val)

    # Iterative

    def postorderTraversal(self, root):
    """
    :type root: TreeNode
    :rtype: List[int]
    """
    ans, stack = [], [root]


    while len(stack) > 0 and root:

    root = stack.pop()
    ans.append(root.val)

    if root.left:
    stack.append(root.left)

    if root.right:
    stack.append(root.right)

    return ans[::-1]

摘要

我们学习了一种描述人体形状和姿态变换模型,这种模型应用了类似的资源并且较之前的模型更为精确。我们的Skinned Multi-Person Linear (SMPL)模型是基于skinned vertex的模型,可以准确得描述人的形状和姿态。模型的参数从包括了静态姿态模板,blend weight,基于姿态的blend shape还有基于人个体的blend shape还有一个由人体模型中的顶点到骨骼joint的regressor中学习。(这些专业的名词太多了,一会儿也会说到)。和之前模型不一样的是,pose-dependent blend shape是姿态旋转矩阵的线性方程。这个 简单的模型可以让我们在大量的对齐人体不同姿态中学习。 SMPL模型的准确度比SCAPE要高,并且因为SMPL模型是基于blend skinning的,所以在渲染engine中也可以使用。

Read more »

排序算法

Quick Sort - 快速排序

快排的主要思想是divide and conquer,是把一个大问题划分成几个小问题然后再合并在一起的方法。

核心问题就是找到一个pivot的位置,使得pivot左边的数字都小于pivot,而右边的数字都大于pivot,这样pivot的位置就定了,然后我们再分别对两边的数字再进行:寻找pivot - 找左边 - 找右边。

这种循环进行的操作的好处在于,我们找到的pivot的位置是正确的,然后在每个小问题中,不断地实施这个操作,就可以让每个数字找到合适自己的位置。

假设我们有一个可以帮助我们寻找pivot的函数 Partition,那么排序的过程就可以写为:

1
2
3
4
5
6
7
8
9
def quicksort(arr, low, high):

if low < high:
pivot = partition(arr, low, high)

quicksort(arr, low, pivot-1)
quicksort(arr, pivot+1, high)

return arr

返回pivot的位置可以帮助我们固定pivot,更好得对pivot之前和之后个数组进行操作。

那么问题是,现在如何定义函数Partition?

函数Partition的主要作用是,将输入array中的某一个数定义为pivot,并且确定它在array中的位置。我们一般选取array最后一个值作为pivot,然后将这个pivot放在正确的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def partition(arr, low, high):
i = 0
j = 0
mid = (low + high)//2
arr[mid], arr[-1] = arr[-1], arr[mid]
while i < len(arr) - 1:

if arr[i] < arr[-1]:
arr[i], arr[j] = arr[j], arr[i]
# print(arr)
j += 1
i += 1

arr[j], arr[-1] = arr[-1], arr[j]

return j

Bubble Sort - 冒泡排序

冒泡排序的核心思想在于两两比较数字之间的大小然后将最大的移动到最后。

第一步bul

时间复杂度 : O(n^2)

空间复杂度:O(1)

1
2
3
4
5
6
7
8
9
10
def bubblesort(arr):
k = len(arr)
while k > 0:
for i in range(1, k):
if arr[i] < arr[i-1]:
arr[i], arr[i-1] = arr[i-1], arr[i]
i += 1
k -= 1

return arr

算是比较简单明白的排序,注意一点的是 while k > 0 和后面的k -= 0 同样可以用一句 for _ in range(len(arr)) 代替,这样时间复杂度会更高。之前用while是考虑到后面比较大的数字已经放在最后就不需要考虑了。时间复杂度是 $O(n^2)$

Merge Sort - 归并排序

归并排序的方法主要思想也是divide and conquer,既然如此,我们需要把大的排序分解成小的序列,然后排序之后再合并在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def merge(left, right):
# print(left, right)
i = 0
j = 0
merge_arr = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
merge_arr.append(left[i])
i += 1
else:
merge_arr.append(right[j])
j += 1
if i == len(left):
merge_arr = merge_arr + right[j:]
else:
merge_arr = merge_arr + left[i:]
print(merge_arr)
return merge_arr


def mergeSort(arr, start, end):

if start == end:

return arr[start:end+1]

mid = (start + end)//2
left = mergeSort(arr, start, mid)
right = mergeSort(arr, mid + 1, end)

merge_arr = merge(left, right)

return merge_arr

Insertion Sort - 插入排序

插入排序的思想是将未排序的值插入到之前的已经排序的数列中去。

时间复杂度:O(n^2)

空间复杂度:O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
def insertionsort(arr):

for i in range(1, len(arr)):
print(arr)
k = i
while k > 0:
if arr[k] < arr[k - 1]:
arr[k], arr[k-1] = arr[k-1], arr[k]
k -= 1
else:
break

return arr

【LeetCode】- June Challenge

0606 - Leetcode1029 - Two City Scheduling - Easy

0607 - Leetcode518 - Coin Change 2 - Medium

补充 - Leetcode322 - Coin Change - Medium

0608 - Leetcode231-Power Of Two - Easy

0609 - Leetcode392 - Is Subsequence - Easy

补充 - Leetcode115 - Distinct Subsequence - Hard

0610 - Leetcode35 - Search Insert Position - Easy

0611 - Leetcode75 - Sorting Colors - Medium

0612 - Leetcode380 - Insert Delete GetRandom O(1) - Medium

0613 - Leetcode368 - Largest Divisible Subset - Medium

补充 - Leetcode300 - Longest Increasing Subsequence - Medium

0614 - Leetcode787 - Cheapest Flights within K stops - Medium

0615 - Leetcode700 - Search in a Binary Search Tree - Easy

0616 - Leetcode468 - Validate IP Address - Medium

0617 - Leetcode130 - Surrounded Regions - Medium

补充 - Leetcode200 - Number of Islands - Medium

补充 - Leetcode547 - Friend Circles - Medium

0618 - Leetcode275 - H-index II - Medium

0619 - Leetcode1044 - Longest Duplicate Substring - Hard

0620 - Leetcode60 - Permutation Sequence - Medium

补充 - Leetcode31 - Next Permutation - Medium

补充 - Leetcode46 - Permutations - Medium

0621 - Leetcode174 - Dungeon Games - Hard

0622 - Leetcode137 - Single Number II - Medium

补充 - Leetcode136 - Single Number I - Easy

补充 - Leetcode260 - Single Number III - Medium

0623 - Leetcode222 - Count Complete Tree Node - Medium

0624 - Leetcode96 - Unique Binary Search Trees - Medium

补充 - Leetcode95 -Unique Binary Search Trre II - Medium

0625 - Leetcode287 - Find the Duplicate Number - Medium

补充 - Leetcode142 - Linked List Cycle II - Medium

0626 - Leetcode129 - Sum Root to Leaf Number - Medium

0627 - Leetcode279 - Perfect squares - Medium

0628 - Leetcode322 - Reconstruct Itenerary - Medium

0629 - Leetcode62 - Unique Paths - Medium

补充 - Leetcode63 - Unique Paths II - Medium

0630 - Leetcode212 - Word Search II - Hard

补充 - Leetcode79 - Word Search I - Medium

Read more »

提取CCL双语语料库信息【简易网页内容抓取】

说明:此程序非商用,纯属个人提取数据时写的简单的抓取网页信息的程序,具体内容请参阅 CCL语料库使用说明 。感谢CCL语料库的创建和维护者。转载请标明出处

适用范围:CCL双语语料库

输入信息:查询词汇query,词汇左右两边限定词汇长度max_left, max_right

返回内容:无 (会在当前文件夹下创建 ./CCL_corpus/ 文件夹保存目标词汇的搜索结果)

图片说明:

3281589670804_.pic_hd3291589670915_.pic_hd

具体程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import re
import requests
import json
import os

def extract_sentences(query:str='bill', max_left:int=300, max_right:int=300):
"""

:param query: 目标搜索词 target word
:param max_left: 左侧提取最多字数(以char为单位) maximum str length before target word
:param max_right: 右侧提取最多字数(以char为单位)minmum str length after target word
:return: None. (Generate the file containing sentences)
"""

page = 1
while True:
web = 'http://ccl.pku.edu.cn:8080/ccl_corpus/search?dir=chen&q='+query+'&LastQuery='+query+'&start='+str((page-1)*50)+'&num=50'+\
'&index=FullIndex&outputFormat=HTML&orderStyle=score&encoding=UTF-8&neighborSortLength=0&maxLeftLength='+str(max_left)+'&maxRightLength='+str(max_right)+'&isForReading=yes'
response = requests.get(web)

if response.raise_for_status(): # check if the website is available
print(response.raise_for_status())

break

url_text = response.content.decode()

se = re.search(
r'<td width=\"3%\">(\d+)<\/td><td width=\"45%\" valign=\"top\" colspan=\"3\" align=\"left\">(.+?)<\/td><td width=\"45%\" valign=\"top\" colspan=\"3\" align=\"left\">(.+?)<\/td><\/tr>',
url_text, re.S)

if not se: # If there is no matching pattern, break
break

match = re.finditer(r'<td width=\"3%\">(\d+)<\/td><td width=\"45%\" valign=\"top\" colspan=\"3\" align=\"left\">(.+?)<\/td><td width=\"45%\" valign=\"top\" colspan=\"3\" align=\"left\">(.+?)<\/td><\/tr>',
url_text, re.S)

if not os.path.isdir('../CCL_corpus'):
os.mkdir('../CCL_corpus')

with open('../CCL_corpus/ccl_'+query+'.txt','a') as f:
for m in match:

f.write(m.group(1)+' '+ re.sub(' +', ' ', re.sub('\n', ' ', m.group(2)))+' '+
re.sub(' +', ' ', re.sub('\n', ' ', m.group(3)))+'\n')

page += 1

if __name__ == "__main__":

target_words_list = ['address', 'appreciate', 'beat', 'bill', 'bond', 'column', 'cover', 'deliver', 'exploit', 'figure','perspective', 'platform', 'provision', 'rest']
for t in target_words_list:
extract_sentences(t)

对Deep Learning的简介

简单来说,Deep Learning的发展从最早的Perceptron开始一直到现在的蓬勃发展也是经历了很多。

就像之前说的,在做Machine Learning的时候,分为3步,Deep Learning也是一样。

  1. Define Function sets 这里的function sets就是neural network,因为neural network中参数不同,所以这些是一系列的方程集。
  2. Goodness of function 在寻找比较适合模型的参数的时候,我们找到的评价模型的是损失函数。根绝不同的任务,会有不同的损失函数
  3. Minimize loss function 无论我们最终应用的损失函数是什么,我们最终的目的都是最小化损失函数。在最小化的过程中我们可以用到一些优化的方法。

在之前的分类和回归的任务中,我们所做的基本上都是一层网络。在regression任务中,是输入数据经过一个参数方程然后得到输出;在classfication任务中,是输入经过一个参数方程,再经过一个sigmoid方程进行分类。

但有时候,只通过一个参数方程无法满足我们复杂的需求,所以神经网络就是让这些参数方程横向变多,纵向变厚。我们通过计算不同的参数方程,再结合不同参数方程的结果得到我们最终想要的分类或回归结果。

这些中间不断生成和组合的层就叫做Hidden Layers,结合了很多隐藏层的网络就被称为深度网络。

目前我我们可以用很多框架(tensorflow,pytorch等)来实现深度神经网络的搭建和计算。

BackPropagation

其实在之前我们提到的梯度下降和Backpropagation差不多, 差别是BackPropagation所涉及的参数和流程更为复杂, 因为在Neural Network中存在的参数更多。

在linear regression和logistic regression中,我们更新参数的步骤很简单,因为预测的结果很简单,可以可以直接用feature $\bf x$ 乘以各自的权重 $\bf w$ 得到预测值 $\bf y = xw$,所以我们在更新参数 $\bf w$ 的时候只用一步计算$\bf w = w - \frac{\part L(w)}{\part w}$参数对结果的导数,然后就可以更新了,但是Neural Network的参数有很多,而且有很多层。

我们先来看一下NeuralNetwork的结构:

这部分介绍的是对于当前大家在做深度学习用到的optimization的几种方法的总结介绍以及改进的方面。首先会介绍常用的几种optimization的方法,然后是对现行几种方法的总结和优劣势分析。

标注说明

  • $\theta_t$ 表示时刻 $t$ 的参数值。这个参数就是我们要训练的模型中的参数集合;
  • $\Delta L(\theta_t) / g_t$ 表示时刻 $t$ 参数的gradient,这个是我们在更新参数时需要用到的;
  • $m_t$ 表示截止到 $t$ 时刻,之前累积的 momentum的和
  • $\eta$ 表示学习率

常见的Optimization回顾

  • SGD(stochastic gradient descent)

    这里说的是随机梯度下降,也就是输入一个example之后就对这个example的梯度进行计算,然后根据公式

    表示模型中的参数会根据本次的梯度,在固定学习率$\eta$ 的状态下进行变化。

  • SGDM (stochastic gradient descent with momentum)

    这里说的是在之前随机梯度下降的基础上加上了对梯度的改进,也就是加上了Momentum对梯度的影响:

    这里若是将 $vt$ 带回第一个公式中,就变为 $\theta{t+1} = \theta{t}-\eta g_t + \lambda v{t-1}$, 和SGD的公式比起来,多加了一项 $\lambda v_{t-1}$ 这项是之前的momentum的历史。

  • Adagrad

    最理想的学习率的变化是在一开始学习的时候,学习率会比较大,这样的话可以减少寻找最佳位置的时间,但是随着我们寻找越接近最佳的点,学习率需要变小,这样的话不至于一步错过需要找寻的点。所以我们希望学习率会随着时间的变化减小,即 $\eta_t = \frac{\eta}{\sqrt{t+1}}$.

    但只做这点改变是不够的的,即使我们对学习率做了变化,它也是随着时间的变化。对于不同的参数,学习率的变化量仍旧是个定值。我们期望学习率能够在自己参数本身变化的情况下进行改变,也就是说有些参数变化的波动大,有些参数变化的波动小,我们希望学习率能根据参数本身做出变化。

    因此, 我们希望考虑过去所有计算过的梯度:

  • RMSProp

    当我们在进行Adagrad的时候,分母的梯度平方和会不停累计变大,如果在一开始我们遇到了一个比较大的梯度,那么会造成

    我们考虑的梯度变化公式为:

    如果将第二个式子带入梯度更新的式子中去,我们会得到:

    这样就可以得到,当 $\alpha$ 的值很小的时候,学习率的分母将会只记住和现在时刻离得比较近的梯度值,时间比较靠前的梯度值都被 $\alpha^n$ 给消去了。

  • Adam

    最后来说一下Adam,Adam是结合了SGDM 和 RMSProp的方法。

    主要的更新公式为:

    $m_t$ 的部分就是采用了之前SGDM对梯度的改变,而 $v_t$ 的部分采用的是RMSProp对学习率的改变。

对于SGDM和Adam的改进

在神经网络急速发展的现在,我们除了上述的几种optimization之外,还记过什么其他别的optimization的方法吗?有,但是很少。在计算机视觉任务中,我们常见到的就是Adam优化方法,和SGD优化方法。其他的很少见也很少尝试,那究竟是为什么呢?

因为SGDM和Adam占尽了先机。我们说,普通的梯度下降 $\theta{t+1} = \theta{t}-\eta g_t$, 在这其中,影响梯度下降的两个因素,一个就是学习率 $\eta$ 另一个就是 梯度 $g_t$。 SGD包括SGDM可以说是对梯度做了调整,而Ada-系是调整了学习率。所以这两种方法从两个方面改进了梯度下降时的自我调整水平。

但是SGDM和Adam分别有各自的优缺点:

  • SGDM在训练的时候下降比较慢,训练集上的精度没有Adam高,但是测试集上的精度会比较高
  • Adam在训练的时候下井比较迅速且精度较高,但是在测试时达到的精度不是很高。

那大家就像根据SGDM和Adam各自的优点和缺点来进行改进。

针对SGDM进行改进

针对Adam进行改进