二分查找位运算——32位整数中寻找第一个为1的位

问题描述


在程序设计中经常会遇到这样一个问题,即在一个32位整数中,从右到左寻找第一个为1的位。这样的问题是很常见的,而面对这样一个问题,一种常见的解法就是逐位的遍历这个整数中的所有位,直到遇到了为1的位。若第一个为1的位出现在高位区域,逐位查找比较的方法会比较浪费 时间。


换一种思路


当我们在一个数组中寻找某数的时候,我们有什么办法?简单的顺序的遍历整个数组的思想是可行的,这样的时间复杂度是o(n)。若数组是一个有序的数组呢?我们一般会采用2分查找的方式在一个数组中寻找某数,这样找起来就比较快了,时间复杂度是o(logn)。那在一个32位的int中寻找第一个为1的数,能否使用2分查找法呢?

在查找第一个为1的位时是可以使用2分查找法的,那么要如何使用2分法呢?关于位相关的操作一定不要忘了位运算,有效的运用位运算是可以实现对该问题的2分查找法。

究竟如何进行2分查找呢? 

判断某一位是否为1时可以使用&操作,那么要判断32位中的16位是否有1时要如何做到呢?用data & 0xFFFF即可。要判断8位是否有1时可以data & 0xFF,判断4位是否有1可以data & 0xF,那么判断2位是否有1就可以用data & 0x3了。

分析到这里,就已经有思路了,通过data与 0xFFFF,0xFF,0xF以及 0x3等做&操作即可。
从左到右

int binarysearch(int data)
{
	int pos=0;
	if ((data & 0xFFFF0000) == 0)
		pos += 16;
	else
		data >>= 16;
	
	if ((data & 0xFF00) == 0)
		pos += 8;
	else
		data >>= 8;
	
	if ((data & 0xF0) == 0)
		pos += 4;
	else
		data >>= 4; 

	if ((data & 0xC) == 0)		// 0b1100
		pos += 2;
	else
		data >>= 2; 

	if ((data & 0x2) == 0)		// 0b10
		pos += 1;

	return pos;
}


从右到左

int binarysearch(int data)
{
        int pos=0;
        if ((data & 0x0000FFFF) == 0){
                data >>= 16;
                pos += 16;
        }

        if ((data & 0x00FF) == 0){
                data >>= 8;
                pos += 8;
        }

        if ((data & 0x0F) == 0){
                data >>= 4;
                pos += 4;
        }

        if ((data & 0x3) == 0){         // 0b1100
                data >>= 2;
                pos += 2;
        }

        if ((data & 0x1) == 0)          // 0b10
                pos += 1;

        return pos;
}


小结

位运算在C语言中是非常重要的,很多有技巧性的优化都是通过位运算来实现,需要熟练掌握才行。

当然针对特定的处理器还可以做些指令级的优化,比如在x86上记得有一条指令就可以完成这个功能了。

