面试题16:数值的整数次方
题目:实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
算法:时间复杂度为O(logN)的快速乘方
测试:力扣
说明:exponent的范围是-2147483648-2147483647,所以取绝对值时需要强制类型转换为long
public double myPow(double base, int exponent) { if (base == 0.0 && exponent < 0) { return -1; } long absExponent = exponent >= 0 ? exponent : -(long)exponent; double result = powerWithUnsignedExponent(base, absExponent); if (exponent < 0) { result = 1.0 / result; } return result; } private double powerWithUnsignedExponent(double base, long exponent) { if (exponent == 0) { return 1; } if (exponent == 1) { return base; } double result = powerWithUnsignedExponent(base, exponent >> 1); result *= result; if ((exponent & 1) == 1) { return result * base; } return result; }
面试题17:打印1到最大的n位数
题目:输入数字n,按顺序打印出从1最大的n位十进制数。比如输入3,则打印出1、2、3一直到最大的3位数即999。
- 算法:char数组保存大数,标记进位
public void print1ToMaxOfNDigits(int n) { if (n <= 0) { return; } char[] number = new char[n]; for (int i = 0; i < n; i++) { number[i] = '0'; } while (!increment(number)) { printNumbers(number); } } private boolean increment(char[] number) { boolean isOverFlow = false; int takeOver = 0; int length = number.length; for (int i = length - 1; i >= 0; i--) { int nSum = number[i] - '0' + takeOver; if (i == length - 1) { nSum++; } if (nSum >= 10) { if (i == 0) { isOverFlow = true; } else { nSum -= 10; takeOver = 1; number[i] = (char) ('0' + nSum); } } else { number[i] = (char) ('0'+nSum); } } return isOverFlow; } private void printNumbers(char[] number) { boolean isBegining0 = true; int length = number.length; for (int i = 0; i < length; i++) { if (isBegining0 && number[i] != '0') { isBegining0 = false; } if (!isBegining0) { System.out.printf("%c", number[i]); } } System.out.printf("\t"); }
- 算法:递归打印全排列
public void print1ToMaxOfNDigits(int n) { if (n <= 0) { return; } char[] number = new char[n]; for (int i = 0; i < 10; i++) { number[0] = (char) ('0' + i); print1ToMaxOfNDigitsRecursive(number, 1); } } private void print1ToMaxOfNDigitsRecursive(char[] number, int index) { if (index == number.length) { printNumbers(number); return; } for (int i = 0; i < 10; i++) { number[index] = (char) ('0' + i); print1ToMaxOfNDigitsRecursive(number, index+1); } } private void printNumbers(char[] number) { boolean isBegining0 = true; int length = number.length; for (int i = 0; i < length; i++) { if (isBegining0 && number[i] != '0') { isBegining0 = false; } if (!isBegining0) { System.out.printf("%c", number[i]); } } System.out.printf("\t"); }
面试题18:删除链表的结点
题目一:在O(1)时间内删除链表的结点
题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。
public ListNode deleteNode(ListNode head, ListNode toBeDeleted) { if (head == null) { return null; } if (toBeDeleted == null) { return head; } if (toBeDeleted.next != null) { // toBeDeleted is not the tail node ListNode next = toBeDeleted.next; toBeDeleted.val = next.val; toBeDeleted.next = next.next; } else if (head == toBeDeleted) { // toBeDeleted is the tail node and the head head = null; } else { // toBeDeleted is the tail node but not the head ListNode curr = head; while (curr.next != toBeDeleted) { curr = curr.next; } curr.next = null; } return head; }
题目二:删除链表中重复的结点
题目:在一个排序的链表中,如何删除重复的结点?例如,在图3.4(a)中重复结点被删除之后,链表如图3.4(b)所示。
1 --> 2 --> 3 --> 3 --> 4 --> 4 --> 5
1 --> 2 --> 5
public ListNode deleteDuplicatedNode(ListNode head) { if (head == null || head.next == null) { return head; } ListNode prev = null; ListNode curr = head; while (curr != null) { boolean needDelete = false; ListNode next = curr.next; if (next != null && next.val == curr.val) { needDelete = true; } if (needDelete) { int value = curr.val; while (next != null && next.val == value) { next = next.next; } if (prev == null) { head = next; } else { prev.next = next; } curr = next; } else { prev = curr; curr = curr.next; } } return head; }
面试题19:正则表达式匹配
题目:请实现一个函数用来匹配包含'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"及"ab*a"均不匹配。
- 算法:动态规划
1, If p.charAt(j) == s.charAt(i): dp[i][j] = dp[i-1][j-1]; 2, If p.charAt(j) == '.': dp[i][j] = dp[i-1][j-1]; 3, If p.charAt(j) == '*': here are two sub conditions: (1) if p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] // in this case, a* only counts as empty (2) if p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.': dp[i][j] = dp[i-1][j] // in this case, a* counts as multiple a or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty
public boolean isMatch(String string, String pattern) { if (string == null || pattern == null) { return false; } boolean[][] dp = new boolean[string.length()+1][pattern.length()+1]; dp[0][0] = true; for (int i = 0; i < pattern.length(); i++) { if (pattern.charAt(i) == '*' && dp[0][i-1]) { dp[0][i+1] = true; } } for (int i = 0 ; i < string.length(); i++) { for (int j = 0; j < pattern.length(); j++) { if (pattern.charAt(j) == '.') { dp[i+1][j+1] = dp[i][j]; } if (pattern.charAt(j) == string.charAt(i)) { dp[i+1][j+1] = dp[i][j]; } if (pattern.charAt(j) == '*') { if (pattern.charAt(j-1) != string.charAt(i) && pattern.charAt(j-1) != '.') { dp[i+1][j+1] = dp[i+1][j-1]; } else { dp[i+1][j+1] = (dp[i+1][j-1] || dp[i+1][j] || dp[i][j+1]); } } } } return dp[string.length()][pattern.length()]; }
- 算法:递归,长的字符串函数调用栈过长时会有TLE
public boolean isMatch(String string, String pattern) { if (string == null || pattern == null) { return false; } return matchCore(string, pattern); } private boolean matchCore(String string, String pattern) { if (pattern.length() == 0) { if (string.length() == 0) return true; else return false; } boolean matchNext = string.length() != 0 && (pattern.charAt(0) == '.' || pattern.charAt(0) == string.charAt(0)); if (pattern.length() >= 2 && pattern.charAt(1) == '*') { if (matchNext) { return matchCore(string.substring(1), pattern.substring(2)) || matchCore(string.substring(1), pattern) || matchCore(string, pattern.substring(2)); } else { return matchCore(string, pattern.substring(2)); } } if (matchNext) { return matchCore(string.substring(1), pattern.substring(1)); } return false; }
面试题20:表示数值的字符串
题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串“+100”、“5e2”、“-123”、“3.1416”及“-1E-16”都表示数值,但“12e”、“1a3.14”、“1.2.3”、“+-5”及“12e+5.4”都不是
- 算法:逐个扫描并删除字符
public boolean isNumber(String s) { if (s == null) { return false; } s = s.trim(); if (s.length() == 0) { return false; } StringBuilder sb = new StringBuilder(s); boolean numeric = scanInteger(sb); // 如果出现'.',接下来是数字的小数部分 if (sb.length() > 0 && sb.charAt(0) == '.') { sb.deleteCharAt(0); // 下面一行代码用||的原因: // 1. 小数可以没有整数部分,例如.123等于0.123;左 // 2. 小数点后面可以没有数字,例如233.等于233.0; // 3. 当然小数点前面和后面可以有数字,例如233.666 numeric = scanUnsignedInteger(sb) || numeric; } // 如果出现'e'或者'E',接下来跟着的是数字的指数部分 if (sb.length() > 0 && (sb.charAt(0) == 'E' || sb.charAt(0) == 'e')) { sb.deleteCharAt(0); // 下面一行代码用&&的原因: // 1. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4;左 // 2. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;右 numeric = scanInteger(sb) && numeric; } return numeric && sb.length() == 0; } private boolean scanInteger(StringBuilder s) { if (s.length() > 0 && (s.charAt(0) == '+' || s.charAt(0) == '-')) { s.deleteCharAt(0); } return scanUnsignedInteger(s); } private boolean scanUnsignedInteger(StringBuilder s) { boolean flag = false; while (s.length() > 0 && s.charAt(0) >= '0' && s.charAt(0) <= '9') { s.deleteCharAt(0); flag = true; } return flag; }
- 算法:四个flag标志遍历进展
- 算法:Here
public boolean isNumber(String s) { s = s.trim(); boolean pointSeen = false; boolean eSeen = false; boolean numberSeen = false; boolean numberAfterE = true; for(int i=0; i<s.length(); i++) { if('0' <= s.charAt(i) && s.charAt(i) <= '9') { numberSeen = true; numberAfterE = true; } else if(s.charAt(i) == '.') { if(eSeen || pointSeen) { return false; } pointSeen = true; } else if(s.charAt(i) == 'e') { if(eSeen || !numberSeen) { return false; } numberAfterE = false; eSeen = true; } else if(s.charAt(i) == '-' || s.charAt(i) == '+') { if(i != 0 && s.charAt(i-1) != 'e') { return false; } } else { return false; } } return numberSeen && numberAfterE; }
面试题21:调整数组顺序使奇数位于偶数前面
题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
说明:Java中函数式接口的功能使其可以像C++中传递函数作为参数
public int[] exchange(int[] nums) { if (nums == null || nums.length == 0) { return nums; } MyFunc isEven = (x) -> (x & 1) == 0; return exchangeWithFunc(nums, isEven); } private interface MyFunc { boolean func(int x); } private int[] exchangeWithFunc(int[] nums, MyFunc myFunc) { int low = 0; int high = nums.length - 1; while (low < high) { while (low < high && !myFunc.func(nums[low])) { low++; } while (high > low && myFunc.func(nums[high])) { high--; } if (low < high) { int temp = nums[low]; nums[low] = nums[high]; nums[high] = temp; } } return nums; }