iOS-算法集锦-剑指offer-百题详解之二

阅前需知

1.本文部分内容参考剑指 offer 题解,如有侵权,请告知。其他内容均属原创,转载请告知。
2.本文示例代码中给一些类增加了一些类扩展,因篇幅原因,未在文中写出,详情见项目源码,地址文末有提供。
3.阅读本文之前需要先了解节点,链表,,二叉树的实现。详情见如下文章连接。
4.因为 OC 中没有栈,链表节点,链表的概念,所以本项目自定义了栈,链表节点,链表类。
5.因技术水平有限,如有错误,欢迎指正。

以下是通过 OC 语法实现

11.旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。

例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。

解题思路

在一个有序数组中查找一个元素可以用二分查找,二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度都为 O(logN)。

本题可以修改二分查找算法进行求解:

  • 当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m;
  • 否则解在 [m + 1, h] 之间,令 l = m + 1。
详细代码如下
/** 折半查找最小值 */
+ (int)minNumberInRotateArray:(NSArray *)nums {
    if (nums.count == 0) {
        return 0;
    }
    int l = 0, h = (int)nums.count - 1;
    while (l < h) {
        int m = l + (h - l) / 2;    // 中间值
        if ([nums[m] intValue] <= [nums[h] intValue]) {
            h = m;
        } else {
            l = m + 1;
        }
    }
    
    return [nums[l] intValue];
}
测试案例代码
// 11.1 折半查询
- (void)minNumberInRotateArray {
    int number = [MinNumberInRotateArray minNumberInRotateArray:@[@1,@4,@8,@11,@20]];
    NSLog(@"number = %d",number);
}
运行结果
11.1折半查找.png
11.2 重复数字

如果数组元素允许重复的话,那么就会出现一个特殊的情况:nums[l] == nums[m] == nums[h],那么此时无法确定解在哪个区间,需要切换到顺序查找。例如对于数组 {1,1,1,0,1},l、m 和 h 指向的数都为 1,此时无法知道最小数字 0 在哪个区间。

详细代码如下
+ (int)minNumberInRotateArray2:(NSArray *)nums {
    if (nums.count == 0) {
        return 0;
    }
    int l = 0, h = (int)nums.count - 1;
    while (l < h) {
        int m = l + (h - l) / 2;    // 中间值
        if (nums[l] == nums[m] && nums[m] == nums[h]) { // 三者相等
            return [self minNumber:nums l:l h:h];
        } else if ([nums[m] intValue] <= [nums[h] intValue]) {
            h = m;
        } else {
            l = m + 1;
        }
    }
    
    return [nums[l] intValue];
}

+ (int)minNumber:(NSArray *)nums l:(int)l h:(int)h {
    for (int i = l; i < h; i++) {
        if ([nums[i] intValue] > [nums[i + 1] intValue]) {
            return [nums[i + 1] intValue];
        }
    }
    return [nums[l] intValue];
}
测试案例代码
// 11.2 折半查询
- (void)minNumberInRotateArray2 {
    int number = [MinNumberInRotateArray minNumberInRotateArray2:@[@3,@4,@2,@1,@3]];
    NSLog(@"number = %d",number);
}
运行结果
image.png

12.矩阵中的路径

题目描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。

例如下面的矩阵包含了一条 bfce 路径。

路径图.png
详细代码如下
  • MatrixHasPath.h
/**
 矩阵中的路径
 */
@interface MatrixHasPath : NSObject

- (BOOL)hasPath:(NSString *)oriStr rows:(int)rows cols:(int)cols str:(NSString *)str;

@end
  • MatrixHasPath.m
@implementation MatrixHasPath {
    NSArray *_next;
    int _rows;
    int _cols;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        // 上 下 左 右 四个方向
        _next = @[@[@0,@(-1)],@[@0,@1],@[@(-1),@0],@[@1,@0]];
    }
    return self;
}