(从网友博客转帖过来,原来代码有误这里做了些修改)



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
1. 算法的基本概念 利用计算机算法为计算机解题的过程实际上是在实施某种算法。 (1)算法的基本特征 算法一般具有4个基本特征:可行性、确定性、有穷性、拥有足够的情报。 (2)算法的基本运算和操作 算法的基本运算和操作包括:算术运算、逻辑运算、关系运算、据传输。 (3)算法的3种基本控制结构 算法的3种基本控制结构是:顺序结构、选择结构、循环结构。 (4)算法基本设计方法 算法基本设计方法:列举法、归纳法、递推、递归、减半递推技术、回溯法。 (5)指令系统 所谓指令系统指的是一个计算机系统能执行的所有指令的集合。 (2)据结构研究的3个方面 ① 据集合据元素之间所固有的逻辑关系,即据的逻辑结构; ② 在对据进行处理时,各据元素在计算机的存储关系,即据的存储结构; ③ 对各种据结构进行的运算。 2. 逻辑结构 据的逻辑结构是对据元素之间的逻辑关系的描述,它可以用一个据元素的集合和定义在此集合的若干关系来表示。据的逻辑结构有两个要素:一是据元素的集合,通常记为D;二是D上的关系,它反映了据元素之间的前后件关系,通常记为R。一个据结构可以表示成:B=(D,R) 其,B表示据结构。为了反映D据元素之间的前后件关系,一般用二元组来表示。 例如,如果把一年四季看作一个据结构,则可表示成:B =(D,R) D ={春季,夏季,秋季,冬季} R ={(春季,夏季),(夏季,秋季),(秋季,冬季)} 3. 存储结构 据的逻辑结构在计算机存储空间的存放形式称为据的存储结构(也称据的物理结构)。 由于据元素在计算机存储空间位置关系可能与逻辑关系不同,因此,为了表示存放在计算机存储空间的各据元素之间的逻辑关系(即前后件关系),在据的存储结构,不仅要存放各据元素的信息,还需要存放各据元素之间的前后件关系的信息。 一种据的逻辑结构根据需要可以表示成多种存储结构,常用的存储结构有顺序、链接等存储结构。 顺序存储方式主要用于线性的据结构,它把逻辑上相邻的据元素存储在物理上相邻的存储单元里,结点之间的关系由存储单元的邻接关系来体现。 链式存储结构就是在每个结点至少包含一个指针域,用指针来体现据元素之间逻辑上的联系。 1.2.2 线性结构和非线性结构 根据据结构据元素之间前后件关系的复杂程度,一般将据结构分为两大类型:线性结构与非线性结构。 (1)如果一个非空的据结构满足下列两个条件: ① 有且只有一个根结点; ② 每一个结点最多有一个前件,也最多有一个后件。 则称该据结构为线性结构。线性结构又称线性表。在一个线性结构插入或删除任何一个结点后还应是线性结构。栈、队列、串等都为线性结构。 如果一个据结构不是线性结构,则称之为非线性结构。组、广义表、树和图等据结构都是非线性结构。 (2)线性表的顺序存储结构具有以下两个基本特点: ① 线性表所有元素所占的存储空间是连续的; ② 线性表据元素在存储空间是按逻辑顺序依次存放的。 元素ai的存储地址为:ADR(ai)=ADR(a1)+(i-1)k,ADR(a1)为第一个元素的地址,k代表每个元素占的字节。 (3)顺序表的运算有查找、插入、删除3种。 1.3 栈 1. 栈的基本概念 栈(stack)是一种特殊的线性表,是限定只在一端进行插入与删除的线性表。 在栈,一端是封闭的,既不允许进行插入元素,也不允许删除元素;另一端是开口的,允许插入和删除元素。通常称插入、删除的这一端为栈顶,另一端为栈底。当表没有元素时称为空栈。栈顶元素总是最后被插入的元素,从而也是最先被删除的元素;栈底元素总是最先被插入的元素,从而也是最后才能被删除的元素。 栈是按照“先进后出”或“后进先出”的原则组织据的。例如,枪械的子弹匣就可以用来形象的表示栈结构。子弹匣的一端是完全封闭的,最后被压入弹匣的子弹总是最先被弹出,而最先被压入的子弹最后才能被弹出。 二级公共基础知识速学教程 2. 栈的顺序存储及其运算 栈的基本运算有3种:入栈、退栈与读栈顶元素。 ① 入栈运算:在栈顶位置插入一个新元素; ② 退栈运算:取出栈顶元素并赋给一个指定的变量; ③ 读栈顶元素:将栈顶元素赋给一个指定的变量。 1.4 队列 1. 队列的基本概念 队列是只允许在一端进行删除,在另一端进行插入的顺序表,通常将允许删除的这一端称为队头,允许插入的这一端称为队尾。当表没有元素时称为空队列。 队列的修改是依照先进先出的原则进行的,因此队列也称为先进先出的线性表,或者后进后出的线性表。例如:火车进遂道,最先进遂道的是火车头,最后是火车尾,而火车出遂道的时候也是火车头先出,最后出的是火车尾。若有队列: Q =(q1,q2,…,qn) 那么,q1为队头元素(排头
很好用的东西很经典的一本C教程,TKS这算是谭浩强C语言设计比较新的版本了!目录很详细,使用很方便目录 第1章 C语言程序设计的概念  1.1 程序与程序设计语言   1.1.1 计算机与程序   1.1.2 计算机程序设计语言   1.1.3 高级语言程序的开发过程  1.2 C语言及其标准   1.2.1 C语言的出现   1.2.2 C语言的标准  1.3 C语言程序概要   1.3.1 函   1.3.2 语句   1.3.3 名字与声明   1.3.4 变量及其赋值   1.3.5 算术运算   1.3.6 赋值类运算符的副作用及限制  习题一 第2章 基本据类型  2.1 基本据类型的特征   2.1.1 值的定点表示与浮点表示   2.1.2 整数的有符号类型与无符号类型   2.1.3 类型宽度与取值范围  2.2 据常量   2.2.1 整型常量   2.2.2 字符类型及其常量   2.2.3 实型常量   2.2.4 符号常量  2.3 据类型转换   2.3.1 几个概念   2.3.2 据类型的隐式转换   2.3.3 据类型的显式转换   2.4 据的控制台输入与输出   2.4.1 格式化输出函pIintf()   2.4.2 格式化输入函scanf()   2.4.3 字符输入/输出函getchar()与putchar()  习题二 第3章 C语言程序的流程控制  3.1 算法   3.1.1 算法的组成要素与基本性质   3.1.2 算法描述工具   3.1.3 自项向下、逐步细化的算法设计过程  3.2 判断   3.2.1 命题的“真”、“假”与C语言的逻辑值   3.2.2 关系运算与关系表达式   3.2.3 逻辑运算与逻辑表达式  3.3 选择型程序设计   3.3.1 if...else结构的应用   3.3.2 if.elseif结构的应用   3.3.3 switch结构的应用   3.3.4 条件表达式  3.4 循环型程序设计   3.4.1 迭代与穷举算法   3.4.2 while结构   3.4.3 dowhile结构   3.4.4 for结构   3.4.5 循环结构的途退出与重复周期的途结束  习题三 第4章 模块化程序设计  4.1 函   4.1.1 设计C语言程序就是设计函   4.1.2 函结构   4.1.3 函定义与函声明   4.1.4 虚实结合与传值调用   4.1.5 递归函  4.2 变量的存储属性   4.2.1 变量的作用域与生存期   4.2.2 C语言变量的存储类型   4.2.3 通过const声明将变量存储在只读区  4.3 模块的编译与链接   4.3.1 分别编译   4.3.2 用项目管理多文件程序的编译与链接过程   4.3.3 头文件  4.4 宏定义与宏替换   4.4.1 字符串宏定义及其基本格式   4.4.2 使用宏需注意的问题   4.4.3 撤销己定义的宏   4.4.4 带参的宏定义  习题四 第5章 组  5.1 一维组   5.1.1 一维组定义及组元素引用   5.1.2 组元素的引用方法   5.1.3 一维组的初始化   5.1.4 一维组元素的查找与排序   5.1.5 组与函  5.2 字符串   5.2.1 字符组与字符串   5.2.2 字符串的输入/输出   5.2.3 字符串处理函  5.3 二维组与多维组   5.3.1 二维组及其定义   5.3.2 二维组的初始化   5.3.3 向函传送二维组   5.3.4 多维组  习题五 第6章 指针  6.1 指针基础   6.1.1 地址与指针   6.1.2 指针变量及其定义   6.1.3指针变量的引用   6.1.4 指针的移动与比较   6.1.5 指向指针变量的指针与多级指针   6.1.6 指向void类型的指针  6.2 指针与组   6.2.1 组元素的指针引用   6.2.2 多字符串的存储与处理   6.2.3 内存的动态分配与动态组的建立  6.3 指针与函   6.3.1 指针参与函的地址传送调用   6.3.2 带参的主函   6.3.3 返回指针值的函   6.3.4 指向函的指针  习题六 第7章 用户定制据类型  7.1 结构体类型基础   7.1.1 结构体类型及其定制   7.1.2 定义结构体类型变量及对变量的初始化   7.1.3 结构体变量的操作   7.1.4 嵌套结构体类型   7.1.5 段  7.2 结构体组   7.2.1 结构体组的定义与初始化   7.2.2 对结构体

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值