还记得Jon Bentley关于二分查找的那句著名言论吗?————90%以上的程序员无法正确无误的写出二分查找代码!几年前刚毕业的时候无意间看到这句话的时候,还有点不以为然,感觉就是一个普通的分治法的算法嘛,有这么夸张?
今天在看SparseArray源码的时候,碰巧发现一个设计的很巧妙的二分查找算法。先看代码:
和最常用的采用尾递归实现二分查找的方式不同,改成了循环。第5行寻找中间位置的时候采用高低位相加然后无符号右移1位,这里就比较与众不同了。对于一般的程序员来说,最普遍的写法就是:
或者
还记得Jon Bentley关于二分查找的那句著名言论吗?————90%以上的程序员无法正确无误的写出二分查找代码!几年前刚毕业的时候无意间看到这句话的时候,还有点不以为然,感觉就是一个普通的分治法的算法嘛,有这么夸张?
今天在看SparseArray源码的时候,碰巧发现一个设计的很巧妙的二分查找算法。先看代码:
和最常用的采用尾递归实现二分查找的方式不同,改成了循环。第5行寻找中间位置的时候采用高低位相加然后无符号右移1位,这里就比较与众不同了。对于一般的程序员来说,最普遍的写法就是:
或者
快速排序是一个非常快的排序算法,有些公司在面试基础的时候经常问到,平均情况下只有O(n log n)的时间复杂度,使得被广泛的使用在计算机科学和工程实践中。
快速排序使用经典的“分治法”策略,把一个排序分解成若干个小的排序,每个小的排序完成了,那么整个大的排序认为也就完成了。
简单来说,快速排序分为3个步骤:
1,随机选取一个基准数
2,以第一步选取的基准数位基准,把要排序的数分成两份,使左边的都小于基准数,右边的都大于基准数,和基准数相等的数可以在左右两个数组中的任意一个里
3,分别对第二步生成两份数组重复1-2步骤
维基百科上的这幅动图很形象地展示了一次快速排序的执行过程:
定义两个下标i和j,i从0的位置开始向右扫描,j从length()-1的数组队尾位置向前扫描。先从i开始,直到找到一个数比基准数大或相等,否则i++,找到后记录i的下标,然后移动j,直到找到一个数比基准数小或相等。两个数都找到后,如果此时i<=j,交换数组中i和j下标处对应的值。交换完之后i+1,j-1,然后i继续向后扫描,j继续向前扫描,当i>j时,终止。
执行完这一趟之后,会使i位置以前的数都小于或等于基准数,i位置以后的数都大于或等于基准数。
这里有一个细节,同时找到i和j之后才去交换二者,而不是像有的算法实现那样,找到i,交换基准,找到j之后再交换基准,减少了数组交换的次数和开销。
举个简单的例子,把数组{1, 12, 5, 26, 7, 14, 3, 7, 2}用快速排序进行排序。
有了上面的分析,我们很容易写出相应的代码,下面是java版的实现:
目前的客户端大都有和服务端进行交互,而数据的格式基本就是json了,于是在Android开发中就经常用到json解析,方便的是Google已经为我们提供了一个很棒的json解析库–gson,那么今天就来总结分享下gson的各种用法。
gson的官方下载地址:google-gson
首先我们来看一个最简单的用法,假设json的数据格式是这样的:
|
|
那么我们只需要定义对应的一个类:
|
|
使用起来只需如下几行代码就行了:
单例模式应该是最简单、最容易理解的设计模式了。即保证一个类只有一个实例,并提供一个访问它的全局访问点。单例模式在各种开源项目中经常见到,但是其中涉及到的知识点并不少,所以经常拿来当面试题来考。本文主要梳理一下常见单例模式的写法,以及其优缺点。
|
|
这是单例模式最常见的写法了,首先构造方法私有化,防止外部直接new一个该类的实例,然后提供一个全局的访问入口方法,在该方法中检查、实例化该类。采用懒汉式,需要用到该类实例的时候才会new。但是这种实现最大的问题是不支持多线程,因为getInstance()方法没有加锁,所以当多个线程调用getInstance()方法时就会产生多个实例。
为了解决上面的问题,最简单的方法是将整个getInstance()方法设为同步(synchronized)。
|
|
这种写法虽然做到了线程安全,并且解决了多实例的问题,能够在多线程中很好的工作,但是它并不高效。因为在任何时候只能有一个线程调用 getInstance() 方法。但是同步操作只需要在第一次创建单例实例对象时才被需要,创建之后getInstance()只是简单的返回成员变量,而这里是无需同步的。 由于同步一个方法会降低100倍或更高的性能,代价太高。 每次调用获取和释放锁的开销似乎是可以避免的:一旦初始化完成,获取和释放锁就显得很不必要。这就引出了双重检验锁。