- (BOOL)hasPath:(NSString *)oriStr rows:(int)rows cols:(int)cols str:(NSString *)str {
    if (rows == 0 || cols == 0) {
        return NO;
    }
    _rows = rows;
    _cols = cols;
    
    // 1.先构造一个二维数组,数值都填充为0,表示没有遍历过
    NSMutableArray *marked = [NSMutableArray array];
    for (int i = 0; i < _rows; i++) {
        NSMutableArray *firstArrM = [NSMutableArray array];
        for (int j = 0; j < _cols; j++) {
            [firstArrM addObject:@0];
        }
        [marked addObject:firstArrM];
    }
    
    // 2.构造一个矩阵 array-完整路径的数组,即a,b,t,g,c...h,并且是一个 rows * cols 的矩阵
    NSMutableArray *oriStrs = [NSMutableArray array];
    for (int i = 0; i < oriStr.length; i++) {
        [oriStrs addObject:[oriStr substringWithRange:NSMakeRange(i, 1)]];
    }
    NSMutableArray *matrix = [self buildMatrix:oriStrs.copy];
    
    // 一条完整路径的数组
    NSMutableArray *strs = [NSMutableArray array];
    for (int i = 0; i < str.length; i++) {
        [strs addObject:[str substringWithRange:NSMakeRange(i, 1)]];
    }
    
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if ([self backetracking:matrix strs:strs marked:marked pathLen:0 r:i c:j]) {
                return YES;
            }
        }
    }
    
    return NO;
}

- (bool)backetracking:(NSArray *)matrix strs:(NSArray *)strs marked:(NSArray *)marked pathLen:(int)pathLen r:(int)r c:(int)c {
    // 0.即路径长度和字符串长度相等
    if (pathLen == strs.count) {
        NSLog(@"marked = %@",marked);
        return YES;
    }
//    r < 0 || r >= _rows || c < 0 || c >= _cols || ![oriPathStr isEqualToString:tarPathStr] || marked[r][c]
    if (r < 0 || r >= _rows || c < 0 || c >= _cols || matrix[r][c] != strs[pathLen] || [marked[r][c] intValue]) {
        // 原路径
        return NO;
    }
    marked[r][c] = @1;
    for (NSArray *next in _next) {
        int next0 = [(NSNumber *)(next[0]) intValue];
        int next1 = [(NSNumber *)(next[1]) intValue];
        if ([self backetracking:matrix strs:strs marked:marked pathLen:pathLen + 1 r:(r + next0) c:(c + next1)]) {
            return YES;
        }
    }
    marked[r][c] = @0;
    return NO;
}

/**
 构造一个矩阵 array-完整路径的数组,即a,b,t,g,c...h,并且是一个 rows * cols 的矩阵
 a  b   t   g
 c  f   c   s
 j  d   e   h
 */
- (NSMutableArray *)buildMatrix:(NSArray *)array {
    // 0.判断是否有值
    if (_rows == 0 || _cols == 0) {
        return nil;
    }
    if (array.count < _rows * _cols) {
        return nil;
    }
    
    // 1.先构造一个二维数组
    NSMutableArray *secondArrM = [NSMutableArray array];
    for (int i = 0; i < _rows; i++) {
        NSMutableArray *firstArrM = [NSMutableArray array];
        for (int j = 0; j < _cols; j++) {
            [firstArrM addObject:@""];
        }
        [secondArrM addObject:firstArrM];
    }
    
    // 2.依次将值填充进数组中
    int idx = 0;
    for (int i = 0; i < _rows; i++) {
        for (int j = 0; j < _cols; j++) {
            secondArrM[i][j] = array[idx++];
        }
    }
    
    return secondArrM;
}
@end
测试案例代码
// 12.矩阵中的路径
- (void)matrixHasPath {
    MatrixHasPath *hasPath = [[MatrixHasPath alloc] init];
    NSString *pathStr = @"abtgcfcsjdeh";
    
    NSMutableArray *tagPaths = [NSMutableArray array];
    [tagPaths addObject:@"bfce"];
    [tagPaths addObject:@"bfceh"];
    [tagPaths addObject:@"acfde"];
    [tagPaths addObject:@"bcjd"];
    [tagPaths addObject:@"abfceh"];
    [tagPaths addObject:@"abtch"];
    [tagPaths addObject:@"abtsh"];
    
    for (int i = 0; i < tagPaths.count; i++) {
        NSString *tagPath = [tagPaths objectAtIndex:i];
        bool result = [hasPath hasPath:pathStr rows:3 cols:4 str:tagPath];
        NSLog(@"i = %d, path = %@, result = %d",i,tagPath, result);
        NSLog(@"----------------------------------------");
    }
}
运行结果
12.路径覆盖图.png

