剑指offer学习笔记(第一章整数)

剑指offer学习笔记

第一章 整数

整数的基础知识

Java中有四种不同的整数类型,分别是8位(1字节)的byte、16位(2字节)的short、32位(4字节)的int和64位(8字节)的long。
整数的范围-2^n~ 2^n-1

面试题1

整数除法
整数相除力扣
思路:当被除数大于除数时,如果大于除数的两倍,则继续判断是否大于其四倍、八倍、2^n倍,将被除数减去除数的两倍,剩余值继续重复判断是否大于除数两倍的操作,以此类推,直到假设不成立。时间复杂度O(logn)
此外,需要考虑到负数的情况,由于32位整数的范围是-2^31 到2^31-1,如果直接将负数转化为正数,会导致溢出,因此可以先将正数转化为负数

public int divide(int dividend,int divisor){
	if(dividend==0x80000000&&divisor==-1){
		return Integer.MAX_VALUE;}
	int negative=2;
	if(dividend>0){
		negative--;
		dividend=-dividend;}
	if(divisor>0){
		negative--;
		divisor=-divisor;}
	int result=divideCore(dividend,divisor);
	return result= negative==1?-result:result;
	}
private int divideCore(int dividend,int divisor){
	int result=0;
	while (dividend<divisor){
		int value=divisor;
		int quotient=1;
		while(value>=0xc0000000&&dividend<=value+value){
			quotient+=quotient;
			value+=value;
			}
		result+=quotient;
		dividend-=value;}
	return result;}

二进制

在Java中,&& 和 & 都是逻辑运算符,但它们的行为有所不同。
&& 是短路逻辑与运算符(Short-Circuit Logical AND),它在进行运算时,如果第一个操作数为假(即false),则不会对第二个操作数进行求值,直接返回结果为假;只有当第一个操作数为真(即true)时,才会继续对第二个操作数进行求值。这种情况下,&& 的运算结果可能是 true 或 false。
& 是逻辑与运算符(Logical AND),它在进行运算时,会对两个操作数都进行求值,无论第一个操作数的值是什么。& 的运算结果是两个操作数的逻辑与的结果,即如果两个操作数都为 true,则结果为 true;否则结果为 false。
另外,& 还有一个用途是进行位运算。当 & 用于整数时,它表示按位与运算符(Bitwise AND)。例如,5 & 3 的结果是 1,因为它们的二进制表示分别是 101 和 011,按位与运算结果是 001。

在 Java 中,二进制运算主要涉及以下几种类型:
位逻辑运算:包括按位与(&)按位或(|)按位异或(^)按位取反(~)等。这些运算符用于操作二进制数据的每一位,对于整数类型的数据可以直接使用。
位移运算:包括左移(<<)右移(>>)无符号右移(>>>)。这些运算符用于将二进制数据向左或向右移动指定的位数。
位检查运算:包括
位与运算(&)
、**位或运算(|)**等,用于检查二进制数据的特定位是否满足某些条件。

面试题2

二进制加法
二进制加法力扣

public String addBinary(String a,string b){
	StringBuilder result=new Stringbuilder();
	int i =a.length()-1;
	int j =b.length()-1;
	int carry=0;
	while(i>=0||j>=0){
		int digitA=i>=0?a.charAt(i--)-'0':0;
		int digitB=j>=0?b.charAt(j--)-'0':0;
		int result=digitA+digitB+carry;
		carry=result>+2?1:0;
		result=resule>=2?result-2:result;
		sum.append(result);}
	if(carry==1){
		sum.append(1);}
	return sum.reverse().toString()}
面试题3

前n个数字二进制形式中1的个数
力扣
第一种方法:外循环 循环遍历从0到num的每一个数字
内循环 对每一个i 数字 通过将其二进制形式的最右边的1变为零,每变一次 计数一次 关键是**i&(i-1)**方法。

public int[] countBits(int num){
	int [] result = new int[num+1];
	for (int i=0;i<=num;i++){
		int j=i;
		while(j!=0){
			result[i]++;
			j=j&(j-1);
			}
		}
	return result
}

第二个方法:根据第一个方法i&(i-1)能够消掉i的二进制形式中最右边的1可以总结出规律:整数i的二进制形式中1的个数比“i&(i-1)”的二进制形式中1的个数多1,那么根据这个规律可以写出如下代码:

public int[] countBits(int num){
	int[] result=new int[num+1];
	for (int i=0;i<=num;i++){
	result[i]=result[i&(i-1)]+1;
	}
	retuen result;
}

第三个方法:根据i/2的二进制形式中1的个数
根据规律:奇数i的二进制形式中1的个数比i/2中1的个数多1,偶数i的二进制形式中1的个数和i/2中1的个数一样多。
i是偶数时,i相当于将i/2左移一位的结果
i是奇数时,i相当于将i/2左移一位再将最右边的一位设置为1的结果

