LeetCode过程中值得反思的细节(二)
本周10道题,此栏目将每周定期更新。题号为LeetCode剑指Offer题库中的题号。
剪绳子14
这道题需要思考剪绳子的过程
public int cuttingRope(int n) { if(n<=3) return n-1; if(n%3==0)return (int)Math.pow(3,(n/3)); if(n%3==2)return (int)Math.pow(3,n/3)*2; if(n%3==1)return (int)Math.pow(3,n/3-1)*4; return 0; }
首先算术平均数大于等于几何平均数,当剪的每段长度相等时达到两个平均数互等条件。
当每段都取3时,可达到极大值,从以下公式求导可知:
动态规划算法:
public int cuttingRope(int n) { int[] dp = new int[n + 1]; dp[2] = 1; for(int i = 3; i < n + 1; i++){ for(int j = 2; j < i; j++){ dp[i] = Math.max(dp[i],Math.max(j * (i - j), j * dp[i - j])); } } return dp[n]; }
这个算法同样成立,但是如果不知道数学推导的话还是比较难理解这个算法,这里面包含了很多可能性都进行了比较。
剪绳子Ⅱ 14
public int cuttingRope(int n) { if(n<=3)return n-1; int b = n/3; long a,rem; if(n%3==0)return quickPow(3,n/3); if(n%3==1){ rem = ((long)quickPow(3,n/3-1))*4%1000000007; return (int)rem; } if(n%3==2)return quickPow(3,n/3)*2%1000000007; return 0; } public int quickPow(long a,int b){ long temp = 1; while(b>0){ if(b%2==1) temp=(a*temp)%1000000007; a=a*a%1000000007; b=b/2; } return (int)temp; }
本题中,n的范围增大,故取余是一个必要操作
取余的操作有两种:例如a^b
- b个a相乘,每次均进行取余(%1000000007),复杂度为O(N)
- a^b = a^(b的二进制转换),复杂度为O(logN),此方法即为快速取余法(利用二进制便于理解)
上述代码的quickPow()
即为取b的二进制进行相乘操作
二进制中的1个数15
本题中是以二进制形式输入一个int
,进行判断二进制数中有多少个1
直接利用int
十进制解决的话涉及到补码问题,需要多个if
判断,并不方便,所以使用移位和与操作较好
两种思路:
- 每次和1与操作
- 每次进行
n&(n-1)
,这个操作是将n
最右边的1变为0,有多少次循环即有多少个1(while
条件n==0)
数值的整数次方16
该题仍然是利用快速幂法的一道题,不再详细赘述,可看剪绳子14
这题的一个细节在于
int
的取值范围为:
- 最小值是 -2,147,483,648(-2^31);
- 最大值是 2,147,483,647(2^31 - 1);
当int
取最小值-2^31
时,取反会越界而赋值出错,所以需要转换类型long
做题时,首要目标在于审题,观察数据的取值范围,进而进行if
的合理安排
打印1到最大的n位数17(包含大数版)
首先第一个易错点是 Math.pow()
返回的是double
类型,注意强制转换。
大数需要将数字转为String
形式,可调用递归返回包含所有数字的字符串。以n=2
举例,共用100种全排列。
class Solution { int[] res; int n,count=0,nine=0; int start; String ans; char[] num,loop={'0','1','2','3','4','5','6','7','8','9'};//num并没有初始化 public int[] printNumbers(int n) { this.n = n; start=n-1; num = new char[n]; res = new int[((int)Math.pow(10,n))-1]; dfs(0); return res; } public void dfs(int x){ if(x==n){ ans = String.valueOf(num).substring(start); if(!ans.equals("0")) res[count++]=Integer.parseInt(ans); if(n-start == nine) start--; return ; } for(char i :loop){ if(i=='9') nine++; num[x]=i; dfs(x+1); } nine--; //注意,这里相当于,例n=3,当'009'时,nine=1,当'010'时,nine=0,当'099'时,nine=2 }}
这里注意substring
中全小写,Integer.parseInt
为将String
转为int
正则表达式匹配19
注意,boolean
的默认值为false
先上答案:
class Solution { public boolean isMatch(String s, String p) { int m = s.length(); int n = p.length(); boolean[][] f = new boolean[m+1][n+1]; f[0][0]=true; for(int i = 0; i < m+1 ; i++) for(int j = 1 ; j < n+1 ; j++){ if(p.charAt(j-1)!='*'){ if(i>0 && (s.charAt(i-1)==p.charAt(j-1) || p.charAt(j-1)=='.')) f[i][j]=f[i-1][j-1]; } else{ if(j>=2){ f[i][j]=f[i][j-2]; } if(i>0 && (s.charAt(i-1)==p.charAt(j-2) || p.charAt(j-2)=='.')) f[i][j] |= f[i-1][j]; } } return f[m][n]; }}
这里利用的是动态规划,f[m][n]
表示s的前m个字符和p的前n个字符是否匹配
f[m][0]
均为false
除f[0][0]
外
因为空串匹配空表达式,但是非空串一定不匹配空表达式
而空串不一定匹配非空表达式
这里需要注意的是.charAt()
中的索引值和数组中的索引值实际上相差1
当遇到*
时,两种情况:1.当前s串的字符和a*
不匹配,即直接返回f[i][j-2]
。2.当前s串的字符和a*
匹配,直接返回f[i-1][j]
,因为如果匹配的话,i-1必然也匹配。
表示数值的字符串20
有一个比较暴力的解法,利用异常自动判断
public boolean isNumber(String s) { s=s.trim(); int len = s.length(); try{ double res = Double.parseDouble(s); }catch (Exception e){ return false; } char a = s.substring(len-1).charAt(0); if(a>='0'&&a<='9'||(a=='.')) return true; return false; }
但是正常思路是利用自动机,建立状态:
共判断了8到10种状态,不同解法状态数可能不同,个人不喜欢这种解法,不再解释了。
不过,自动机解法有利于理解计算机的词法器语法器等。
数组顺序奇数前偶数后21
这道题首先可以暴力求解,遍历两次,建立新数组,时间复杂度:O(N)
双指针解法:(首尾双指针和快慢双指针)
public int[] exchange(int[] nums) { if(nums.length==0) return new int[]{}; int left=0,right=nums.length-1; int temp; while(left!=right){ if(nums[left]%2==1){ left++; continue; } if(nums[right]%2==0){ right--; continue; } temp=nums[left]; nums[left]=nums[right]; nums[right]=temp; } return nums; }
demo
为首尾双指针算法,快慢双指针为:快指针去找后面的奇数,而慢指针指向当前最左的偶数。
class Solution { public int[] exchange(int[] nums) { int i=0,j=0; //i为慢指针,j为快指针 while(j<nums.length){ if((nums[j]&1)!=0){ int tmp=nums[i]; nums[i]=nums[j]; nums[j]=tmp; i++; } j++; } return nums; }}
链表中倒数第k个节点22
利用快慢双指针即可解,可参考21,(easy题目不再详细解释)
class Solution { public ListNode getKthFromEnd(ListNode head, int k) { ListNode res = head; for(int i = 0 ; i < k ; i++){ res = res.next; } while(res!=null){ head = head.next; res = res.next; } return head; }}
反转链表24
class Solution { public ListNode reverseList(ListNode head) { ListNode first=null,sec=null,thi=null; first = head; if(first!=null && first.next!=null){ sec = first.next; thi = sec.next; } if(first!=null) first.next = null; while(sec!=null){ sec.next = first; first = sec; sec = thi; if(thi!=null) thi = thi.next; } return first; }}
这个方法是建立了三个临时节点,进行链表反转,easy题目不再详细解释。
官方解法,减少了判断条件:
class Solution { public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } return prev; }}
此外,还可以利用递归,不再详细赘述。
原文转载:http://www.shaoqun.com/a/701746.html
worldfirst:https://www.ikjzd.com/w/289
慧聪商务网:https://www.ikjzd.com/w/1836
LeetCode过程中值得反思的细节(二)本周10道题,此栏目将每周定期更新。题号为LeetCode剑指Offer题库中的题号。剪绳子14这道题需要思考剪绳子的过程publicintcuttingRope(intn){if(n<=3)returnn-1;if(n%3==0)return(int)Math.pow(3,(n/3));if(n%3==2)return(int)Math.pow(3
unsplash:https://www.ikjzd.com/w/756.html
gtc:https://www.ikjzd.com/w/974
淘粉8:https://www.ikjzd.com/w/1725.html
口述:爱爱后老婆总把狗抱上床 受不了口述爱爱老婆:http://lady.shaoqun.com/m/a/25294.html
权威解读!你知道亚马逊跨境电商园吗?:https://www.ikjzd.com/home/20095
(精品分析)亚马逊美国站夜灯类目市场调查数据报告:https://www.ikjzd.com/home/102790
No comments:
Post a Comment