前言
题解/思路整理
1. 判断链表中是否有环
- 哈希法
- 双指针
2. 最小栈
- 辅助栈
3. 求最大公约数
- 辗转相除 取模运算性能较差
- 更相减损 避免了取模运算,但是算法性能不稳定
- 更相减损术与移位相结合 避免了取模运算,而且算法性能稳定
若a、b均为偶数:gcd(a, b) = 2*gcd(a/2, b/2) = 2*gcd(a>>1, b>>1);
若a为偶数,b为奇数:gcd(a, b) = gcd(a/2, b) = 2*gcd(a>>1, b);
若a、b均为奇数:gcd(a, b) = 2*gcd(a/2, b/2) = 2*gcd(a>>1, b>>1);
移位运算:
左移时,按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方;例如:
3 << 2
0000 0000 0000 0000 0000 0000 0000 0011 ->
0000 0000 0000 0000 0000 0000 0000 1100
3 << 2 == 12
右移时,按二进制形式把所有的数字向右移动对应位移位数,低位移出(舍弃),高位的空位补符号位,即正数补零,负数补1。右移一位相当于除2,右移n位相当于除以2的n次方。例如:
11 >> 2
0000 0000 0000 0000 0000 0000 0000 1011 ->
0000 0000 0000 0000 0000 0000 0000 0010
11 >> 2 == 2
判断整数奇偶性的方式是让整数和 1 进行与运算,如果 (a & 1) == 0,则说明整数 a 是偶数;如果 (a & 1) != 0,则说明整数 a 是奇数。
4. 判断一个数是否为2的整数次幂
2的整数次幂减去1,其二进制数字均为1,此时使用原数值和其减1的结果进行按位与运算,即a&(a-1),结果均为0.若结果不为0,则该数不是2的整数次幂。
public static boolean isPowerOf2(int num){
return (num & num-1) == 0
}
5. 求无序整形数组排序后的最大相邻差
- 计数排序:求出区间长度k和偏移量d,根据这两个量创建新数组,遍历原数组,每遍历一个元素,就将新数组对应下标的值+1。最后遍历新数组,统计最大连续出现0值的次数并加1,就是相邻元素最大差值。
- 桶排序:根据原数组创建桶,桶的个数等于原数组元素个数,所有桶均分原数组的取值区间;遍历原数组来确定每个桶内的最大值和最小值;遍历所有的桶,每一个桶的最小值和这个桶右侧非空桶的最小值的差即为所求。
注意:桶排序时无序比较桶内元素的相邻差(为什么?)
6. 使用栈实现队列
- 辅助栈 A栈负责入队,B栈负责出队。
入队时,等于A栈入栈操作,不操作B栈。
出队时,等于B栈出栈操作,如果B栈为空,则首先从A栈按顺序出栈,同时将这些元素按顺序压入栈B,此时B栈中元素的顺序与A栈原元素顺序是相反的,出队即B出栈直到为空,为空时重复上述操作。
7. 寻找包含一个正整数所有数字全排列的下一个数
字典序算法
- 从后向前查找逆序区域,找到逆序区域的前一位,即数字置换的边界。
- 让该置换边界与逆序区域中大于该边界数的最小数字交换位置。
- 将原逆序区域转为顺序状态。
8. 删除K个数字后的最小值
贪心算法:局部最优解,但整体未必最优。
思路:无论删除哪一个数字,结果都是将整数的位数降低,显然应该优先把高位的数字降低,这样对新整数的值的影响最大。
算法分为两步:
- 从左到右遍历,找到比自己右侧大的数字并删除
- 其他情况的处理:如果没有找到要删除的数字则删除最后一位数字;如果左侧数字出现了0,则清楚整数左侧的0;如果整数的所有数字都被删除了,则直接返回0.
在遍历原整数的数字时,让所有数字一个一个入栈,当某个数字需要删除时,让该数字出栈。最后,程序把栈中的元素转化为字符串类型的结果。
9. 超大整数的相加
建立临时数组,模拟竖式运算
- 创建两个整形数组,数组长度是较大整数的位数+1(给最高位进位预留空间),将两个整数分别倒叙存储到两个数组中。
- 创建结果数组,数组长度是较大整数的位数+1。
- 遍历两个数组,从左到右元素两两相加,进位时将进位的1填充到下一个位置。
- 逆序打印结果数组(或者逆序字符拼接)
- 优化: 按位数来拆分并建立数组,数组的长度可能过大。int类型取值范围为 -2 147 483 648 ~ 2 147 483 647,位数最多为10.我们可以按每9位拆分为数组的一个元素,来进行加法运算。这样占用空间和运算次数都被压缩到原来的1/9。
10. 金矿问题
动态规划:将全局问题简化为多个子结构,子结构简化成更小的子结构,为了避免重复调用,自底向上求解。
动态规划的要点:确定全局最优解和最优子结构之间的关系以及问题的边界。
11. 寻找缺失的整数
有一个范围1100的整型无序数组,其中有99个不重复的整数,缺少1个1100中的整数。请找出这个缺失的整数。
哈希法:创建一个哈希表,以1到100这些100个整数为所有的key,遍历原数组,同步的定位到哈希表对应的key同时删除该key。最终剩下的key即为缺失的整数。
排序,查找不连续
数组求和,与1+2+...+100的结果比较
问题拓展1:一个无序数组里有若干个正整数,范围是 1~100,其中 99 个整数都出现了偶数次,只有 11 个整数出现了奇数次,请找到这个出现奇数次的整数。
异或运算:在进行位运算时,相同位得0,不同位得1。遍历整个数组并做一个异或运算,所有出现偶数次的整数都会抵消为0,唯一出现奇数次的整数会被留下。
问题拓展2:一个无序数组里有若干个正整数,范围是 1~100,其中 98 个整数都出现了偶数次,只有 2 个整数出现了奇数次,请找到这2个出现奇数次的整数。
分治法:将问题分解为规模更小的子问题,将这些规模更小的子问题逐个击破,合并已解决的子问题,最终得到原“母”问题的解。将原数组遍历做异或运算后,最终结果至少有一个二进制位是1,假设是倒数第3位,则可以把原数组分为两部分:一部分数组中所有元素的倒数第3二进制位为0,另一部分所有元素倒数第3二进制位为1,这两个奇数将分别位于这两部分数组中。