搜索插入位置与右移运算符
搜索插入位置
问题描述:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。比如数组 [1, 3, 5, 6],目标值 2,由于 2 不在数组内,所以返回 2 本应该在的索引即 1 。
利用二分查找可以实现,具体解法如下:
1 public static int binarySearch0(int[] a, int fromIndex, int toIndex, int key) {
2 int low = fromIndex;
3 int high = toIndex - 1;
4
5 while (low <= high) {
6 // 学习一个新知识,>>>
7 int mid = (low + high) >>> 1;
8 int midVal = a[mid];
9
10 if (midVal < key)
11 low = mid + 1;
12 else if (midVal > key)
13 high = mid - 1;
14 else
15 return mid; // key found
16 }
17 // return -(low + 1); // key not found. java 源码中的返回值: -(插入点)-1
18 return low;
19 }
右移运算符 >>> 与 >>
我们在代码看到了 >>> 运算符,之前只用过 >> 运算符,>> 运算符简单来说就是将数的二进制位右移指定位数,去除给定位数的最右边的二进制位,空出来的高位用 0 补齐。那么 >>> 又是什么呢?通过查阅资料,我们发现, >>> 与 >> 都是右移运算符,他们的区别如下:
>> singed right shift operator。 即有符号数的右移运算符,在对数的二进制位进行右移的时候会考虑数的符号位,符号位不参加移动
>>> unsinged right shift operator, 即非负数的右移运算符,在对数的二进制位进行右移的时候不考虑数的符号位,符号位也参加移动
让我们先来看看无符号数的右移动
1 int a = 60;
2 int b = -60;
3 int c;
4
5 System.out.println("60 = " + Integer.toBinaryString(a));
6 System.out.println("-60 = " + Integer.toBinaryString(b));
7
8 //signed shift
9 c = a >> 1;
10 System.out.println("60 >> 1 = " + Integer.toBinaryString(c));
11
12 c = a >> 2;
13 System.out.println("60 >> 2 = " + Integer.toBinaryString(c));
14
15 //unsigned shift
16 c = a >>> 1;
17 System.out.println("60 >>> 1 = " + Integer.toBinaryString(c) );
18
19 c = a >>> 2;
20 System.out.println("60 >>> 2 = " + Integer.toBinaryString(c) );
输出结果为:
1 60 = 111100 2 -60 = 11111111111111111111111111000100 3 60 >> 1 = 11110 4 60 >> 2 = 1111 5 60 >>> 1 = 11110 6 60 >>> 2 = 1111
可以看到,对于无符号数来说,用 >> 和 >>> 进行右移其实没什么区别,因为符号位为 0,用有符号数的右移运算符 >> 移动,符号位不跟着移动,当从符号位 0 后的被右移后空缺的位置用 0 填充。用 >>> 进行右移动,符号位也跟着移动了,所有的空缺的高位也都用 0 填充。
再来看看有符号数的 >> 和 >>> 右移
同样使用上面的变量 b
1 c = b >> 1;
2 System.out.println("-60 >> 1 = " + Integer.toBinaryString(c) );
3 // 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 32位
4 c = b >> 2;
5 System.out.println("-60 >> 2 = " + Integer.toBinaryString(c) );
6
7 c = b >> 3;
8 System.out.println("-60 >> 3 = " + Integer.toBinaryString(c) );
9
10 c = b >>> 1;
11 System.out.println("-60 >>> 1 = " + Integer.toBinaryString(c));
12 // 1111111111111111111111111100010 31位
13
14 c = b >>> 2;
15 System.out.println("-60 >>> 2 = " + Integer.toBinaryString(c));
16 // 1111111111111111111111111100010 30位
17
18 c = b >>> 3;
19 System.out.println("-60 >>> 3 = " + Integer.toBinaryString(c));
20 // 1111111111111111111111111100010 29位
输出为:
1 -60 >> 1 = 11111111111111111111111111100010 2 -60 >> 2 = 11111111111111111111111111110001 3 -60 >> 3 = 11111111111111111111111111111000 4 -60 >>> 1 = 1111111111111111111111111100010 5 -60 >>> 2 = 111111111111111111111111110001 6 -60 >>> 3 = 11111111111111111111111111000
这里需要注意下,对于有符号数,用有符号数右移运算符进行右移,符号位 1 不会跟着移动,符号位 1 后们被右移动之后所空缺的位置全部会用 1 来填补,看一下 - 60 分别被有符号右移动 1,2,3 位的情况。就明白了,再看看用无符号运算符进行右移,由于不考虑符号位,二进制位的所有有效位全部会跟着右移动,在左边的空位通通用 0 填补。
位移的周期性
32 位整形在进行移动的时候,会以32为周期。比如移动 1 位与移动 33 位 的效果一样。移动 -1 位会与移动 31 位的效果是一样的,比如:
1 System.out.println("-1 = " + Integer.toBinaryString(-1));
2
3 System.out.println("-1>>>32 = " + Integer.toBinaryString(-1>>>32));
4 System.out.println("-1>>>0 = " + Integer.toBinaryString(-1>>>0));
5
6 System.out.println("-1>>>1 = " + Integer.toBinaryString(-1>>>1));
7 System.out.println("-1>>>33 = " + Integer.toBinaryString(-1>>>33));
8
9 System.out.println("-1>>>-1 = " + Integer.toBinaryString(-1>>>-1));
10 System.out.println("-1>>>31 = " + Integer.toBinaryString(-1>>>31));
11
12 System.out.println("1<<1 = " + Integer.toBinaryString(1<<-1));
13 System.out.println("1<<33 = " + Integer.toBinaryString(1<<31));
输出为:
1 -1 = 11111111111111111111111111111111 2 -1>>>32 = 11111111111111111111111111111111 3 -1>>>0 = 11111111111111111111111111111111 4 -1>>>1 = 1111111111111111111111111111111 5 -1>>>33 = 1111111111111111111111111111111 6 -1>>>-1 = 1 7 -1>>>31 = 1 8 1<<1 = 10000000000000000000000000000000 9 1<<33 = 10000000000000000000000000000000
总结
一般来说我们遇到的大部分情况都是移动无符号数,即非负数,一般也是用的有符号运算符 >> ,而且用 >> 与 >>> 的效果一致。
对于有符号数,用 >> 和 >>> 进行右移会有根本性的不同,用 >> 进行右移,会考虑符号位 1 , 符号位 1 不跟着移动,空缺位用 1 补齐;用 >>> 进行移动,不需要考虑符号位,所以位一起右移,空缺高位用 0 补齐。