public int[] countBits(int num){
	int[] result=new result[num+1];
	for(int i=0;i<=num;i++){
		result[i]=result[i>>1]+(i&1);
		#这里i>>1将i右移一位 就可以计算出i/2 后面的i&1是一种巧妙地判断i的奇偶的方法 如果奇数 算式值就是1 正好加上  如果偶数 就是0 i&1也就是对变量i的二进制表示中的最低位进行逻辑与操作 
		}
	return result}
面试题4

只出现一次的数字(其他数字出现了三次)
只出现一次的数字力扣
思路就是:用一个长度为32位的整数数组, 其中有32个整数,第i个整数分别对应保存其对应数组nums中所有整数的二进制形式的第i个数位之和。由于整数是由32个0或者1组成,

public int singleNumber(int[] nums){
	int[] bitSums=new int[32];
	for int(num:nums){
		for (int i=0;i<32;i++){
			bitSums[i]+=(num>>(31-i))&1;
			#这里就是 **下标为i的整数的值 就是 整数数组nums中的整数num的第i位的值**
			#num向右移动31-i位 也就是把第i位移动到了最右边 因为i是从0开始的,由于要得到这个i位的值,移动之后仍然是一个32位二进制数字,需要得到一个值 那就和1做逻辑与运算。只保留下第i位的值,如果第i位是1 逻辑与之后就是1 如果是0 那么就是0
			
			}}
	int result=0;
	for (int i=0;i<32;i++){
		result=(result<<1)+bitSums[i]%3;
		#对结果 按每一位对3取余的结果对应加到每一位上 刚开始0 向左移动一位
		#刚开始的错误想法:bitSums=[3,0,1,0,0,1,1] i=3,result=0+3%3=0;i=0,result=0+0%3=0;i=1,result=0+1%3=1;i=0,result=10+0%3=10;i=0,result=100+0%3=0;i=1,result=1000+1%3=1001;i=1,result=10010+1=10011
		#后来意识到,bitSums=[0,0,...,0,1,1,0,0,1,0,3]
		}
	return result;}

**举一反三:**输入一个整数数组,数组中只有一个数字出现m次,其他数字出现n次,找出出现m次的数字,假设m不能被n整除
这种思路类似面试题4,如果数组中所有数字的二进制形式的第i位相加能被n整除,那么出现m次的数字的第i个数位一定是0,否则出现m次的数字的第i个数位一定是1.

面试题5

单词长度的最大乘积
力扣
要计算字符串数组中无重复字符的两个字符的长度乘积的最大值
第一种方法:用哈希表记录字符串中出现的字符
对于每个字符串,可以用一个哈希表来记录这个字符串中出现的字符,由于题目都是小写字符,则用一个长度为26的布尔型数组来模拟哈希表
数组下标为0的值表示字符a是否出现,往后就是bcd…,

public int maxProduct(String[] words){
	boolean[][]flags=new boolean[words.length][26];
	for (int i=0;i<words.length;i++){
		for(char c:words[i].toCharArray()){
			flags[i][c-'a']=true;
			#将字符c所在的对应位置的值设置为true
			}}
	int result=0;
	for(int i=0;i<words.length;i++){
		for (int j=i+1;j<words.length;j++){
			int k=0;
			for(k=0;k<26;k++){
				if(flag[i][k]&&flag[j][k]){
				#这里的&&符号表示如果前者为false 则不必计算后者  只有当两个值都是true 才是1
					break;}}
			if(k==26){
				int prod=words[i].length()*words[j].length();
				result=Math.max(result,prod);
					}}}
	return result;}

第二个方法:用整数的二进制数位记录字符串中出现的字符
将长度为26的布尔型数组用26个二进制的数位代替,0对应false 1对应true
因为一个int整数32位,26小于32 可以用
因此

public int maxProduct(String() words){
	int[]flags=new int(words.length);
	#整数数组,里面包含words.length个整数,每个整数32for (int i=0;i<words.length;i++){
		for(char ch:words[i].toCharArray()){
			flags[i]|=1<<(ch-'a');
			#这个式子很难想,就是第i个整数 按位或赋值,|= 表示按位或操作,将 flag[i] 的第 i 位与右边表达式的结果进行按位或操作,然后赋值给 flag[i]。
			#右边的1<<(ch-'a')表示将1左移ch-'a'位 然后与flag[i]一起执行按位或操作
			}}
	int result=0;
	for(i=0;i<words.length;i++){
		for(j=i+1;j<words.length;j++){
			if((flags[i]&flags[j])==0){
				#如果两个字符串不包含相同的字符,那么它们所对应的整数相与一定是0
				int prod=words[i].length()*words[j].length();
				result=Math.max(result,prod);}}
				
		}
	return result;}
  • 40
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值