搜索插入位置与右移运算符

搜索插入位置

问题描述:

  给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。比如数组 [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 补齐

posted @ 2019-08-23 15:43  LimLee  阅读(295)  评论(0编辑  收藏  举报