给定一个正数,输出它的“补数”。求的方法是把二进制位置的数进行取反。

注意:

  1. 给定的正数在32位内
  2. 正数前面没有补0。比如2(B10),在它的前面没有0。

例子1:

输入: 5
输出: 2
解释: 5的二进制数是101 (前面不补充0), 它的补数是010。所以输出是2。

例子2:

输入: 1
输出: 0
解释: 1的二进制是1 (前面不补充0), 它的补数是0。因此输出是0。

思路

假设输入数为input, 输出为output, 设mask的二进制位数与input相等,且每一位都为1。
这里求input有两种方法。这里你可以用上面的例子数据代进去验证一下。

  1. output = input ^ mask
  2. output = mask - input

所以这个问题的关键是如何得到mask。

还有第三种思路是这样的。每次将input分别与其对应二进制位为1的数进行异或操作,就能得到output。

1
2
3
4
5
6
7
class Solution(object):
    def findComplement(self, num):
        i = 1
        while num >= i:
            num ^= i
            i <<= 1
        return num

计算mask方法一:

1
2
3
4
5
6
var flipMask: UInt32 = ~0
let numUInt32 = UInt32(num)
while ((numUInt32 & flipMask) != 0) {
    flipMask <<= 1
}
let mask = ~flipMask;

将全是’1’的UInt32数不断的左移,直到(numUInt32 & flipMask) == 0停止,然后再对flipMask取反就能得到要求的mask

计算mask方法二:

1
2
3
4
5
6
7
int mask = 0;
int j = 0;
while (mask < num)
{
  mask += Math.pow(2, j);
  j++;
}

从右往左不断的增加1的位数,直到mask >= num

计算mask方法三:

1
2
3
4
5
6
int mask = num;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;

每次与右移后的值进行与操作,移动的位数每次能扩大2倍。这样就能保证mask的所有二进制位都是1。

计算mask方法四:

1
mask = ((2<<int(math.log(num, 2)))-1)

利用数学函数能得到input的位数(int(math.log(num, 2))+1), 这样进1位减去1就得到了mask值。

给定两个数字,求这两个数的二进制数相应位不同的总数。

注意:
$0 \leq x, y < 2^{31}$

例子:

输入: x=1, y=4
输出: 2

解释:
1 (0 0 0 1)
4 (0 1 0 0)
^ ^
上面箭头指的位置相应两个数字的二进制位不一样,一共有2处。

##解法1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// javascript
/**
 * @param {number} x
 * @param {number} y
 * @return {number}
 */
var hammingDistance = function(x, y) {
    let xor = x ^ y;
    let count = 0;
    while(xor !== 0) {
        count += xor & 1;
        xor = xor>>1;
    }
    return count;
};

首先通过异或操作得到一个值xor。
在循环里面判断有几个不一样的位置。
从最后一位开始判断,直到所有位置判断完成。

##解法2:

1
2
3
4
// javascript
var hammingDistance = function(x, y) {
    return (x ^ y).toString(2).replace(/0/g, '').length;
};

第一步同解法一。
第二步转换成操作字符串的操作。
删除’0’,随后判断字符串的长度,即不同位置数。

##解法3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//javascript
/**
 * @param {number} x
 * @param {number} y
 * @return {number}
 */
var hammingDistance = function(x, y) {
    let xor = x ^ y;
    let count = 0;
    while(xor) {
        count++;
        xor &= xor-1;
    }
    return count;
};

其实这个问题可以简化成找出二进制数中有几个‘1’。比如异或结果是‘1010’,其中有2个‘1’表示hamming distance是2。

这段代码while循环可能一眼看上去发现自己懵逼了。其实xor &= xor-1;这个操作是移除一个’1‘。有几个就是移除几次。如果还懵逼,可以多调试几次。

给定一组数和一个目标值,求出两个加数的位置。

比如:

