数据结构与算法之美
王争
前Google工程师
立即订阅
71638 人已学习
课程目录
已完结 75 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 从今天起,跨过“数据结构与算法”这道坎
免费
入门篇 (4讲)
01 | 为什么要学习数据结构和算法?
02 | 如何抓住重点,系统高效地学习数据结构与算法?
03 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?
04 | 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度
基础篇 (38讲)
05 | 数组:为什么很多编程语言中数组都从0开始编号?
06 | 链表(上):如何实现LRU缓存淘汰算法?
07 | 链表(下):如何轻松写出正确的链表代码?
08 | 栈:如何实现浏览器的前进和后退功能?
09 | 队列:队列在线程池等有限资源池中的应用
10 | 递归:如何用三行代码找到“最终推荐人”?
11 | 排序(上):为什么插入排序比冒泡排序更受欢迎?
12 | 排序(下):如何用快排思想在O(n)内查找第K大元素?
13 | 线性排序:如何根据年龄给100万用户数据排序?
14 | 排序优化:如何实现一个通用的、高性能的排序函数?
15 | 二分查找(上):如何用最省内存的方式实现快速查找功能?
16 | 二分查找(下):如何快速定位IP对应的省份地址?
17 | 跳表:为什么Redis一定要用跳表来实现有序集合?
18 | 散列表(上):Word文档中的单词拼写检查功能是如何实现的?
19 | 散列表(中):如何打造一个工业级水平的散列表?
20 | 散列表(下):为什么散列表和链表经常会一起使用?
21 | 哈希算法(上):如何防止数据库中的用户信息被脱库?
22 | 哈希算法(下):哈希算法在分布式系统中有哪些应用?
23 | 二叉树基础(上):什么样的二叉树适合用数组来存储?
24 | 二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树?
25 | 红黑树(上):为什么工程中都用红黑树这种二叉树?
26 | 红黑树(下):掌握这些技巧,你也可以实现一个红黑树
27 | 递归树:如何借助树来求解递归算法的时间复杂度?
28 | 堆和堆排序:为什么说堆排序没有快速排序快?
29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?
30 | 图的表示:如何存储微博、微信等社交网络中的好友关系?
31 | 深度和广度优先搜索:如何找出社交网络中的三度好友关系?
32 | 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?
33 | 字符串匹配基础(中):如何实现文本编辑器中的查找功能?
34 | 字符串匹配基础(下):如何借助BM算法轻松理解KMP算法?
35 | Trie树:如何实现搜索引擎的搜索关键词提示功能?
36 | AC自动机:如何用多模式串匹配实现敏感词过滤功能?
37 | 贪心算法:如何用贪心算法实现Huffman压缩编码?
38 | 分治算法:谈一谈大规模计算框架MapReduce中的分治思想
39 | 回溯算法:从电影《蝴蝶效应》中学习回溯算法的核心思想
40 | 初识动态规划:如何巧妙解决“双十一”购物时的凑单问题?
41 | 动态规划理论:一篇文章带你彻底搞懂最优子结构、无后效性和重复子问题
42 | 动态规划实战:如何实现搜索引擎中的拼写纠错功能?
高级篇 (9讲)
43 | 拓扑排序:如何确定代码源文件的编译依赖关系?
44 | 最短路径:地图软件是如何计算出最优出行路径的?
45 | 位图:如何实现网页爬虫中的URL去重功能?
46 | 概率统计:如何利用朴素贝叶斯算法过滤垃圾短信?
47 | 向量空间:如何实现一个简单的音乐推荐系统?
48 | B+树:MySQL数据库索引是如何实现的?
49 | 搜索:如何用A*搜索算法实现游戏中的寻路功能?
50 | 索引:如何在海量数据中快速查找某个数据?
51 | 并行算法:如何利用并行处理提高算法的执行效率?
实战篇 (5讲)
52 | 算法实战(一):剖析Redis常用数据类型对应的数据结构
53 | 算法实战(二):剖析搜索引擎背后的经典数据结构和算法
54 | 算法实战(三):剖析高性能队列Disruptor背后的数据结构和算法
55 | 算法实战(四):剖析微服务接口鉴权限流背后的数据结构和算法
56 | 算法实战(五):如何用学过的数据结构和算法实现一个短网址系统?
加餐:不定期福利 (6讲)
不定期福利第一期 | 数据结构与算法学习书单
不定期福利第二期 | 王争:羁绊前行的,不是肆虐的狂风,而是内心的迷茫
不定期福利第三期 | 测一测你的算法阶段学习成果
不定期福利第四期 | 刘超:我是怎么学习《数据结构与算法之美》的?
总结课 | 在实际开发中,如何权衡选择使用哪种数据结构和算法?
《数据结构与算法之美》学习指导手册
加餐:春节7天练 (7讲)
春节7天练 | Day 1:数组和链表
春节7天练 | Day 2:栈、队列和递归
春节7天练 | Day 3:排序和二分查找
春节7天练 | Day 4:散列表和字符串
春节7天练 | Day 5:二叉树和堆
春节7天练 | Day 6:图
春节7天练 | Day 7:贪心、分治、回溯和动态规划
加餐:用户学习故事 (2讲)
用户故事 | Jerry银银:这一年我的脑海里只有算法
用户故事 | zixuan:站在思维的高处,才有足够的视野和能力欣赏“美”
结束语 (3讲)
结束语 | 送君千里,终须一别
第2季回归 | 这一次,我们一起拿下设计模式!
打卡召集令 | 60 天攻克数据结构与算法
免费
数据结构与算法之美
登录|注册

