leetcode:杂项、二分查找等
来源:互联网 发布:淘宝茶叶类目 编辑:程序博客网 时间:2024/06/08 02:24
292. 尼姆游戏(Nim Game)
有一堆石头,两个人轮流从中取出1到3块石头,取得最后一块石头的是胜者。
思路:
若石头总数n是4的倍数,则后手有必胜法:每次取完后,保持剩余石头数是4的倍数。
若n不是4的倍数,则先手就有必胜法了。
return n%4 != 0;
371. 不用加减号实现简单加法
思路:
考察位运算。
按位把a和b相加,如果不考虑进位,那么结果就是 a ^ b
,即1+1 =0 0+0 = 0 1+0=1
;
考虑进位,两个数的某一位上,只有同时为1才进位,因此进位可以表示成 (a & b) << 1
,注意因为是进位,所以需要向左移动1位。
于是a+b
可以看成 (a ^ b) + ((a & b) << 1)
,这时候如果 ((a & b) << 1)
仍不为0,就循环继续,直到没有进位。
class Solution {public: int getSum(int a, int b) { int x,y; while(b) {//直到没有进位 x=a^b;//不考虑进位 y=(a&b)<<1;//a+b时的进位 a=x; b=y; } return a; }};
258. 将各位数相加(Add Digits)
将一个整数的各位数相加,再将相加结果的各位数相加……直到相加结果是个位数结束。求该个位数。
例:456->4+5+6=15->1+5=6
思路:
[n*10^(m)]%9 = [n*99...9 + n]%9 = n%9num%9 = (num各位相加的和)%9 = …… = (所求个位数)%9
设所求个位数为a,则
- 当num%9==0时,a=9;
- 当num%9!=0时,a=num%9
上面的讨论也可以用一句话描述:
return (num-1)%9+1;
319. 翻转灯泡
有n盏灯,初始状态是全灭。第1次,全部打开;第2次,每2盏灯翻转状态;第i次,每i盏灯翻转状态;……;第n次,最后一盏灯翻转状态。求此时亮着的灯的个数。
思路:
第i盏灯如果亮着,说明它有奇数个因子。对i而言,除了1和i本身,若p*q=i,则i有成对的因子,除非p=q,即i是平方数。所以,题目实际上是求n以内平方数的个数。
return (int)Math.sqrt(n);
13. 罗马数字转换成阿拉伯数字
罗马数字有如下符号:
Ⅰ(1)Ⅴ(5)Ⅹ(10)L(50)C(100)D(500)M(1000)
计数规则:
- 若干相同数字连写表示的数是这些罗马数字的和,如III=3;
- 小数字在大数字前面表示的数是用大数字减去小数字,如IV=4;
- 小数字在大数字后面表示的数是用大数字加上小数字,如VI=6;
组合规则:
- 基本数字Ⅰ、X 、C 中的任何一个,自身连用构成数目,或者放在大数的右边连用构成数目,都不能超过三个;放在大数的左边只能用一个。
- 不能把基本数字 V 、L 、D 中的任何一个作为小数放在大数的左边采用相减的方法构成数目;放在大数的右边采用相加的方式构成数目,只能使用一个。
- V 和 X 左边的小数字只能用Ⅰ。
- L 和 C 左边的小数字只能用X。
- D 和 M 左 边的小数字只能用 C 。
思路:
从前往后遍历罗马数字,如果某个数比前一个数小,则把该数加入到结果中;反之,则在结果中两次减去前一个数并加上当前这个数;
class Solution {public: int romanToInt(string s) { int sum=0; int pre=0; for(int i=0;i<s.size();i++){ int c; switch(s[i]){ case 'I':c=1;break; case 'V':c=5;break; case 'X':c=10;break; case 'L':c=50;break; case 'C':c=100;break; case 'D':c=500;break; case 'M':c=1000;break; } if(c>pre) sum+=c-2*pre; else sum+=c; pre=c; } return sum; }};
12. 阿拉伯数字转换成罗马数字
思路:
按照个-十-百-千的顺序,依次将相应位上的阿拉伯数字转换成罗马数字。如:
十位上的数字(表示0、10、20、……、90)分别表示为”“,”X”, “XX”,”XXX”,”XL”,”L”,”LX”,”LXX”,”LXXX”,”XC”,添加到最终的罗马数字字符串中去。
class Solution {public: string intToRoman(int num) { string thousand[4] = {"", "M", "MM","MMM"}; string hundred[11] = {"", "C","CC","CCC","CD","D","DC","DCC","DCCC","CM"}; string ten[11] = {"", "X", "XX","XXX","XL","L","LX","LXX","LXXX","XC"}; string one[11] = {"", "I","II","III","IV","V","VI","VII","VIII","IX"}; string result = ""; string* trans[4] = {one, ten, hundred, thousand}; int index = 0; while (num > 0) { result = trans[index][num % 10] + result; num = num / 10; index++; } return result; } };
20. 合法的括号(Java)
Given a string containing just the characters '(', ')', '{', '}', '[' and ']'
, determine if the input string is valid.
The brackets must close in the correct order, "()"
and "()[]{}"
are all valid but "(]"
and "([)]"
are not.
思路:
利用Stack,思路也比较简单,当遇上(
或者[
或者{
,就将它们压栈;当遇上)
或者]
或者}
,如果栈顶元素是对应的左括号,就将该左括号弹出。最后,如果栈为空,就说明合法,否则不合法。
public class Solution { public boolean isValid(String s) { Stack stack = new Stack<>(); for(int i=0;i<s.length();i++) { char c=s.charAt(i); if(c=='(' || c=='[' || c=='{') { stack.push(c); } if(c==')') { if(stack.empty()) return false; if((char)stack.peek()=='(') stack.pop(); else return false; } if(c==']') { if(stack.empty()) return false; if((char)stack.peek()=='[') stack.pop(); else return false; } if(c=='}') { if(stack.empty()) return false; if((char)stack.peek()=='{') stack.pop(); else return false; } } if(stack.empty()) return true; return false; }}
32. 最长的合法括号
给定一个只含有左右括号的字符串,寻找最长的合法括号子串。
例如 ")()())"
,其最长合法括号子串是"()()"
,长度为4。
思路:
同样用到栈,但和上题不同的是,为了正确计算合法括号子串的长度,将左括号的下标存入堆栈。另外定义了一个辅助变量lastValidIndx
,它用来保存一个新的子串的起始位置。
class Solution {public: int longestValidParentheses(string s) { stack<int> paranStack; int maxLength=0; int lastValidIndx=0; for (int indx=0; indx<s.length(); indx++) { //遇到左括号,直接存入。 if (s[indx]=='(') paranStack.push(indx); //遇到右括号,分情况讨论 else { //如果此时栈已经为空,那么当前的右括号就是多余出来的,表明当前合法子串到此结束了。下一个可能的合法字符串的起始位置为indx+1 if (paranStack.empty()) lastValidIndx=indx+1; //如果此时栈不空,可能有两种情况: else { paranStack.pop(); //如果栈正好剩下1个左括号和当前右括号配对,当前合法子串的长度就是indx-lastValidIndx+1 if (paranStack.empty()) maxLength=max(maxLength, indx-lastValidIndx+1); //如果栈有超过1个的左括号,说明当前的合法字符串的开始位置是栈顶(的下标,结束位置为当前位置,长度为indx-paranStack.top() else maxLength=max(maxLength, indx-paranStack.top()); } } } return maxLength; }};
22. 生成括号
给定n对括号,返回所有合法的组合情况。
思路:
组合的个数属于卡特兰数。
对于本题,注意到左括号剩余个数leftNum和右括号剩余个数rightNum满足这样几条规则:
- 当leftNum和rightNum都等于0时,说明完成了一种组合,返回;
- 当leftNum还剩余时,下面添加一个左括号是合法的;
- 当rightNum还剩余,而且
leftNum<rightNum
(已有的左括号数量大于右括号)时,下面添加一个右括号也是合法的。
递归实现如下:
class Solution {public: vector<string> generateParenthesis(int n) { vector<string> v; string s; generate(n,n,s,v); return v; }void generate(int leftNum,int rightNum,string s,vector<string> &result) { if(leftNum==0 && rightNum==0)//终止条件 { result.push_back(s); } if(leftNum>0) { generate(leftNum-1,rightNum,s+'(',result); } if(rightNum>0&&leftNum<rightNum) { generate(leftNum,rightNum-1,s+')',result); } } };
326. 判断某数是否是3的N次幂
思路:
最直观的思路是while(n%3==0) n/=3
,但这样会超时。另一种思路是这样的:
若3^x=n
,则x*log(3)=log(n)
,x=log(n)/log(3)
,通过判断x是否是整数就可以判断n是否是3的幂。
但值得注意的是,实际操作时,用log(n)函数会因为精度问题不能ac,应该采用log10(n)函数。
该思路可以用来解决所有类似“判断是否是x的N次幂”的问题。
class Solution {public: bool isPowerOfThree(int n) { if(n<=0) return false; double logAns= log10(n)/log10(3); return abs(logAns-int(logAns))<1e-12; }};
59. 填入螺旋矩阵
给定一个数n,要求将1~n^2以顺时针螺旋的方式填入n*n的矩阵中。
思路:
定义四个变量,rowBegin、rowEnd、colBegin、colEnd,它们用于帮助确定起始坐标。螺旋填数字可以看成(向右、向下、向左、向上)模式的循环。所以,在while循环内,依次执行一次循环下的四个方向的填入操作,当然,每次执行时都要判断一下是否已经填完了。
class Solution {public: vector<vector<int> > generateMatrix(int n) { if(n == 0) return vector<vector<int>>(); vector<vector<int>> ret(n, vector<int>(n, 0)); int rowBegin = 0; int rowEnd = n - 1; int colBegin = 0; int colEnd = n - 1; int num = 1; while (rowBegin <= rowEnd && colBegin <= colEnd) { if (num <= n^2) { //向右遍历添加 for (int j = colBegin; j <= colEnd; j++) { ret[rowBegin][j] = num; num++; } } rowBegin++; if (num <= n^2) { //向下遍历添加 for (int i = rowBegin; i <= rowEnd; i++) { ret[i][colEnd] = num; num++; } } colEnd--; if (num <= n^2) { //向左遍历添加 for (int j = colEnd; j >= colBegin; j--) { ret[rowEnd][j] = num; num++; } } rowEnd--; if (num <= n^2) { //向上遍历添加 for (int i = rowEnd; i >= rowBegin; i--) { ret[i][colBegin] = num; num++; } } colBegin++; } return ret; }};
54. 读取螺旋矩阵(Java)
和上题类似。这里我用了另一组变量,感觉两种变量的选取都可以。
public class Solution { public List<Integer> spiralOrder(int[][] matrix) { List<Integer> res=new ArrayList<>(); int m,n; m=matrix.length; if(m==0) return res; n=matrix[0].length; int dir=1;//读取方向 int num=0;//计数,判断是否结束 int row=0,col=-1; int right=n-1,down=m-1,left=0,up=1;//存储各个方向上的边界位置 while(num<(m*n)) { if(dir==1) { for(int i=col+1;i<=right;i++) { num++; res.add(matrix[row][i]); } col=right; right--; dir=2; continue; } else if(dir==2) { for(int j=row+1;j<=down;j++) { num++; res.add(matrix[j][col]); } row=down; down--; dir=3; continue; } else if(dir==3) { for(int i=col-1;i>=left;i--) { num++; res.add(matrix[row][i]); } col=left; left++; dir=4; continue; } else if(dir==4) { for(int j=row-1;j>=up;j--) { num++; res.add(matrix[j][col]); } row=up; up++; dir=1; continue; } } return res; }}
240. 搜索2维矩阵
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
Integers in each row are sorted in ascending from left to right.
Integers in each column are sorted in ascending from top to bottom.
For example,
Consider the following matrix:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
Given target = 5, return true.
Given target = 20, return false.
思路:
如果逐行逐列地找,肯定能找到,但会超出时间。
巧妙的思路是从右上角出发,
- 如果matrix[i][j]小于target, 则该行不可能有此数, 所以i++;
- 如果matrix[i][j]大于target, 则该列不可能有此数, 所以j–。
- 遇到边界则表明该矩阵不含target。
class Solution {public: bool searchMatrix(vector<vector<int>>& matrix, int target) { int row=matrix.size(); int column=matrix[0].size(); if(row==0 && column==0) return false; int j=column-1; int i=0; while(i<row && j>=0){ if(matrix[i][j]==target) return true; if(matrix[i][j]>target) j--; if(matrix[i][j]<target) i++; } return false; }};
73. 将矩阵的某行某列设为0
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.
思路:
解这道题不难,但如果要求不开辟新的空间,就需要一个比较巧妙的思路。
可以利用矩阵的第0行和第0列作为辅助空间使用。具体方法是:
- 先确定第0行和第0列本身是否需要清零,用两个标志位先标记好。如果需要清零,最后一步就是把第0行或第0列清零。
- 扫描剩下的
(m-1)*(n-1)
矩阵,如果遇到了0,就将对应的第0行和第0列上的元素置0。
比如matrix[i][j]==0,那么matrix[i][0]处在第i行,matrix[0][j]处于第j列,将它们置0。 - 根据第0行和第0列的信息,将需要清零的行和列进行清零。
- 最后,根据两个flag,处理第0行和第0列。
这样一来,不需要开辟新的空间,就能完成任务。
class Solution {public: void setZeroes(vector<vector<int>>& matrix) { int m=matrix.size(); int n=matrix[0].size(); bool flag0row=0,flag0col=0; int i=0,j=0,row=0,col=0; for(i=0;i<n;i++) if(!matrix[0][i]) {flag0row=1;break;} for(i=0;i<m;i++) if(!matrix[i][0]) {flag0col=1;break;} for(i=0;i<m;i++) { row=i; for(j=0;j<n;j++) { col=j; if(matrix[i][j]==0) { matrix[0][col]=0; matrix[row][0]=0; } } } for(i=1;i<n;i++) if(matrix[0][i]==0) { for(int p=1;p<m;p++) matrix[p][i]=0; } for(i=1;i<m;i++) if(matrix[i][0]==0) { for(int p=1;p<n;p++) matrix[i][p]=0; } if(flag0row) for(int p=0;p<n;p++) matrix[0][p]=0; if(flag0col) for(int p=1;p<m;p++) matrix[p][0]=0; }};
172. 求n!后带多少个零
Given an integer n, return the number of trailing zeroes in n!.
思路:
只有2和5相乘才会出现0,所以,题目转化成看1-n中有多少个2和5。又发现2的数量一定多于5的个数,于是我们只看n前面有多少个5就行了。
5:1,10:1,15:1,20:1,25:2
30:1,35:1,40:1,45:1,50:2
…… n/5+n/25+n/625+...
可以得到1-n中所有5的个数。
class Solution { public: int trailingZeroes(int n) { if(n<1) return 0; int c = 0; while(n/5 != 0) { n /= 5; c += n; } return c; } };
367. 判断是否是完全平方数
题目大意:不用sqrt函数,判断一个正整数是否为完全平方数。
思路:
基于二分查找法,寻找能使平方数等于num的n值,如果找到了,也就说明num是完全平方数,否则num不是。
class Solution {public: bool isPerfectSquare(int num) { return nt(num,1,num); } bool nt(long num,long begin,long end){ if(begin>end) return false;//如果begin大于end了,就返回false if(begin==end) return (begin*begin) == num;//如果begin等于end,判断该值的平方是否是所给的数 long mid=(begin+end)/2; if(mid*mid == num) return true;//进行二分查找 if(mid*mid < num) return nt(num,mid+1,end); return nt(num,begin,mid-1); }};
201. 对数列进行按位与操作(Java)
Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers in this range, inclusive.
For example, given the range [5, 7], you should return 4.
思路:
暴力解法会超时,当m!=n,那么最低位必定等于0,因为[m, n]必定包含奇偶数,相与后最低位等于0。
可以将m,n都右移一位,记为mk、 nk,这样就相当于将[m, n]之间的所有的数都右移动了一位,当mk=nk的时候,说明之前[m, n]之间的数右移一位后是相等的,这些数进行AND操作,结果还是mk(或nk),所以操作就可以停止了。记录右移的次数offset,m<<offset
即为所求结果。
public class Solution { public int rangeBitwiseAnd(int m, int n) { int bit = 0; while(m!=n) { m>>=1; n>>=1; bit++; } return m<<bit; } }
200. 小岛个数(Java)
思路:
递归,每遇到’1’后, 开始向四个方向递归搜索. 搜到后将其变为’0’, 因为相邻的属于一个island. 然后开始继续找下一个’1’。
当然,传统的思路是并查集。
public class Solution { public int numIslands(char[][] grid) { int count = 0; for(int i=0; i<grid.length; i++) { for(int j=0; j<grid[0].length; j++) { if(grid[i][j]=='1') { search(grid, i, j); ++count; } } } return count; } private void search(char[][] grid, int x, int y) { if(x<0 || x>=grid.length || y<0 || y>=grid[0].length || grid[x][y]!='1') return; grid[x][y] = '0'; search(grid, x-1, y); search(grid, x+1, y); search(grid, x, y-1); search(grid, x, y+1); } }
29. 不用除号实现两数相除(Java)
Divide two integers without using multiplication, division and mod operator.
If it is overflow, return MAX_INT.
思路:
不能乘除就应该用加减,但是加减有可能速度太慢,因此需要转换。由于任何一个数都能表示成二进制,所以有dividend=divisor*(a*2^0 + b*2^1 + …… + m*2^k)
所以只要计算出所有divisor*2^k,然后减去即可。
public class Solution { public int divide(int dividend, int divisor) { boolean isNeg = (dividend >= 0) ^ (divisor >= 0); //除数和被除数是否异号,是的话结果为负 long divid = Math.abs( (long) dividend); long divis = Math.abs( (long) divisor); long quotient = 0; while(divid>=divis) { long k = divis; int i = 0; while((k<<1)<divid) { k = k<<1; ++i; } divid -= k; quotient += 1<<i; } if(quotient > Integer.MAX_VALUE && !isNeg) return Integer.MAX_VALUE; return (int) (isNeg? -quotient : quotient); } }
50. 实现幂运算
思路:
需要注意的是n是负数的情况,如果n是INT_MAX,则要注意防止溢出。
大体上是通过pow(x,n)=pow(x,n/2) * pow(x,n/2)
或pow(x,n)=pow(x,n/2) * pow(x,n/2) * x
来加快运算。
class Solution {public: double myPow(double x, int n) { if (n == 0) return 1.0; //如果n是负数 if (n < 0) { //判断是否溢出 if (n == INT_MIN) return 1.0 / (myPow(x, INT_MAX)*x); else return 1.0 / myPow(x, -n); } else { if (n % 2 == 0) { double temp = myPow(x, n >> 1); return temp * temp; } else { double temp = myPow(x, (n - 1) >> 1); return temp * temp * x; } } }};
69. 实现平方根运算
思路:
首先是二分法:
class Solution {public: int mySqrt(int x) { double begin = 0; double end = x; double result = 1; double mid = 1; while(abs(result-x) > 0.000001){ mid = (begin+end)/2; result = mid*mid; if(result > x) end = mid; else begin = mid; } return (int)mid; }};
更快速的方法是牛顿迭代法:
这里有f(x)=x^2-N
,其根为sqrt(N)
,即为所求,因此需要不断逼近。
有Xn+1 = Xn - (Xn*Xn-N)/(2*Xn)
class Solution { public: int mySqrt(int x) { double pre = 0; double cur = x; //这里从x开始,从x/2开始会导致 1 不能满足 x(n+1)= xn - f'(xn)/f(xn) while(abs(cur - pre) > 0.000001){ pre = cur; cur = pre - (pre*pre-x)/(2*pre); } return int(cur); } };
166. 以字符串形式返回小数(Java)
题意:
给定一个分子和一个分母,以字符串的形式返回该小数。如果小数无限循环的话,用括号扩住循环体。
思路:
难点:如何识别循环体?
解决方法:用一个HashMap记录每一个余数,当出现重复的余数时,那么将会进入循环,两个重复余数之间的部分就是循环体。
示例:1/13=0.076923076923076923…,当小数部分第二次出现0时,就意味着开始了循环,那么需要把076923用括号括起来,结果为0.(076923)。
涉及技巧:1)在不断相除的过程中,把余数乘以10再进行下一次相除,保证一直是整数相除;2)HashMap的key和value分别是<当前余数, 对应结果下标>,这样获取076923时就可根据value值来找。
注意点1:考虑正负数,先判断符号,然后都转化为正数;
注意点2:考虑溢出,如果输入为Integer.MIN_VALUE,取绝对值后会溢出。
public class Solution { public String fractionToDecimal(int numerator, int denominator) { if (numerator == 0) return "0"; if (denominator == 0) return ""; String ans = ""; //如果结果为负数 if ((numerator < 0) ^ (denominator < 0)) { ans += "-"; } //下面要把两个数都转为正数,为避免溢出,int转为long long num = numerator, den = denominator; num = Math.abs(num); den = Math.abs(den); //结果的整数部分 long res = num / den; ans += String.valueOf(res); //如果能够整除,返回结果 long rem = (num % den) * 10; if (rem == 0) return ans; //结果的小数部分 HashMap<Long, Integer> map = new HashMap<Long, Integer>(); ans += "."; while (rem != 0) { //如果前面已经出现过该余数,那么将会开始循环 if (map.containsKey(rem)) { int beg = map.get(rem); //循环体开始的位置 String part1 = ans.substring(0, beg); String part2 = ans.substring(beg, ans.length()); ans = part1 + "(" + part2 + ")"; return ans; } //继续往下除 map.put(rem, ans.length()); res = rem / den; ans += String.valueOf(res); rem = (rem % den) * 10; } return ans; }}
372. 超级幂(Java)
Your task is to calculate ab mod 1337 where a is a positive integer and b is an extremely large positive integer given in the form of an array.
Example1:
a = 2b = [3]
Result: 8
Example2:
a = 2b = [1,0]
Result: 1024
思路:
需要用到的数学知识:快速幂取模。
- (a^b) % 1337 = ((a%1337)^b) % 1337
- (x*y) % 1337 = ((x%1337) * (y%1337)) % 1337
式一用来削减a,而式二是用来将b按位处理。
public class Solution { public int superPow(int a, int[] b) { int res = 1; for (int i = 0; i < b.length; i++) { res = pow(res, 10) * pow(a, b[i]) % 1337; } return res; } public int pow(int a, int b) { if (b == 0) return 1; if (b == 1) return a % 1337; return pow(a % 1337, b / 2) * pow(a % 1337, b - b / 2) % 1337; } }
9. 回文数字(Java)
判断一个整数是否是回文数字。
思路:
首先,负数不是回文数字,0是回文数字。对于一个正整数,由于不能使用额外的空间,可以每次将该数字的首尾两个数取出来,然后判断其是否相等。
public class Solution { public boolean isPalindrome(int x) { if(x<0) return false; if(x==0) return true; int t=1; int left=0; int right=0; while((x/t)>=10) t*=10; while(x>0) { left=x/t; right=x%10; x=x%t/10; t/=100; if(left!=right) return false; } return true; }}
- leetcode:杂项、二分查找等
- leetcode 二分查找系列
- LeetCode二分查找总结
- leetcode二分排序 & 查找:
- LeetCode专题: 二分查找
- Leetcode二分查找算法
- LeetCode总结--二分查找篇
- 【leetcode边做边学】二分查找应用
- leetcode 二分查找 Sqrt(x)
- Leetcode二分查找类题目
- <LeetCode> 题14:二分查找
- Leetcode分类解析:二分查找
- leetcode--34--二分查找范围
- leetcode--二分查找和二分排序
- LeetCode基础-查找-排序数组二分查找
- 字典文件等杂项
- 服务器数据库等杂项
- Leetcode 二分查找 Search Insert Position
- CoreLocation基本使用
- App 跳转到App Store,一句代码搞定
- Python读写Json涉及到中文的处理
- svn 查看,删除用户名,密码
- Add Digits
- leetcode:杂项、二分查找等
- http
- 一次图像处理工程师笔试面试经历
- AVS与h.264的来源与区别
- 图片高斯模糊效果简单优化
- MAC Cocoa Opengl入门系列教程二(OpenGl坐标系)
- [Maven实战](2)Eclipse插件m2eclipse
- java线程组
- 机器学习常见算法分类汇总