1
2
3
nums = [2, 7, 11, 15], target = 9
因为nums[0] + nums[1] = 2 + 7 = 9,
返回 [0, 1]

注意返回的位置是基于0的

##方法一 O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    for (var i=0; i<nums.length; i++) {
        for (var j=i+1; j<nums.length; j++) {
            if ((nums[i] + nums[j]) == target) {
                return [i, j];
            }
        }
    }
};

##方法二 O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let map = new Map();
    for (var i=0; i<nums.length; i++) {
        let nextNum = target - nums[i]
        
        if (map[nextNum] !== undefined) {
            let result = []
            result.push(map[nextNum])
            result.push(i)
            return result
        }
        
        map[nums[i]] = i
    }
};

##总结
方法二巧妙利用hashMap取值的时间是固定的这一特点。把复杂度从O(n^2) 降到了O(n)。

从学习iOS开发就开始接触到MVC的开发模式了。随着越来越注重用户体验以及业务越来越复杂。ViewController也越来越臃肿,同时我们的工作量也越来越大。在ViewController中混杂着业务逻辑与UI处理的逻辑。完整的测试ViewController既要对UI进行测试也需要对业务逻辑进行测试。测试起来比较麻烦。

物极必反 器满则倾

接下来进入正题,看看MVVM设计模式是如何解决这些问题的。

MVVM = Model + View + ViewModel

看上去和MVC差不多,就是把ViewController替换成了ViewModel。如果是这样的话,MVVM压根就没有存在的必要了。所以并不是进行了简单的替换。MVVM由3部分组成:Model , View , ViewModel。

  • Model: 作为数据的容器
  • View: 负责界面的展示以及用户交互的处理
  • ViewModel: 负责业务逻辑处理

##iOS中的MVVM

然并卵iOS中我们没有办法绕过UIViewController。诸如UITabController, UINavigationController是非常非常常用的。没有这些容器,我们写界面时的情景简直不敢想象。IB与UIViewController的结合比较紧密。如果你通过IB来画界面的话,更离不开UIViewController。那么iOS中的MVVM是什么样的?

图:捂脸

  • Model: 作为数据的容器
  • View: 负责界面的展示以及用户交互的处理
  • ViewController: 用代码创建视图;胶水代码,连接View和ViewModel
  • ViewModel: 负责业务逻辑处理

Model,View,ViewModel的职责都没有改变。iOS的MVVM开发模式中ViewController只做与视图有关的操作,以及连接View与ViewModel的胶水代码。也就是说View部分其实是由原本的View+部分ViewController组成。

一旦我们这样组织代码,首先进行业务逻辑测试的时候我们只需要测试ViewModel就可以。测试UI只需要测试View+ViewController。对于UI的自动化测试我一直没有找到什么特别好的办法,可能主要靠手工和一些第三方的测试平台吧。其次我们在开发过程中可以进行并行开发了,即可以同时开发界面和业务逻辑。即使你是一个人开发App,那么每一次只做一件事情同样会让你的头脑更清楚。

图:开心

##胶水代码

iOS中没有胶水代码的MVVM是不完整的

胶水代码都做了哪些事情?

  1. 将控件的事件传递给ViewModel,使得ViewModel其有机会处理用户交互。
  2. 将ViewModel的内容与View进行绑定,使得View有机会显示正确的内容。

在ReactiveCocoa出来之前,胶水代码很难写的很优雅。这也是为什么以前iOS中MVVM并不怎么火的原因。关于ReactiveCocoa的学习与使用本文不会涉及。如果有需要可以自行搜索学习。它是响应式开发的利器。

iOS的MVVM开发模式不能没有”ReactiveCocoa”

没有胶水代码,你不能将业务逻辑从ViewController中抽离。

也谈谈MVVM(二)将会进行实战演练。结合一个简单的Demo进行讲解MVVM开发模式。敬请期待!另外欢迎大家能和我一起讨论交流,一起进步。