春节7天练 | Day 6:图

王争 2019-02-10
你好,我是王争。初六好!
为了帮你巩固所学,真正掌握数据结构和算法,我整理了数据结构和算法中,必知必会的 30 个代码实现,分 7 天发布出来,供你复习巩固所用。今天是第六篇。
和之前一样,你可以花一点时间,来手写这些必知必会的代码。写完之后,你可以根据结果,回到相应章节,有针对性地进行复习。做到这些,相信你会有不一样的收获。

关于图的几个必知必会的代码实现

实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法
实现图的深度优先搜索、广度优先搜索
实现 Dijkstra 算法、A* 算法
实现拓扑排序的 Kahn 算法、DFS 算法

对应的 LeetCode 练习题(@Smallfly 整理)

Number of Islands(岛屿的个数)
Valid Sudoku(有效的数独)
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《数据结构与算法之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(18)

  • kai
    今天根据老师的课程,总结了一下图的相关知识点,然后用代码实现了一下图的相关的算法,感觉图还是要难于其他数据结构,需要接着多练习~
    2019-02-10
    2
  • 李皮皮皮皮皮
    图很复杂😢
    2019-02-10
    2
  • kai
    实现图的深度优先搜索、广度优先搜索:

    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.LinkedList;
    import java.util.Queue;

    public class BFSAndDFS {

        class Node {
            public int value; //Node 值
            public int in; //入度:指向该节点的边有几条
    public int out; //出度:指向其他节点的边有几条
    public ArrayList<Node> nexts;
    public ArrayList<Edge> edges;

    public Node(int value) {
    this.value = value;
    this.in = 0;
    this.out = 0;
    this.nexts = new ArrayList<>();
    this.edges = new ArrayList<>();
    }
    }

        public static void bfs(Node node) {
            if (node == null) {
                return;
            }

            Queue<Node> queue = new LinkedList<>();
            HashSet<Node> set = new HashSet<>();
            queue.add(node);
            set.add(node);
            while (!queue.isEmpty()) {
                Node cur = queue.poll();
                System.out.print(cur.value + " ");
                for (Node next : cur.nexts) {
                    if (!set.contains(next)) {
                        queue.add(next);
                        set.add(next);
                    }
                }
            }
        }

        public static void dfs(Node node) {
            if (node == null) {
                return;
            }

            Stack<Node> stack = new Stack<>();
            HashSet<Node> set = new HashSet<>();
            stack.push(node);
            set.add(node);
            System.out.print(node.value + " ");
            while (!stack.isEmpty()) {
                Node cur = stack.pop();
                for (Node next : cur.nexts) {
                    if (!set.contains(next)) {
                        stack.push(cur);
                        stack.push(next);
                        set.add(next);
                        System.out.print(next.value + " ");
                        break;
                    }
                }
            }
        }
    }
    2019-02-11
    1
  • 纯洁的憎恶
    1.在邻接矩阵中找出连通图个数即可。在每个顶点执行DFS或BFS,执行次数即为岛屿数,也可以使用并查集。

    2. 依次考察9✖️9数独各行各列是否有重复数字(可以用9位数组统计),然后再考察每个3✖️3子矩阵是否有重复数字。都没有则成功。
    2019-02-10
    1
  • 苦行僧
    有效的数独确实可以用暴力匹配法解决
    2019-08-26
  • Brandon
    有效数独,就是穷举遍历方法求解,跟这一节练习的图,没有什么关系啊!放这个题目的时候是怎么考虑的啊?

    作者回复: 好像确实暴力就能解决

    2019-06-27
  • Nereus

    并查集—go实现
    func numIslands(grid [][]byte) int {
    if len(grid) == 0 {
    return 0
    }

    N := len(grid)*len(grid[0]) + 1

    u := NewUnionSet(N)

    for i := 0; i < len(grid); i ++ {
    for j := 0; j < len(grid[i]); j ++ {
    if grid[i][j] == '1' {
    // 联通下边
    if i+1 < len(grid) {
    if grid[i+1][j] == '1' {
    u.join(i*len(grid[i])+j, (i+1)*len(grid[i])+j)
    }
    }

    // 联通右边
    if j+1 < len(grid[i]) {
    if grid[i][j+1] == '1' {
    u.join(i*len(grid[i])+j, i*len(grid[i])+j+1)
    }
    }
    } else {
    u.join(i*len(grid[i])+j, N-1)
    }
    }
    }

    return u.counts() -1
    }

    type UnionSet []int

    func NewUnionSet(n int) UnionSet {
    var u UnionSet
    u = make([]int, n)
    for i := 0; i < len(u); i ++ {
    u[i] = i
    }
    return u

    }

    func (u UnionSet) find(i int) int {
    tmp := i
    for u[tmp] != tmp {
    tmp = u[tmp]
    }

    j := i
    for j != tmp {
    tt := u[j]
    u[j] = tmp
    j = tt
    }

    return tmp
    }

    func (u UnionSet) connected(i, j int) bool {
    return u.find(i) == u.find(j)
    }

    func (u UnionSet) counts() int {
    var count int
    for idx, rec := range u {
    if idx == rec {
    count++
    }
    }
    return count
    }

    func (u UnionSet) join(i, j int) {
    x, y := u.find(i), u.find(j)
    if x != y {
    if y > x {
    u[x] = y
    } else {
    u[y] = x
    }
    }
    }

    编辑回复: 感谢您参与春节七天练的活动,为了表彰你在活动中的优秀表现,赠送您10元无门槛优惠券,我们会在3个工作日之内完成礼品发放,如有问题请咨询小明同学,微信geektime002。

    2019-02-14
  • 拉欧
    Valid Sudoku(有效的数独)go语言实现

    func isValidSudoku(board [][]byte) bool {

    isValid:=true
    for i:=0;i<9;i++{
    for j:=0;j<9;j++{
    if board[i][j]=='.' {
    continue
    }else{
    if !judgeLine(board,i,j){
    return false
    }
    }
    }
    }

    return isValid
    }

    func judgeLine(board [][]byte,i,j int) bool{
    hash:=make(map[byte]int,9)
    for k:=0;k<9;k++{
    if board[i][k]!='.'{
    if hash[board[i][k]]==0{
    hash[board[i][k]]=1
    }else{
    return false
    }
    }
    }
    hash=make(map[byte]int,9)
    for k:=0;k<9;k++{
    if board[k][j]!='.' {
    if hash[board[k][j]]==0{
    hash[board[k][j]]=1
    }else{
    return false
    }
    }
    }
    hash=make(map[byte]int,9)
    for m:=i/3*3;m<i/3*3+3;m++{
    for n:=j/3*3;n<j/3*3+3;n++{
    if board[m][n]!='.'{
    if hash[board[m][n]]==0{
    hash[board[m][n]]=1
    }else{
    return false
    }
    }
    }
    }
    return true

    }


    2019-02-14
  • 拉欧
    Number of Islands(岛屿的个数)go语言实现,亲测通过:
    func numIslands(grid [][]byte) int {

    isSearch:=make([][]int,len(grid))
    island:=0
    for i:=0;i<len(isSearch);i++{
    isSearch[i]=make([]int,len(grid[0]))
    }
    for i,line:=range grid{
    for j,_:=range line{
    if isSearch[i][j]==0 && grid[i][j]=='1'{
    Search(grid,isSearch,i,j)
    island++
    }

    }
    }
    return island
    }

    func Search(grid [][]byte,isSearch [][]int, i int,j int){
    if isSearch[i][j]==1{
    return
    }
    isSearch[i][j]=1
    if grid[i][j]=='1'{
    if i>=1{
    Search(grid,isSearch,i-1,j)
    }
    if i<len(grid)-1{
    Search(grid,isSearch,i+1,j)
    }
    if j>=1{
    Search(grid,isSearch,i,j-1)
    }
    if j<len(grid[0])-1{
    Search(grid,isSearch,i,j+1)
    }
    }else{
    return
    }
    }

    编辑回复: 感谢您参与春节七天练的活动,为了表彰你在活动中的优秀表现,赠送您99元专栏通用阅码,我们会在3个工作日之内完成礼品发放,如有问题请咨询小明同学,微信geektime002。

    2019-02-14
  • molybdenum
    island 我用的深搜,把所有的1探索,用visited保存访问过访问的,搜索次数便是岛屿个数
    2019-02-11
  • 小美
    岛屿数Java实现
    public int numIslands(char[][] grid) {
            int m = grid.length;
            if (m == 0) return 0;
            int n = grid[0].length;
            
            int ans = 0;
            for (int y = 0; y < m; ++y)
                for (int x = 0; x < n; ++x)
                    if (grid[y][x] == '1') {
                        ++ans;
                        dfs(grid, x, y, n, m);
                    }
            
            return ans;
        }
        
        private void dfs(char[][] grid, int x, int y, int n, int m) {
            if (x < 0 || y < 0 || x >= n || y >= m || grid[y][x] == '0')
                return;
            grid[y][x] = '0';
            dfs(grid, x + 1, y, n, m);
            dfs(grid, x - 1, y, n, m);
            dfs(grid, x, y + 1, n, m);
            dfs(grid, x, y - 1, n, m);
        }

    编辑回复: 感谢您参与春节七天练的活动,为了表彰你在活动中的优秀表现,赠送您10元无门槛优惠券,我们会在3个工作日之内完成礼品发放,如有问题请咨询小明同学,微信geektime002。

    2019-02-11
  • 黄丹
    已经初六啦,就快要到去学校的时间了,难受。
    图的邻接矩阵表示法是使用一个二维数组int[0..n-1][0...n-1]来保存顶点和边的,对于无权图,1表示有边,0表示两个顶点没有变,有权图,值代表权重。
    图的邻接表则是采用数组+链表的结构来表示的,数组里存的是顶点,链表存储的是边的信息,当然链表也可以换做二叉搜索树,散列表等高效查找的数据结构。
    今天的两道leetcode题的解题思路和代码如下:
    1. Number of Islands (岛屿的个数)
    解题思路:遍历数组,遇到1时,使用深度/广度遍历,将连通的1都置为0,然后将岛屿个数加1.
    代码:https://github.com/yyxd/leetcode/blob/master/src/leetcode/graph/Problem200_NumberofIslands.java
    2. Valid Sudoku  (有效的数独)
    解题思路:emm,不知道为什么这道题要放在图论的专题下,我的解法就是横着一行行判断,竖着一列列的判断,然后每个3*3的子块进行判断。没有用到图的知识。
    代码:https://github.com/yyxd/leetcode/blob/master/src/leetcode/graph/Problem36_ValidSudoku.java
    2019-02-10
  • 虎虎❤️
    基于临接表实现的联通分量求法, go 语言实现:
    package graph_basics

    type Components struct {
    graph Graph
    visited []bool
    id []int
    ccount int
    }

    func InitComponents(g Graph) *Components {
    return &Components{
    graph: g,
    visited: make([]bool, g.V()),
    id: make([]int, g.V()),
    ccount: 0,
    }
    }

    func (c *Components) dfs(index int) {
    c.visited[index] = true
    c.id[index] = c.ccount
    adj := c.graph.Iterator(index)
    for i := range adj {
    if !c.visited[adj[i]] {
    c.dfs(adj[i])
    }
    }
    }

    func (c *Components) CalculateComponents() {
    for i := 0; i < c.graph.V(); i++ {
    if c.visited[i] {
    continue
    }
    c.dfs(i)
    c.ccount++
    }
    }

    func (c *Components) Count() int {
    return c.ccount
    }

    func (c *Components) IsConnected(p int, q int) bool {
    return c.id[p] == c.id[q]
    }

    临接表的实现:
    package graph_basics

    import "fmt"

    type SparseGraph struct {
    v int
    e int
    direct bool
    g [][]int
    }

    func InitSparseGraph(n int, direct bool) *SparseGraph {
    graph := make([][]int, n)
    return &SparseGraph{
    v: n,
    e: 0,
    direct: direct,
    g: graph,
    }
    }

    func (sg *SparseGraph) V() int {
    return sg.v
    }

    func (sg *SparseGraph) E() int {
    return sg.e
    }

    func (sg *SparseGraph) AddEdge(p int, q int) {
    sg.g[p] = append(sg.g[p], q)
    if !sg.direct {
    sg.g[q] = append(sg.g[q], p)
    }

    sg.e++
    }

    func (sg *SparseGraph) HasEdge(p int, q int) bool {
    for i := 0; i < len(sg.g[p]); i++ {
    if sg.g[p][i] == q {
    return true
    }
    }
    return false
    }

    func (sg *SparseGraph) Show() {
    for i := range sg.g {
    fmt.Printf("vertex %d :\t", i)
    for j := range sg.g[i] {
    fmt.Printf("%d\t", sg.g[i][j])
    }
    fmt.Println()
    }
    }

    func (sg *SparseGraph) Iterator(v int) []int {
    return sg.g[v]
    }
    2019-02-10
  • 你看起来很好吃
    岛屿个数python实现(广度优先搜索算法):
    def numIslands(self, grid):
        if not grid:
            return 0
            
        count = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    self.dfs(grid, i, j)
                    count += 1
        return count

    def dfs(self, grid, i, j):
        if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j] != '1':
            return
        grid[i][j] = '#'
        self.dfs(grid, i+1, j)
        self.dfs(grid, i-1, j)
        self.dfs(grid, i, j+1)
        self.dfs(grid, i, j-1)
    2019-02-10
  • _CountingStars
    有效的数独 go 语言实现
    package main

    import (
    "fmt"
    )

    func hasRepeatedNumbers(numbers []byte) bool {
    var numbersExistFlag [9]bool
    for _, num := range numbers {
    if num == '.' {
    continue
    }
    index := num - '0' - 1
    if numbersExistFlag[index] {
    return true
    }
    numbersExistFlag[index] = true
    }
    return false
    }

    func isValidSudoku(board [][]byte) bool {
    sudokuSize := 9
    sudokuUnitSize := 3
    for _, line := range board {
    if hasRepeatedNumbers(line) {
    return false
    }
    }

    for columnIndex := 0; columnIndex < sudokuSize; columnIndex++ {
    columnNumbers := make([]byte, 0)
    for lineIndex := 0; lineIndex < sudokuSize; lineIndex++ {
    columnNumbers = append(columnNumbers, board[lineIndex][columnIndex])
    }
    if hasRepeatedNumbers(columnNumbers) {
    return false
    }
    }

    sudokuUnitCountEachLine := sudokuSize / sudokuUnitSize
    for i := 0; i < sudokuUnitCountEachLine; i++ {
    for j := 0; j < sudokuUnitCountEachLine; j++ {
    sudokuUnitNumbers := make([]byte, 0)
    for _, line := range board[i*3 : (i+1)*3] {
    sudokuUnitNumbers = append(sudokuUnitNumbers, line[j*3:(j+1)*3]...)
    }

    if hasRepeatedNumbers(sudokuUnitNumbers) {
    return false
    }
    }
    }

    return true
    }

    func main() {
    testData1 := [][]byte{
    {'5', '3', '.', '.', '7', '.', '.', '.', '.'},
    {'6', '.', '.', '1', '9', '5', '.', '.', '.'},
    {'.', '9', '8', '.', '.', '.', '.', '6', '.'},
    {'8', '.', '.', '.', '6', '.', '.', '.', '3'},
    {'4', '.', '.', '8', '.', '3', '.', '.', '1'},
    {'7', '.', '.', '.', '2', '.', '.', '.', '6'},
    {'.', '6', '.', '.', '.', '.', '2', '8', '.'},
    {'.', '.', '.', '4', '1', '9', '.', '.', '5'},
    {'.', '.', '.', '.', '8', '.', '.', '7', '9'}}
    fmt.Println(isValidSudoku(testData1))
    }

    编辑回复: 感谢您参与春节七天练的活动,为了表彰你在活动中的优秀表现,赠送您每日一课年度会员,我们会在3个工作日之内完成礼品发放,如有问题请咨询小明同学,微信geektime002。

    2019-02-10
  • island个数,从一个点从发,判断一个island的逻辑是如果本身点是water,那么必然不是island,如果是陆地,说明它能扩展成一个island,那么向上下左右进行扩展,然后再以扩展的陆地点又一直递归扩展,直到所有边界为0。而判断island的个数,就在此基础上去遍历所有点,并加上一个boolean[][]记录每个点是否已经被遍历或者扩展过。
    2019-02-10
  • C_love
    Valid Sudoku

    class Solution {
        public boolean isValidSudoku(char[][] board) {
            for (int row = 0; row < 9; row++) {
                for (int col = 0; col < 9; col++) {
                    if (board[row][col] == '.') continue;
                    if (!isValid(board, row, col)) return false;
                }
            }
            return true;
        }
        
        private boolean isValid(char[][] board, final int row, final int col){
            char target=board[row][col];
            //check rows
            for (int i = 0; i < 9; i++) {
                if (i == row) continue;
                if (board[i][col] == target) return false;
            }
            
            //check cols
            for (int i = 0; i < 9; i++) {
                if (i == col) continue;
                if (board[row][i] == target) return false;
            }
            
            //check 3*3
            int rowStart = row / 3 * 3, colStart = col / 3 * 3;
            for (int i = rowStart; i < rowStart + 3; i++) {
                for (int j = colStart; j < colStart + 3; j++) {
                    if (i == row && j == col) continue;
                    if (board[i][j] == target) return false;
                }
            }
            
            return true;
        }
    }
    2019-02-10
  • ext4
    有效的数独
    class Solution {
      public:
        bool isValidSudoku(vector< vector<char> >& board) {
          set<char> numset;
          for (int i = 0; i < 9; i++) {
            numset.clear();
            for (int j = 0; j < 9; j++) {
              char val = board[i][j];
              if (val != '.') {
                if (numset.count(val) != 0) return false;
                numset.insert(val);
              }
            }
          }
          for (int j = 0; j < 9; j++) {
            numset.clear();
            for (int i = 0; i < 9; i++) {
              char val = board[i][j];
              if (val != '.') {
                if (numset.count(val) != 0) return false;
                numset.insert(val);
              }
            }
          }
          for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
              numset.clear();
              for (int p = 0; p < 3; p++) {
                for (int q = 0; q < 3; q++) {
                  char val = board[i * 3 + p][j * 3 + q];
                  if (val != '.') {
                    if (numset.count(val) != 0) return false;
                    numset.insert(val);
                  }
                }
              }
            }
          }
          return true;
        }
    };

    编辑回复: 感谢您参与春节七天练的活动,为了表彰你在活动中的优秀表现,赠送您99元专栏通用阅码,我们会在3个工作日之内完成礼品发放,如有问题请咨询小明同学,微信geektime002。

    2019-02-10
收起评论
18
返回
顶部