第一題:
在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
????這是非常簡單的一道題,最常規的一種方式就是兩個方向上掃描,但是要保證其中一個掃描方向數字是遞增的,而另外一個方向上數字是遞減的。
????下面給出我的解法,從矩陣中的右上角開始,從右往左掃,當橫向當前數的下一個數小于目標數的時候往下少,找到返回true,越界返回false。
public class Solution {
public boolean Find(int target, int [][] array) {
for(int i = array[0].length-1; i >= 0; i--) {
if(array[0][i] <= target) {
for(int j = 0; j < array.length; j++) {
if(array[j][i] == target) return true;
}
}
}
return false;
}
}
第二題
請實現一個函數,將一個字符串中的空格替換成“%20”。例如,當字符串為We Are Happy.則經過替換之后的字符串為We%20Are%20Happy。
????這題不解釋了,太簡單,很多編程語言直接用字符串處理函數就能做了,用正則也能做,當然,題目本身可能并不希望你這么干。
public class Solution {
public String replaceSpace(StringBuffer str) {
char[] arr = str.toString().toCharArray();
int len = arr.length;
int blankCount = 0;
for(int i = 0; i < len; i++) {
if(arr[i] == ' ')
blankCount++;
}
int[] res = new int[len+blankCount];
int j = 0;
for(int i = 0; i < len; i++) {
if(arr[i] == ' ') {
res[j++] = '%';
res[j++] = '2';
res[j++] = '0';
} else {
arr[j++] = arr[i];
}
}
StringBuffer sb = new StringBuffer();
for(int i = 0; i < res.length; i++) {
sb.append(res[i]);
}
return sb.toString();
}
}
第三題
輸入一個鏈表,從尾到頭打印鏈表每個節點的值。
????最簡單的思路就是用棧倒序輸出。
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
Stack<Integer> stack = new Stack<>();
while(listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}
while(!stack.isEmpty()) {
list.add(stack.pop());
}
return list;
}
}
第四題
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹并返回。
????總算來了個比較有意思的題目。給你前序和中序,讓你重構二叉樹返回。我們直接按題目中給的例子來講。
????前序遍歷是{1,2,4,7,3,5,6,8}
,也就是說 {1}
這個數字就是根。而中序遍歷是{4,7,2,1,5,3,8,6}
,從而可以劃出兩個子集{4,7,2,1}
{5,3,8,6}
分別為組成以{1}
為根的左右子樹。
????依照這個邏輯下去,就可以得出整棵樹的結構了。這顯然就是一些重復步驟,用遞歸去做是最好的方式。再進一步思考,我們只需要知道前序和中序遍歷的邊界下標,就能在遞歸過程中計算出我們要得到的數據。接下來,讓我們直接看代碼。
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
return rebuilder(pre, in, 0, pre.length-1, 0, in.length-1);
}
public TreeNode rebuilder(int[] pre, int[] in, int prel, int prer, int inl, int inr) {
if(prel > prer) return null;
TreeNode node = new TreeNode(pre[prel]);
for(int i = inl; i <= inr; i++) {
if(in[i] == pre[prel]) {
node.left = rebuilder(pre, in, prel+1, prel+i-inl, inl, i-1);
node.right = rebuilder(pre, in, prel+i-inl+1, prer, i+1, inr);
break;
}
}
return node;
}
}
第五題
用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素為int類型。
????也是非常簡單的一題。要用兩個棧模擬隊列,我們只需要保持兩個原則。第一,當你要push一個元素到其中一個棧時候,另外一個棧必須為空。第二,當你要pop一個元素的時候,必須把棧中的所有元素傾倒到另外一個空棧,然后再pop數據出去。
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
while(!stack2.isEmpty()) {
stack1.push(stack2.pop());
}
stack1.push(node);
}
public int pop() {
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
第六題
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。 輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。 NOTE:給出的所有元素都大于0,若數組大小為0,請返回0。
????咋看之下,這題似乎掃描一遍數組就行了。沒錯,確實如此,但是題目本身是希望你用二分去做的,不然給你非遞減這個特性就沒有意義了。題目說,非遞減數列做旋轉,旋轉之后其實主要有兩種情況,一種就是旋轉后和旋轉前一樣,一種是旋轉后和選擇前發生了變化(這里暫時忽略數列中過的數全部相等的情況),第一種情況,數列的最后一個數一定是大于第一個數的。第二種情況,最小的數一定在數列中。而這個數列也會分成兩個遞增的子數列,且兩個子序列相連的邊界數字一定是遞減的,比如:{3,4,5}
{1, 2}
兩個遞增數列,5和1這兩個邊界是下降的,從而我們可以得到二分的條件,假設:當前位置為i
,那么當a[i-1] > a[i]
時, i
就是最小的數。
public class Solution {
public int minNumberInRotateArray(int [] array) {
return binarySearch(array, 0, array.length-1);
}
/**
大體上就是一個二分搜索,注意搜索條件即可
*/
public int binarySearch(int[] arr, int l, int r) {
if (arr[0] < arr[arr.length - 1])
return arr[0];
int mid = (l + r) / 2;
// 這一句就是為了覆蓋數列數字全部相等的情況
if(mid == r) return arr[r];
if (arr[mid] < arr[mid - 1])
return arr[mid];
int res = 0;
if(l <= r) {
if (arr[mid] < arr[l])
res = binarySearch(arr, l, mid);
else
res = binarySearch(arr, mid, r);
}
return res;
}
}
第七題
大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項。
n<=39
????這題沒啥好講的,為了避免爆棧,應該采用迭代的方式。
public class Solution {
public int Fibonacci(int n) {
if(n == 0)
return 0;
if(n == 1)
return 1;
int count = 1;
Queue<Integer> queue = new LinkedList<Integer>();
queue.add(0);
queue.add(1);
int res = -1;
while(count != n) {
res = queue.poll() + queue.peek();
queue.add(res);
count++;
}
return res;
}
}
第八題
一只青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
????這題考察的就是一個遞推的是個思想。假設i
表示青蛙要跳上的臺階,當i==1
時,只有一種方法;當i==2
時,可以從上一個臺階,或上上一個臺階跳上去,所有有兩種方式,從而可以知道,當i > 1
時,res = step1 + step2
(這里的step1和step2分別表示跳到上一個臺階和跳到上上一個臺階的跳法)。
public class Solution {
public int JumpFloor(int target) {
if(target == 1)
return 1;
if(target == 2)
return 2;
int result = 0;
int step1 = 1, step2 = 1;
target -= 1;
while(target-- != 0) {
result = step1 + step2;
step1 = step2;
step2 = result;
}
return result;
}
}
第九題
一只青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
????這道題,最簡單最快速的方法,就是直接用排列組合去做。把n個臺階分成1部分、2部分、3...n部分,c(N,0)+c(N,1)+...+C(N,N) = 2^N。
public class Solution {
public int JumpFloorII(int target) {
return (int)Math.pow(2, target-1);
}
}
第十題
我們可以用21的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個21的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?
????這道題的思路其實和第八題非常類似,假設n=5,我們可以看成n=3和n=4兩種情況,但是注意:n=3時,剩下2個小矩陣,所以還可以排成2種,但是有其中一種(豎著排列的)會與n=3時候的重復,所以其實也只能排一種。所以我們可以知道:當 n==1
時 res = 1;當n==2
時,res=2,當n>2
時,res = cnt[n-1] + cnt[n-2]。
public class Solution {
public int RectCover(int target) {
if(target == 0)
return 0;
if(target == 1)
return 1;
if(target == 2)
return 2;
return RectCover(target-1) + RectCover(target-2);
}
}