未完待续…

图:三只企鹅

我们公司在南京招聘iOS的时候,人真难找啊。而我也一直有想开设一个培训班的想法。乘着现在不想写代码。写一篇iOS学习路线图的文章。同时也决定出一系列的学习教程。

iOS开发的人相对于以前来说已经慢慢多起来了。但是不管从质量还是数量上来说都明显感觉不够。大学里面应该很少有iOS开发课程的。iOS开发前期投入比较高——你得先买一台苹果电脑才能开始学习iOS开发。这些限制使得iOS程序员数量跟不上企业的需求。由于物以稀为贵,这也使得iOS程序员的工资普遍也还不错。

言归正传,上学的时候我看了不少学习方法的书。其中无非就是以下几点:

  1. 学习新知识
  2. 总结新知识
  3. 重复的练习
  4. 融汇总结知识

这个系列的教材我将按照这些学习规律进行设计。本系类教材内容主要是帮助零基础的同学进入iOS开发的领域,并能通过它找到一份能让你养家糊口的工作。教材的前期会快速的带大家进入iOS开发。以便在你学习热情还没有消散的情况下让你快速入门。后面会逐渐补充上对大家以后有帮助的知识。换句话说,前面部分是让你会走,而后面部分是让你走的更远。同时在大家能做项目的时候,我也会附上我工作中的一些经历与感受。希望能让大家在工作上少走点弯路。

啰嗦了这么多,接下来看看教程的列表。

阶段一:学习语言

零基础的人首先可以快速的学习C语言,从而了解什么是编程语言。如何使用编程语言与计算机进行交流,并指导其完成机械的任务。学习完这一阶段之后我们可以使用编程语言来完成一些简单的任务。

  1. C语言
  2. Swift语言
阅读全文 »

iOS App在打开的时候一般会显示一张启动图片。这样用户会觉得程序打开速度和响应速度很快。启动图片只有一个用途就是让用户觉得程序响应很快,除此之外没有其他作用。

启动图片的内容

启动图片能帮助我们提高App的用户体验,接下来我们看看启动图片应该是什么样子的。
在启动图片中,我们不应该用来提供这些内容:

  1. 一些进入的效果,如一个被溅射的屏幕
  2. 关于窗口
  3. 一些打上烙印的元素。(除非这些元素出现在即将出现的第一个程序界面。)
阅读全文 »

常常想把自己看的书记录下来,一直想下一次记,然后就没有然后了。现在先记下来再读吧。:D

  1. 《Swift Development with Cocoa》2014/12/24 - 2015/2/12
  2. 《程序员修炼之道》2015/4/15 - 2015/5/6

  1. 登录到 iPhone Developer Connection Portal(http://developer.apple.com/iphone/manage/overview/index.action )并点击 App IDs

  2. 创建一个不使用通配符的 App ID 。通配符 ID 不能用于推送通知服务。例如, com.itotem.iphone

  3. 点击App ID旁的“Configure”,然后按下按钮生产 推送通知许可证。根据“向导” 的步骤生成一个签名并上传,最后下载生成的许可证。

阅读全文 »

for(NSString *familyName in [UIFont familyNames]){
    NSLog(@"Font FamilyName = %@",familyName); //*输出字体族科名字

    for(NSString *fontName in [UIFont fontNamesForFamilyName:familyName]){
        NSLog(@"\t%@",fontName);         //*输出字体族科下字样名字
    }
}

最近想开始折腾Swift了。所以整理整理学习笔记。不得不说Swift确实比Objective-C简洁啊。在OC中Closure已经用的比较多了,每次声明的时候都是长长一串。现在终于不用那么麻烦了。不废话了,先来看看Closure的语法吧。

Closure表达式语法

1
2
3
{ (parameters) -> return type in
    statements
}

简单吧。由于Swift编译器可以推断出变量的类型,所以我们有更简单的写法。

阅读全文 »
0%