高中大家都学过区间——闭区间、开区间、左闭右开区间、左开右闭区间。
区间在Java里也有体现。比如用解析字符串子串的形式来提取身份证的信息,提取子串就相当于截取一个区间。
用一门语言,用的久应该会对它的一些特点比较熟知。随着我对Java的学习,慢慢发现很多Java的区间都是左闭右开的,即取左不取右(当然了,取索引的话是离散的数值)。
举个例子,比如长度14的字符串,你取的是(13,14),得到第13索引即第14位(索引从0开始)。但是14并不越界,因为根本取不到!
在jshell里输入String a = "abcdefghijklmn";
,显示a ==> "abcdefghijklmn"
,再输入a.substring(2, 3);
,显示$2 ==> "c"
,再输入a.substring(2, 4);
,显示$3 ==> "cd"
。
其实不仅仅是Java,在C语言、Python、Swift等很多流行的编程语言中都有类似的体现(只不过并不是所有的时候都是而已,所谓左闭右开只能说是比较普遍的规律吧)
那为什么左闭右开受到青睐呢?
我查了一些资料,加上自己浅薄的感悟,略作分享。
先举一些例子,直观地感受一下吧:
先说开区间吧,开区间真的不是很直观。明明是索引1,非得是(0,2);一旦是索引0,就需要面临(-1,1)的窘境,连负数都出来了。
闭区间呢,一旦遇到单一索引,就无法处理,比如只有索引1,那就无法表示,莫非存在[1,1]的表示吗?显然不是很合理。
那么,半开半闭显得比较合理。
左开右闭的话遇到了和开区间第二个问题类似的情况,表示0的时候需要(-1,0],这就很奇怪,所以相对来讲我认为左闭右开显得比较合理和优雅。
下面比较系统的总结一下:
- 上下界之差等于元素的数量
- 易于表示两个相邻子序列,一个子序列的上界就是另一个子序列的下界
- 序列从零(最小自然数)开始计数时,下界的下标不是 -1(非自然数)
- 表达空集时,不会使得上界小于下界
这是4个比较明确的解释,对于一个学过编程的人来说不难理解。
另有一些从数学和算法上做的解释如下:
- 左闭右开区间划分的子区间,也符合左闭右开的性质,是同构的
像二分查找、快速排序这样的算法,都是分治算法,不断地划分区间的时候,左闭右开能保持父子区间形式的统一 - 按比例划分子区间后,映射到边界节点上的概率也是成比例的
js、java中,产生随机数函数 Math.random() 返回值是[0,1)间的实数,就有这样的优点 - 全闭区间要处理边界情况,有特殊点,对程序设计和算法理解造成障碍
划分整数时,无论怎么分,总有一个区间和其他不同,划分偏左或偏右一个元素,划分是不整齐的。要打各种边界处理补丁来弥补
在实数范围来进行计算,就用闭区间就不能实现了
当然了,如果用久了,习惯了以后也就很自然了。
大概就说这么多,希望对读者有帮助。