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


    搜索插入位置

    问题描述:

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

  • 相关阅读:
    [][]
    Spark笔记04
    Spark笔记03
    Spark笔记02
    Spark笔记01
    【熟能生巧】使用Screw快速生成数据库文档
    记一次关于jdbcTemplate.queryForList快速Debug及感悟
    【从零单排】Exception实战总结1
    【从零单排】Java性能排查实战模拟
    【从零单排】关于泛型Generic的一些思考
  • 原文地址:https://www.cnblogs.com/dogeLife/p/11400432.html
Copyright © 2020-2023  润新知