13.机器人的运动范围

题目描述

地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。

例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,37),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?

解题思路

参考12题解思路

详细代码如下
  • MovingCount_13.h
@interface MovingCount_13 : NSObject

- (int)movintCount:(int)threshold rows:(int)rows cols:(int)cols;

@end
  • MovingCount_13.m
@implementation MovingCount_13 {
    NSArray *_next;
    int _count;
    int _rows;
    int _cols;
    int _threshold; // 行坐标和列坐标的数位之和小于等于于_threshold
    NSMutableArray *_digitSum;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _next = @[@[@0,@(-1)],@[@0,@1],@[@(-1),@0],@[@1,@0]];
    }
    return self;
}

- (int)movintCount:(int)threshold rows:(int)rows cols:(int)cols {
    _rows = rows;
    _cols = cols;
    _threshold = threshold;
    [self initDigitSum];
    
    NSMutableArray *marked = [NSMutableArray array];
    for (int i = 0; i < _rows; i++) {
        NSMutableArray *firstArrM = [NSMutableArray array];
        for (int j = 0; j < _cols; j++) {
            [firstArrM addObject:@0];
        }
        [marked addObject:firstArrM];
    }
    [self dfs:marked r:0 c:0];
    return _count;
}

- (void)dfs:(NSMutableArray *)marked r:(int)r c:(int)c {
    if (r < 0 || r >= _rows || c < 0 || c >= _cols || [marked[r][c] intValue]) {
        return;
    }
    marked[r][c] = @1;
    if ([_digitSum[r][c] intValue] > _threshold) {
        return;
    }
    _count++;
    for (NSArray *next in _next) {
        int next0 = [(NSNumber *)(next[0]) intValue];
        int next1 = [(NSNumber *)(next[1]) intValue];
        [self dfs:marked r:r + next0 c:c + next1];
    }
}

- (void)initDigitSum {
    int maxNumber = MAX(_rows, _cols);
    NSMutableArray *numbers = [NSMutableArray array];
    for (int i = 0; i < maxNumber; i++) {
        [numbers addObject:@0];
    }
    
    for (int i = 0; i < numbers.count; i++) {
        int n = i;
        while (n > 0) {
            numbers[i] = @([numbers[i] intValue] + n % 10);
            n /= 10;
        }
    }
    
    // 初始化原始数组
    if (_digitSum == nil) {
        _digitSum = [NSMutableArray array];
        for (int i = 0; i < _rows; i++) {
            NSMutableArray *firstArrM = [NSMutableArray array];
            for (int j = 0; j < _cols; j++) {
                [firstArrM addObject:@0];
            }
            [_digitSum addObject:firstArrM];
        }
    }
    
    // 赋值
    for (int i = 0; i < _rows; i++) {
        for (int j = 0; j < _cols; j++) {
            _digitSum[i][j] = @([numbers[i] intValue] + [numbers[j] intValue]);
        }
    }
}

@end
测试案例代码
// 13.机器人的运动范围
- (void)movingCount {
    MovingCount_13 *movingCount = [[MovingCount_13 alloc] init];
    int count = [movingCount movintCount:18 rows:10 cols:10];
    NSLog(@"count = %d",count);
}
运行结果
13.机器人的运动范围.png

15.二进制中 1 的个数

题目描述

输入一个整数,输出该数二进制表示中 1 的个数。

解题思路

