持续更新。。。
1、A+B问题(2019年5月5日)
描述:给出两个整数 a 和 b , 求他们的和。
- 你不需要从输入流读入数据,只需要根据
aplusb
的两个参数a和b,计算他们的和并返回就行。
public int aplusb(int a, int b) {
//a ^ b表示a和b相加之后,该进位的地方没有进位的结果
//a & b表示a和b相加之后,没有进位的地方
//a & b << 1 表示进位之后的结果
//所以:a + b = (a ^ b) + (a & b << 1)
//若a & b << 1等于0,则a ^ b为输出结果
int _a=a^b;
int _b=(a & b) << 1;
return _b==0?_a:aplusb(_a,_b);
}
2、尾部的零
描述:设计一个算法,计算出n阶乘中尾部零的个数。
样例:
输入: 5
输出: 1
样例解释:
5! = 120, 结尾的0有1个。
挑战:O(logN)的时间复杂度
public long trailingZeros(long n) {
//0是由2*5产生的,而5的数量一定小于2的数量
//因此5的个数决定了结尾0的个数
//只要计算n的阶乘中,5这个素因子出现多少次即可
int count=0;
while(n!=0){
count+=n/5;
n/=5;
}
return count;
}
3、统计数字
描述:计算数字 k 在 0 到 n 中的出现的次数,k 可能是 0~9 的一个值
样例:
输入:
k = 1, n = 12
输出:
5
解释:
在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 中,我们发现 1 出现了 5 次 (1, 10, 11, 12)(注意11中有两个1)。
public int digitCounts(int k, int n) {
int count=0;
for(int i=0;i<=n;i++){
count+=singleCounts(k,i);
}
return count;
}
private int singleCounts(int k,int n){
if(n==0&&k==0){
return 1;
}
int cut=0;
while(n!=0){
if(n%10==k)cut++;
n/=10;
}
return cut;
}
4、丑数
描述:设计一个算法,找出只含素因子2
,3
,5
的第 n 小的数。符合条件的数如:1, 2, 3, 4, 5, 6, 8, 9, 10, 12...
- 我们可以认为
1
也是一个丑数
样例:
输入:9
输出:10
挑战:要求时间复杂度为 O(nlogn) 或者 O(n)
public int nthUglyNumber(int n) {
List<Integer> list=new ArrayList<>();
list.add(1);
int p2=0,p3=0,p5=0;
for(int i=0;i<n;++i){
int temp=list.get(i);
while(list.get(p2)*2<=temp)p2++;
while(list.get(p3)*3<=temp)p3++;
while(list.get(p5)*5<=temp)p5++;
list.add(Math.min(list.get(p2)*2,Math.min(list.get(p3)*3,list.get(p5)*5)));
}
return list.get(n-1);
}
5、第K大的数(2019年6月16日)
描述:在数组中找到第 k 大的元素。
- 你可以交换数组中的元素的位置
样例:
输入:n = 1, nums = [1,3,4,2]
输出:4
挑战:要求时间复杂度为O(n),空间复杂度为O(1)。
public int kthLargestElement(int n, int[] nums) {
// write your code here
//第n大的数,index为nums.length-n
//算法:快速排序的变形
if(nums==null||nums.length==0||n<1||n>nums.length){
return -1;
}
return kthLargestElement(nums.length-n,nums,0,nums.length-1);
}
private int kthLargestElement(int k,int[] nums,int start,int end){
int left=start;
int right=end;
int base=nums[left];
while(left<=right){
while(left<=right&&base<nums[right]){
right--;
}
while(left<=right&&base>nums[left]){
left++;
}
if(left<=right){
int temp=nums[left];
nums[left]=nums[right];
nums[right]=temp;
left++;
right--;
}
}
if(k<=right){
return kthLargestElement(k,nums,start,right);
}
if(k>=left){
return kthLargestElement(k,nums,left,end);
}
return nums[k];
}
6、合并排序数组 II
描述:合并两个有序升序的整数数组A和B变成一个新的数组。新数组也要有序。
样例:
输入: A=[1,2,3,4], B=[2,4,5,6]
输出: [1,2,2,3,4,4,5,6]
样例解释: 返回合并后的数组。
挑战:你能否优化你的算法,如果其中一个数组很大而另一个数组很小?
public int[] mergeSortedArray(int[] A, int[] B) {
// write your code here
//算法:快速排序变形
if(A==null||B==null){
return null;
}
if(A.length>=B.length){
return mergeSortedArray(A,B,new int[A.length+B.length]);
}else{
return mergeSortedArray(B,A,new int[A.length+B.length]);
}
}
private int[] mergeSortedArray(int[] A,int[] B,int[] C){
int a=0;//数组A的计数变量
int b=0;//数组B的计数变量
int c=0;//数组C的计数变量
while(b<B.length){
while(a<A.length&&A[a]<=B[b]){
C[c++]=A[a++];
}
C[c++]=B[b++];
}
while(a<A.length){
C[c++]=A[a++];
}
return C;
}
7、二叉树的序列化和反序列化(2019年6月17日)
描述:设计一个算法,并编写代码来序列化和反序列化二叉树。将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。
如何反序列化或序列化二叉树是没有限制的,你只需要确保可以将二叉树序列化为一个字符串,并且可以将字符串反序列化为原来的树结构。
- 对二进制树进行反序列化或序列化的方式没有限制,LintCode 将您的
serialize
输出作为deserialize
的输入,它不会检查序列化的结果。
样例:
输入:{3,9,20,#,#,15,7}
输出:{3,9,20,#,#,15,7}
解释:
二叉树 {3,9,20,#,#,15,7},表示如下的树结构:
3
/ \
9 20
/ \
15 7
它将被序列化为 {3,9,20,#,#,15,7}
我们的数据是进行 BFS 遍历得到的。当你测试结果 Wrong Answer 时,你可以作为输入调试你的代码。
你可以采用其他的方法进行序列化和反序列化。
public String serialize(TreeNode root) {
// write your code here
//二叉树的序列化,即根据给定的root节点,依次写入左节点L1,右节点R1;
//而后依次写入左节点L1对应的左右子节点,接着是右节点R1的左右子节点;
//依次类推
//容器:ArrayList
if(root==null){
return "{}";
}
ArrayList<TreeNode> list=new ArrayList<TreeNode>();
list.add(root);
for(int i=0;i<list.size();++i){
if(list.get(i)==null){
continue;
}
list.add(list.get(i).left);
list.add(list.get(i).right);
}
while(list.get(list.size()-1)==null){
list.remove(list.size()-1);
}
StringBuilder sb=new StringBuilder();
sb.append('{');
sb.append(root.val);
for(int i=1;i<list.size();++i){
TreeNode node=list.get(i);
if(node==null){
sb.append(",#");
}else{
sb.append(',');
sb.append(node.val);
}
}
sb.append('}');
return sb.toString();
}
public TreeNode deserialize(String data) {
// write your code here
//二叉树的反序列化,即解析给定的字符串,
//将字符串中的每一个元素,转化为二叉树对应的一个节点的值
if(data.equals("{}")){
return null;
}
String[] strVals=data.substring(1,data.length()-1).split(",");
ArrayList<TreeNode> list=new ArrayList<TreeNode>();
TreeNode root=new TreeNode(Integer.parseInt(strVals[0]));
list.add(root);
int index=0;
boolean isleftnode=true;
for(int i=1;i<strVals.length;++i){
if(!strVals[i].equals("#")){
TreeNode node=new TreeNode(Integer.parseInt(strVals[i]));
if(isleftnode){
list.get(index).left=node;
}else{
list.get(index).right=node;
}
list.add(node);
}
if(!isleftnode){
index++;
}
isleftnode=!isleftnode;
}
return root;
}
8、旋转字符串(2019年6月20日)
描述:给定一个字符串(以字符数组的形式给出)和一个偏移量,根据偏移量原地
旋转字符串(从左向右旋转)
- offset >= 0;str的长度 >= 0
样例:
输入: str="abcdefg", offset = 3
输出: str = "efgabcd"
样例解释: 注意是原地旋转,即str旋转后为"efgabcd"输入: str="abcdefg", offset = 0
输出: str = "abcdefg"
样例解释: 注意是原地旋转,即str旋转后为"abcdefg"输入: str="abcdefg", offset = 10
输出: str = "efgabcd"
样例解释: 注意是原地旋转,即str旋转后为"efgabcd"
挑战:在数组上原地旋转,使用O(1)的额外空间
public void rotateString(char[] str, int offset) {
// write your code here
if (str == null || str.length == 0)
return;
offset = offset % str.length;
reverse(str, 0, str.length - offset - 1);
reverse(str, str.length - offset, str.length - 1);
reverse(str, 0, str.length - 1);
}
private void reverse(char[] str, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
public void rotateString(char[] str, int offset) {
// write your code her
//挑战:在数组上原地旋转,使用O(1)的额外空间
if(str.length!=0&&offset>=str.length){
offset%=str.length;
}
if(offset==0||str.length==0){
return;
}
rotateString(str,0,str.length-offset,offset);
}
private void rotateString(char[] str,int i,int j,int offset){
for(;i<str.length-offset&&j<str.length;++i,++j){
char temp=str[i];
str[i]=str[j];
str[j]=temp;
}
if(i<str.length-offset){
rotateString(str,i,str.length-offset,offset);
}
if(j<str.length){
rotateString(str,i,j,str.length-j);
}
}
9、Fizz Buzz 问题
描述:给你一个整数n. 从 1 到 n 按照下面的规则打印每个数:
- 如果这个数被3整除,打印
fizz
. - 如果这个数被5整除,打印
buzz
. - 如果这个数能同时被
3
和5
整除,打印fizz buzz
. - 如果这个数既不能被
3
整除也不能被5
整除,打印数字本身
样例:
比如 n =
15
, 返回一个字符串数组:[
"1", "2", "fizz",
"4", "buzz", "fizz",
"7", "8", "fizz",
"buzz", "11", "fizz",
"13", "14", "fizz buzz"
]
挑战:你是否可以只用一个 if
来实现
public List<String> fizzBuzz(int n) {
// write your code here
List<String> list=new ArrayList<String>();
int i=1;
while(i<=n){
while(i<=n&&i%3!=0&&i%5!=0){
list.add(i+"");
i++;
}
while(i<=n&&i%3==0){
if(i%5==0){
list.add("fizz buzz");
}else{
list.add("fizz");
}
i++;
break;
}
while(i<=n&&i%5==0){
list.add("buzz");
i++;
break;
}
}
return list;
}
11、二叉查找树中搜索区间
描述:给定一个二叉查找树和范围[k1, k2]
。按照升序返回给定范围内的节点值。
样例:
输入:{5},6,10
输出:[]
5
它将被序列化为 {5}
没有数字介于6和10之间
输入:{20,8,22,4,12},10,22
输出:[12,20,22]
解释:
20
/ \
8 22
/ \
4 12
它将被序列化为 {20,8,22,4,12}
[12,20,22]介于10和22之间
public ArrayList<Integer> searchRange(TreeNode root, int k1, int k2) {
ArrayList<Integer> results = new ArrayList<Integer>();
helper(root, k1, k2);
return results;
}
//从给定的BST的根节点开始查找,如果当前节点大于k1,就向左子树搜索,
//如果当前节点小于k2,就继续向右子树搜索。如果位于[k1,k2],存入结果。
private void helper(TreeNode root, int k1, int k2) {
if (root == null) {
return;
}
if (root.val > k1) {
helper(root.left, k1, k2);
}
if (root.val >= k1 && root.val <= k2) {
results.add(root.val);
}
if (root.val < k2) {
helper(root.right, k1, k2);
}
}
public List<Integer> searchRange(TreeNode root, int k1, int k2) {
// write your code here
List<Integer> list=new ArrayList<Integer>();
searchRange(root,list,k1,k2);
return list;
}
//前序遍历
private void searchRange(TreeNode node,List<Integer> list,int k1,int k2){
if(node==null){
return;
}
if(k1<=node.val&&node.val<=k2){
list.add(node.val);
}
searchRange(node.left,list,k1,k2);
searchRange(node.right,list,k1,k2);
}
12、带最小值操作的栈
描述:实现一个栈, 支持以下操作:
push(val)
将 val 压入栈pop()
将栈顶元素弹出, 并返回这个弹出的元素min()
返回栈中元素的最小值
要求 O(1) 开销.
- 保证栈中没有数字时不会调用
min()
样例:
输入:
push(1)
min()
push(2)
min()
push(3)
min()
输出:
1
1
1
//空间复杂度仍然是 O(n)
public class MinStack {
LinkedList<Integer> stack1;//栈1,push所有元素
LinkedList<Integer> stack2;//栈2,push最小元素
public MinStack() {
// do intialization if necessary
stack1=new LinkedList<>();
stack2=new LinkedList<>();
}
/*
* @param number: An integer
* @return: nothing
*/
public void push(int number) {
// write your code here
stack1.addFirst(number);
if(stack2.size()==0){
stack2.addFirst(number);
}else{
stack2.addFirst(Math.min(stack2.getFirst(),number));
}
}
/*
* @return: An integer
*/
public int pop() {
// write your code here
stack2.removeFirst();
return stack1.removeFirst();
}
/*
* @return: An integer
*/
public int min() {
// write your code here
return stack2.getFirst();
}
}
13、字符串查找
描述:对于一个给定的 source 字符串和一个 target 字符串,你应该在 source 字符串中找出 target 字符串出现的第一个位置(从0开始)。如果不存在,则返回 -1
。
在面试中我是否需要实现KMP算法?
- 不需要,当这种问题出现在面试中时,面试官很可能只是想要测试一下你的基础应用能力。当然你需要先跟面试官确认清楚要怎么实现这个题。
样例:
输入: source = "source" , target = "target"
输出:-1
样例解释: 如果source里没有包含target的内容,返回-1
输入: source = "abcdabcdefg" ,target = "bcd"
输出: 1
样例解释: 如果source里包含target的内容,返回target在source里第一次出现的位置
挑战:O(n2)的算法是可以接受的。如果你能用O(n)的算法做出来那更加好。(提示:KMP)
public int strStr(String source, String target) {
if (source == null || target == null) {
return -1;
}
for (int i = 0; i < source.length() - target.length() + 1; i++) {
int j = 0;
for (j = 0; j < target.length(); j++) {
if (source.charAt(i + j) != target.charAt(j)) {
break;
}
}
// finished loop, target found
if (j == target.length()) {
return i;
}
}
return -1;
}
public int strStr(String source, String target) {
// Write your code here
if(source==null||target==null)return -1;
char[] s=source.toCharArray();
char[] t=target.toCharArray();
if(t.length==0)return 0;
for(int i=0;i<=s.length-t.length;i++){
int j=0;
for(j=0;j<t.length;j++){
if(s[j+i]!=t[j]){
break;
}
}
if(j==t.length){
return i;
}
}
return -1;
}
14、二分查找(2019年6月21日)
描述:给定一个排序的整数数组(升序)和一个要查找的整数target
,用O(logn)
的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1
。
样例:
样例 1:
输入:[1,4,4,5,7,7,8,9,9,10],1
输出: 0
样例解释:
第一次出现在第0个位置。样例 2:
输入: [1, 2, 3, 3, 4, 5, 10],3
输出: 2
样例解释:
第一次出现在第2个位置
样例 3:
输入: [1, 2, 3, 3, 4, 5, 10],6
输出: -1
样例解释:
没有出现过6, 返回-1
挑战:如果数组中的整数个数超过了2^32,你的算法是否会出错?
public int binarySearch(int[] nums, int target) {
// write your code here
if(nums==null) return -1;
int s=0,e=nums.length-1;
int mid=0;
while(s<e){
mid=(e-s)/2+s;
if(nums[mid]>target){
e=mid-1;
}else if(nums[mid]<target){
s=mid+1;
}else{
e=mid;
}
}
if(nums[s]==target){
return s;
}
return -1;
}
15、全排列(2019年6月21日)
描述:给定一个数字列表,返回其所有可能的排列。
样例:
样例1:
输入:[1]
输出:
[
[1]
]样例2:
输入:[1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
挑战:使用递归和非递归分别解决。
递归解答
public List<List<Integer>> permute(int[] nums) {
// write your code here
List<List<Integer>> resultList=new ArrayList();
if(nums==null||nums.length==0){
resultList.add(new ArrayList());
return resultList;
}
rank(nums,new ArrayList(nums.length),resultList);
return resultList;
}
private static void rank(int[] nums,List<Integer> tempList,List<List<Integer>> resultList){
if(nums.length==0){//一次排列完成
resultList.add(new ArrayList(tempList));
}
for(int i=0;i<nums.length;i++){
tempList.add(nums[i]);
int[] newNums=new int[nums.length-1];//移除已经被排列的那个数
System.arraycopy(nums,0,newNums,0,i);
System.arraycopy(nums,i+1,newNums,i,nums.length-i-1);
rank(newNums,tempList,resultList);//递归排列下一个数
}
//将最后一个数移除,否则会影响循环过程
if(tempList.size()!=0){
tempList.remove(tempList.size()-1);
}
}