持續更新。。。
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);
}
}