什么是大小端
大端模式
高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放。
小端模式
高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
单个进程每秒请求服务器的成功次数QPS = req/sec = 请求数/秒
QPS统计方式 [一般使用 http_load 进行统计]QPS = 总请求数 / ( 进程总数 * 请求时间 )
Thread#run
方法是不抛出任何检查型异常(Checked Exception),但是它自身却可能因为一个异常而被终止,导致这个线程的终结。
最麻烦的是,在线程中抛出的异常即使在主线程中使用try...catch
也无法截获,因此可能导致一些问题出现,比如异常的时候无法回收一些系统资源,或者没有关闭当前的连接等等。
主线程之所以不处理子线程抛出的RuntimeException
,是因为线程是异步的,子线程没结束,主线程可能已经结束了。UncaughtExceptionHandler
名字意味着处理未捕获的异常。更明确的说,它处理未捕获的运行时异常。
序列化:将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
调用writeObject()
序列化一个对象,是将其写入磁盘,以后在程序再次调用readObject()
时,根据wirteObject()
磁盘的文件重新恢复那个对象。Externalizable
接口扩展Serializable
,并增添两个方法:writeExternal()
和readExternal()
。在序列化和重新装配的过程中,会自动调用这两个方法。Externalizable
是Serializable
接口的子类,有时不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()
和readExternal()
可以指定序列化哪些属性。
算法一般会按步骤做如下事情:
目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等。
跳表是一种随机化的数据结构,目前开源软件Redis和LevelDB都有用到它,它的效率和红黑树以及AVL树不相上下,但跳表的原理相当简单,只要你能熟练操作链表,就能轻松实现一个 SkipList。
这是跳表的作者,上面介绍的William Pugh给出的解释。
Skip lists are a data structure that can be used in place of balanced trees. Skip lists use probabilistic balancing rather than strictly enforced balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees.
跳表是平衡树的一种替代的数据结构,但是和红黑树不相同的是,跳表对于树的平衡的实现是基于一种随机化的算法的,这样也就是说跳表的插入和删除的工作是比较简单的。
从该有序表中搜索元素“23,43,59”,需要比较的次数分别为<2, 4, 6>,总共比较的次数为2 + 4 + 6 = 12
次。有没有优化的算法吗? 链表是有序的,但不能使用二分查找。类似二叉搜索树,把一些节点提取出来,作为索引。得到如下结构。
把“14,34,50,72”提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构。
如果是说链表是排序的,并且节点中还存储指向前面第二个节点的指针的话,那么在查找一个节点时,仅仅需要遍历N/2
个节点即可。
这基本上就是跳表的核心思想,其实也是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。
是度量算法执行的时间长短,时间复杂度是指他运行时计算的次数。
一般情况下,算法的基本操作重复执行的次数是模块n的某一个函数f(n)
,因此,算法的时间复杂度记做:T(n)=O(f(n))
随着模块n的增大,算法执行的时间的增长率和f(n)
的增长率成正比,所以f(n)
越小,算法的时间复杂度越低,算法的效率越高。
在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出T(n)
的同数量级(它的同数量级有以下:1,$log_{2}n$,n,$nlog_{2}n$,n的平方,n的三次方,2的n次方),找出后,f(n) = 该数量级
,若T(n)/f(n)
求极限可得到一常数c,则时间复杂度T(n) = O(f(n)
。
按数量级递增排列,常见的时间复杂度有:常数阶O(1)
,对数阶O(log2n),线性阶O(n)
,线性对数阶O(nlog2n),平方阶O($n^2$),立方阶O($n^3$),…,k次方阶O($n^k$),指数阶O($2^n$)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
是度量算法所需存储空间的大小,空间复杂度是指运行完一个程序所需内存的大小。在算法中所谓的空间复杂度为O(1)
并不是说只使用一个空间,而是说在待处理的数据量变化的情况下使用的空间量是不变的。空间复杂度一般算的是辅助空间的大小,如果这个辅助空间的大小不随元素N变化那么就是O(1)
。
例如:冒泡排序,需要的辅助空间是一个作交换用的临时变量,而且无论任何情况下都只要这一个,所以空间复杂度就是O(1)
。但桶排序就不同了,本身需要M个桶,桶的数量是人为决定的,然后需要N个结点,去装原本的N个元素,所以空间复杂度就是O(M+N)
。
一个程序的空间复杂度是指运行完一个程序所需内存的大小。利用程序的空间复杂度,可以对程序的运行所需要的内存多少有个预先估计。一个程序执行时除了需要存储空间和存储本身所使用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和存储一些为现实计算所需信息的辅助空间。
程序执行时所需存储空间包括以下两部分。
固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。
可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。
一个算法所需的存储空间用f(n)
表示:S(n)=O(f(n))
,其中n为问题的规模,S(n)
表示空间复杂度。
1 | for(i=1; i<=n; ++i) |
则有T(n) = n
的平方+n的三次方,根据上面括号里的同数量级,我们可以确定n的三次方为T(n)
的同数量级,则有f(n) = n
的三次方,然后根据T(n)/f(n)
求极限可得到常数c,则该算法的时间复杂度:T(n) = O($n^3$) 。注:$n^3$即是n的3次方。
1 | int x = 1;//时间复杂度为O(1) |
1 | int x = 1; |
1 | int n = 8, count = 0;; |
1 | int n = 8, count = 0;; |
1 | int n = 8, count = 0;; |
一个临时变量temp,所以空间复杂度为O(1)1
2
3
4int x=1, y=2;
int temp = x;
x = y;
y = temp;
Update your browser to view this website correctly. Update my browser now