n&(n-1)
该位运算去除 n 的位级表示中最低的那一位。

n       : 10110100
n-1     : 10110011
n&(n-1) : 10110000
详细代码如下
/** 输入一个整数,输出该数二进制表示中 1 的个数。*/
+ (int)numberOfOne:(int)number {
    int cnt = 0;
    while (number != 0) {
        cnt++;
        number &= (number - 1);
    }
    return cnt;
}
测试案例代码
// 15.二进制中 1 的个数
- (void)numberOfOne {
    for (int i = 0; i < 20; i++) {
        NSLog(@"i = %d, decimal = %@, count = %d",i,[[NSString stringWithFormat:@"%d",i] convertBinarySystemFromDecimalSystem],[NumberOfOne_15 numberOfOne:i]);
    }
}
  • 十进制数转二进制数 (将其写到NSString 的分类中)
/** 十进制数转二进制数 */
- (NSString *)convertBinarySystemFromDecimalSystem {
    NSInteger num = [self intValue];
    NSInteger remainder = 0;      //余数
    NSInteger divisor = 0;        //除数
    
    NSString * prepare = @"";
    
    while (true){
        remainder = num % 2;
        divisor = num / 2;
        num = divisor;
        prepare = [prepare stringByAppendingFormat:@"%ld",remainder];
        
        if (divisor == 0){
            break;
        }
    }
    
    NSString * result = @"";
    
    for (NSInteger i = prepare.length - 1; i >= 0; i --){
        result = [result stringByAppendingFormat:@"%@",[prepare substringWithRange:NSMakeRange(i , 1)]];
    }
    
    // 补齐8位显示
    while (result.length < 8) {
        result = [NSString stringWithFormat:@"0%@",result];
    }
    
    return result;
}
运行结果
15.二进制中1的个数.png

16.数值的整数次方

题目描述

给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent,求 base 的 exponent 次方。

解题思路

下面的讨论中 x 代表 base,n 代表 exponent。

image.png

因为 (x*x)n/2 可以通过递归求解,并且每次递归 n 都减小一半,因此整个算法的时间复杂度为 O(logN)。

详细代码如下
/** 数值的整数次方 */
+ (double)power:(double)base exponent:(int)exponent {
    if (exponent == 0) {
        return 1;
    }
    if (exponent == 1) {
        return base;
    }
    bool isNegative = NO;
    if (exponent < 0) {
        exponent -= exponent;
        isNegative = YES;
    }
    double pow = [self power:base * base exponent:exponent / 2];
    if (exponent % 2 != 0) {
        pow = pow * base;
    }
    return isNegative ? 1 / pow : pow;
}
测试案例代码
// 16.数值的整数次方
- (void)power {
    for (int i = 0; i < 10; i++) {
        NSLog(@"base = 2, exponent = %d, result = %f",i,[power_16 power:2 exponent:i]);
    }
}
运行结果
16.数值的整数次方.png

18.在 O(1) 时间内删除链表节点

题目描述

删除链表节点

解题思路

1.如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。

image.png

2.否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。

image.png

综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N ~ 2,因此该算法的平均时间复杂度为 O(1)。

详细代码如下
/** 删除节点 */
- (void)deleteNode {
    NSMutableArray *numbers = [NSMutableArray array];
    for (int i = 0; i < 10; i++) {
        [numbers addObject:@(i)];
    }
    LinkedArray *linkArray = [[LinkedArray alloc] initLiknedArrayWithNunbers:numbers];
    ListNode *headNode = [linkArray getFirstListNode];
    ListNode *lastDelNode = [linkArray getLastListNode];
    NSLog(@"---------------原始链表数据---------------");
    [headNode printAllListNode];
    
    ListNode *newHeadNode = [self deleteNode:headNode tagDelNode:lastDelNode];
    NSLog(@"---------------删除后链表数据---------------");
    [newHeadNode printAllListNode];
}

