数字处理
翻转数字
atoi
范围数字的与和
整数除法
丑数
pow算法:
格雷编码
是否是3的幂次方
众数
超过 ⌊ n/3 ⌋ 次的众数
所有因子
两个恰好出现一次的数
重复三次数字
灯泡开关
计算1的数目
超级丑树
范围数字的与和:
给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。
示例 1:
输入: [5,7]
输出: 4
示例 2:
输入: [0,1]
输出: 0
此题其实就是寻找[m,n]范围内二进制数高位(左边)没有变化的数,后面补上0即为所求的结果。
判断m、n是否相等,如果不相等,m+1会使m的二进制数末位进位,有进位说明m的末位肯定有0的情况,0与任何数相与皆得0,所以结果的末位肯定是0。同理,不断右移1位进行比较,直到最终 m=n 时,说明找到了[m,n]这个范围内高位没有变化的数,左移相同位数得到的结果就是所求的值。
class Solution {
public int rangeBitwiseAnd(int m, int n) {
int count = 0; // 统计移位次数
while (m != n)
{
m >>= 1;
n >>= 1;
count++;
}
n <<= count;
return n;
}
}
翻转数字:
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
解析:难点在于越界值得判断
举个例子。如果说允许的最大值是500的话,那么输入数字是125,逆置为521即越界。所以在
52*10之前进行一次判断。
class Solution {
public int reverse(int x) {
int result=0,pop=0;
while(x!=0){
pop=x%10;
x=x/10;
if(result>Integer.MAX_VALUE/10||result== Integer.MAX_VALUE/10&&pop> Integer.MAX_VALUE%10){return 0;}
if(result<Integer.MIN_VALUE/10||result== Integer.MIN_VALUE/10&&pop< Integer.MIN_VALUE%10){return 0;}
result=result*10+pop;
}
return result;
}
}
atoi:
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: “42”
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。
示例 4:
输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
解析:
1:首先需要删除前后空格
2:判断第一个字符是否数字或者符号
3:之后读取后置所有数字,遇到非数字终止
4:读取过程中判断数字大小是否越界
class Solution {
public int myAtoi(String str) {
if(str.length()==0)return 0;
str=str.trim();//去除前后空格
char c[]=str.toCharArray();
if((c[0]<'0'||c[0]>'9')&&c[0]!='-'&&c[0]!='+'){
return 0;
}
int i=0;int flag=1,result=0;//正负标志
if(c[i]=='-'){i++;flag=-1;}
else if(c[i]=='+'){i++;}
while(i<c.length&&c[i]>='0'&&c[i]<='9'){
// String str1=String.valueOf(c[i]);
// int num=Integer.parseInt(str1);
int num=c[i]-'0';
if(flag==1&&(result>Integer.MAX_VALUE/10||(result== Integer.MAX_VALUE/10&&num>Integer.MAX_VALUE%10))){return Integer.MAX_VALUE;}
if(flag==-1&&(-1*result<Integer.MIN_VALUE/10||(-1*result== Integer.MIN_VALUE/10&&-1*num<Integer.MIN_VALUE%10))){return Integer.MIN_VALUE;}
result=result*10+num; i++;
}
return flag*result;
}
}
整数除法:
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以字符串形式返回小数。
如果小数部分为循环小数,则将循环的部分括在括号内。
示例 1:
输入: numerator = 1, denominator = 2
输出: “0.5”
示例 2:
输入: numerator = 2, denominator = 1
输出: “2”
示例 3:
输入: numerator = 2, denominator = 3
输出: “0.(6)”
解析:分为四个部分±号的判别,整数部分[注意越界],".",小数部分
class Solution {
public String fractionToDecimal(int numerator, int denominator) {
StringBuilder result=new StringBuilder();
if(numerator==0)return "0";
//添加-号
if(numerator<0^denominator<0){
result.append("-");
}
//获得整数部分,注意越界,−2147483648
long Ldenominator=Math.abs(Long.valueOf(denominator));
long Lnumerator=Math.abs(Long.valueOf(numerator));
result.append(Lnumerator/Ldenominator);
long num1=Lnumerator%Ldenominator;
if(num1==0)return result.toString();
//获得小数部分
result.append(".");
Map<Long,Integer> map=new HashMap<>();
StringBuilder fraction=new StringBuilder();
while(num1!=0){
if(map.containsKey(num1)){
fraction.insert(map.get(num1), "(");
fraction.append(")");
break;
}
map.put(num1,fraction.length());
num1*=10;
long num2=num1/Ldenominator;
fraction.append(String.valueOf(num2));
num1=num1%denominator;
}
result.append(fraction);
return result.toString();
}
}
丑数:
编写一个程序,找出第 n 个丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
class Solution {
public int nthUglyNumber(int n) {
int dp[]=new int [n];
int i2=0,i3=0,i5=0;
dp[0]=1;
for(int i=1;i<n;i++){
dp[i]=Math.min(dp[i2] * 2, Math.min(dp[i3] * 3, dp[i5] * 5));
if(dp[i]==dp[i2]*2)i2++;
if(dp[i]==dp[i3]*3)i3++;
if(dp[i]==dp[i5]*5)i5++;
}
return dp[n-1];
}
}
pow算法:
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
class Solution {
public double myPow(double x, int n) {
long N=n;
if(n<0){
N=-N;
x=1/x;
}
return getPow(x,N);
}
public double getPow(double x,long n){
if(n==1)return x;
if(n==0)return 1;
double value=getPow(x,n/2);
if(n%2==0){
return value*value;
}
else{
return value*value*x;
}
}
}
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。格雷编码序列必须以 0 开头。
示例 1:
输入: 2
输出: [0,1,3,2]
解释:
00 - 0
01 - 1
11 - 3
10 - 2
对于给定的 n,其格雷编码序列并不唯一。
例如,[0,2,3,1] 也是一个有效的格雷编码序列。
00 - 0
10 - 2
11 - 3
01 - 1
示例 2:
输入: 0
输出: [0]
解释: 我们定义格雷编码序列必须以 0 开头。
给定编码总位数为 n 的格雷编码序列,其长度为 2^n。当 n = 0 时,长度为 20 = 1。
因此,当 n = 0 时,其格雷编码序列为 [0]。
处。
解析,其实比较找规律。利用的迭代的方法
class Solution {
public List<Integer> grayCode(int n) {
List< Integer> list=new LinkedList<>();
list.add(0);
if(n==0)return list;
list.add(1);
for(int i=1;i<n;i++){
int value=1<<i;
for(int j=list.size()-1;j>=0;j--){
list.add(list.get(j)+value);
}
}
return list;
}
}
题目三:
给定一个整数,写一个函数来判断它是否是 3 的幂次方。
示例 1:
输入: 27
输出: true
示例 2:
输入: 0
输出: false
示例 3:
输入: 9
输出: true
示例 4:
输入: 45
输出: false
class Solution {
public boolean isPowerOfThree(int n) {
if(n==1)return true;
if(n<3)return false;
while(n%3==0){n=n/3;}
if(n==1)return true;
return false;
}
}
重复三次数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
解析:利用模拟三进制,for (int i = 0; i < 32; i++) 对每个进制位进行计算,nums[j] >>> i & 1就将数字转变成第i位
class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
//考虑每一位
for (int i = 0; i < 32; i++) {
int count = 0;
//考虑每一个数
for (int j = 0; j < nums.length; j++) {
//当前位是否是 1
if ((nums[j] >>> i & 1) == 1) {
count++;
}
}
//1 的个数是否是 3 的倍数
if (count % 3 != 0) {
ans = ans+(1 << i);
}
}
return ans;
}
}
众数:
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
class Solution {
public int majorityElement(int[] nums) {
Stack<Integer> sta=new Stack<>();
sta.push(nums[0]);int i=1;
while(i<nums.length){
if(sta.isEmpty())sta.push(nums[i]);
else if(sta.peek()==nums[i])sta.push(nums[i]);
else sta.pop();i++;
}
return sta.peek();
}
}
超过 ⌊ n/3 ⌋ 次的众数
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
示例 1:
输入: [3,2,3]
输出: [3]
示例 2:
输入: [1,1,1,3,3,2,2,2]
输出: [1,2]
解析:很多细节的地方。比如说虽然是两个值投票,但是刚开始的时候都指向nums[0],关键在于if后一定要直接continue。
并且最后还要遍历一次,因为存储的值不一定是n/3
class Solution {
public List<Integer> majorityElement(int[] nums) {
int nums1=0,nums2=0,flag1=0,flag2=0;
for(int i=0;i<nums.length;i++){
if(nums1==nums[i]){flag1++;continue;}
if(nums2==nums[i]){flag2++;continue;}
if(flag1==0){nums1=nums[i];flag1++;continue;}
if(flag2==0){nums2=nums[i];flag2++;continue;}
flag1--;flag2--;
}
List<Integer> list=new LinkedList<>();
flag1 = 0;
flag2 = 0;
for (int num : nums) {
if (num == nums1)
flag1++;
else if (num == nums2)
flag2++; }
if (flag1 > nums.length / 3)
list.add(nums1);
if (flag2 > nums.length / 3)
list.add(nums2);
return list;
}
}
所有因子:
整数可以被看作是其因子的乘积。
例如:
8 = 2 x 2 x 2;
= 2 x 4.
请实现一个函数,该函数接收一个整数 n 并返回该整数所有的因子组合。
注意:
你可以假定 n 为永远为正数。
因子必须大于 1 并且小于 n。
示例 1:
输入: 1
输出: []
示例 2:
输入: 37
输出: []
示例 3:
输入: 12
输出:
[
[2, 6],
[2, 2, 3],
[3, 4]
]
class Solution {
List<List<Integer>> result=new LinkedList<>();
public List<List<Integer>> getFactors(int n) {
if(n==1)return result;
getValue(2,n,n,new LinkedList<>());
return result;
}
public void getValue(int start,int value ,int n,LinkedList<Integer> list){
if(value==1)
{result.add(new LinkedList(list));return; }
for(int i=start;i<n;i++){
if(value%i==0){
list.add(i);
getValue(i,value/i,n,list);
list.remove(list.size()-1);
}
}
}
}
两个恰好出现一次的数
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
示例 :
输入: [1,2,1,3,2,5]
输出: [3,5]
注意:
class Solution {
public int[] singleNumber(int[] nums) {
int xor = 0;
for (int i : nums)// 一样的抵消,不一样的两个数字异或运算结果必定有一位是1
xor ^= i;
int mask = xor & (-xor);
int[] ans = new int[2];
for (int i : nums) {
if ((i & mask) == 0)//== 0、 == mask 两种结果
ans[0] ^= i;
else
ans[1] ^= i;
}
return ans;
}
}
灯泡开关
初始时有 n 个灯泡关闭。 第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。 找出 n 轮后有多少个亮着的灯泡。
示例:
输入: 3
输出: 1
解释:
初始时, 灯泡状态 [关闭, 关闭, 关闭].
第一轮后, 灯泡状态 [开启, 开启, 开启].
第二轮后, 灯泡状态 [开启, 关闭, 开启].
第三轮后, 灯泡状态 [开启, 关闭, 关闭].
你应该返回 1,因为只有一个灯泡还亮着。
class Solution {
public int bulbSwitch(int n) {
return (int)Math.sqrt(n);
}
}
计算1的数目
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
public class Solution {
public int[] countBits(int num) {
int[] ans = new int[num + 1];
int i = 0, b = 1;
while (b <= num) {
while(i < b && i + b <= num){
ans[i + b] = ans[i] + 1;
++i;
}
i = 0;
b <<= 1;
}
return ans;
}
}
超级丑数:
编写一段程序来查找第 n 个超级丑数。
超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数
示例:
输入: n = 12, primes = [2,7,13,19]
输出: 32
解释: 给定长度为 4 的质数列表 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。
说明:
1 是任何给定 primes 的超级丑数。
给定 primes 中的数字以升序排列。
0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000 。
第 n 个超级丑数确保在 32 位有符整数范围内。
解析:nums为超级丑数的值,pos[i]指的是primes[i]对应nums中的哪一个位置
public class Solution {
public int nthSuperUglyNumber(int n, int[] primes) {
if (n<=1) return 1;
int[] nums = new int[n];
nums[0] = 1;
int[] pos = new int[primes.length];
PriorityQueue<Integer> minHeap = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
return Integer.compare(nums[pos[i1]] * primes[i1], nums[pos[i2]] * primes[i2]);
}
});
for(int i=0; i<primes.length; i++) minHeap.add(i);
for(int i=1; i<n; i++) {
do {
int min = minHeap.poll();
nums[i] = nums[pos[min]] * primes[min];
pos[min] ++;
minHeap.offer(min);
} while (nums[i]==nums[i-1]);
}
return nums[n-1];
}
}