- (ListNode *)deleteNode:(ListNode *)headNode tagDelNode:(ListNode *)tagDelNode {
    // 头节点和要删除的节点为空
    if (headNode == nil || tagDelNode == nil) {
        return nil;
    }
    
    if (tagDelNode.next != nil) {   // 要删除的节点不是尾节点
        ListNode *next = tagDelNode.next;
        tagDelNode.content = next.content;
        tagDelNode.next = next.next;
    } else {
        ListNode *cur = headNode;
        while (cur.next != tagDelNode) {
            cur = cur.next;
        }
        cur.next = nil;
    }
    return headNode;
}
测试案例代码
// 18.在 O(1) 时间内删除链表节点
- (void)deleteNode {
    DeleteNode_18 *deleteNode = [[DeleteNode_18 alloc] init];
    [deleteNode deleteNode];
}
运行结果
18.1删除链表节点.png

18.2 删除链表中重复的结点

题目描述
image.png
解题思路

参考18.1的解题思路

详细代码如下
/** 删除 重复节点 */
- (void)deleteDuplicationNode {
    NSMutableArray *numbers = [NSMutableArray array];
    for (int i = 0; i < 10; i++) {
        if (i / 2 == 1) {
            [numbers addObject:@(2)];
        } else if (i / 3 == 1) {
            [numbers addObject:@(3)];
        } else {
            [numbers addObject:@(i)];
        }
    }
    LinkedArray *linkArray = [[LinkedArray alloc] initLiknedArrayWithNunbers:numbers];
    NSLog(@"----------------原始链表数据----------------");
    [linkArray printAllListNode];
    ListNode *firstNode = [linkArray getFirstListNode];
    ListNode *headNode = [self deleteDuplicationListNode:firstNode];
    NSLog(@"----------------删除重复节点后的链表数据----------------");
    [headNode printAllListNode];
}

// 删除重复节点
- (ListNode *)deleteDuplicationListNode:(ListNode *)pHeadNode {
    if (pHeadNode == nil || pHeadNode.next == nil) {
        return pHeadNode;
    }
    
    ListNode *next = pHeadNode.next;
    if (pHeadNode.value == next.value) {
        while (next != nil && pHeadNode.value == next.value) {
            next = next.next;
        }
        return [self deleteDuplicationListNode:next];
    } else {
        pHeadNode.next = [self deleteDuplicationListNode:pHeadNode.next];
        return pHeadNode;
    }
}
测试案例代码
// 18.2 删除链表中重复的结点
- (void)deleteDuplication {
    DeleteNode_18 *deleteNode = [[DeleteNode_18 alloc] init];
    [deleteNode deleteDuplicationNode];
}
运行结果
18.2.删除重复节点.png

本文参考CS_Nodes 剑指 offer 题解.md 非常感谢该作者


相关知识点文章参考链接地址

  • 更多OC算法详解文章请持续关注作者,本人会在接下来的时间陆续整理。

如有错误,欢迎指正,多多点赞,打赏更佳,您的支持是我写作的动力。


项目连接地址 - ArithmeticDemo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 目录 1. 前言 2. 实现 Singleton 3. 数组中重复的数字 4. 二维数组中的查找 5. 替换空格 ...
    路飞_Luck阅读 2,014评论 4 22
  • 前言 2. 实现 Singleton 3. 数组中重复的数字 4. 二维数组中的查找 5. 替换空格 6. 从尾到...
    Observer_____阅读 2,909评论 0 1
  • 算法思想贪心思想双指针排序快速选择堆排序桶排序荷兰国旗问题二分查找搜索BFSDFSBacktracking分治动态...
    第六象限阅读 3,051评论 0 0
  • 像我这样的人,总想着,大城市有一席之地。 但是,自身的差距又望尘莫及, 说起来,要经商吧,又没有这样的胆气。 懂得...
    星星变奏曲阅读 258评论 0 0
  • 第一轮21天运动打卡游戏结束后,大家意犹未尽,期待接下来进行第二期。 那么好吧,我们继续! 在第一轮的基础上对游戏...
    齐言楚羽阅读 4,362评论 6 2