劍指offer
一.
Java查找二維數組時,如何確定二維數組的行列長度
確定二維數組行數:int rowLength = array.length;
確定二維數組列數:int colLength = array[0].length;
二。
請實現一個函數,將一個字符串中的每個空格替換成“%20”。例如,當字符串爲We Are Happy.則經過替換之後的字符串爲We%20Are%20Happy。
解法一:
調用自帶函數replaceAll()
解法二:
用新的StringBuffer存儲,每當掃描到空格時就append("%20") ,否則掃到什麼append什麼
三.
List容器的下標從0開始
List ls = new ArrayList();
ls.add("first");
System.out.println(ls.get(0));
非遞歸思路:ArrayList 中有個方法是 add(index,value),可以指定 index 位置插入 value 值
所以我們在遍歷 listNode 的同時將每個遇到的值插入到 list 的 0 位置,最後輸出 listNode 即可得到逆序鏈表
遞歸代碼:
public class Solution { ArrayList<Integer> list = new ArrayList();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode!=null){
printListFromTailToHead(listNode.next);
list.add(listNode.val);
}
return list;
}
}
四.旋轉數組的最小數字
初始想法是順序查找直到出現後一個數比前一個數字小的情況
看了題解後,可以採用時間複雜度更低的二分查找
五.斐波那契數列
遞歸解法並不難,重點是在存儲和遞歸的優化需要好好研究一下。
https://www.nowcoder.com/questionTerminal/c6c7742f5ba7442aada113136ddea0c3?answerType=1&f=discussion
六.跳臺階
變形的斐波那契數列
dp(i) = dp(i-1)+dp(i-2)
七.變態跳臺階
變形的斐波那契數列
f(n) = f(n-1)+f(n-2)+f(n-3)+…
f(n-1) = f(n-2)+f(n-3)+…
上式-下式可得:f(n) = f(n-1)
斐波那契數列的變形掌握不夠熟練!
八.矩陣覆蓋
每次可以橫向擺放覆蓋一行剩餘n-1行
也可以豎向擺放覆蓋兩行剩餘n-2行
f(n) = f(n-1)+f(n-2)
九.二進制中1的個數
原碼:第一位代表符號位。
反碼:在原碼的基礎上,除符號位外,逐位取反。
補碼:反碼+1
直觀思想:將輸入數字與0xffffffff相與,再進行1的計數。
正常思路:將數字右移再與1相與,但負數右移會再右側補1,因此可採用將1左移與數字相與的策略,直到將1移出計數範圍變成0。
更好的思路:將數字-1後再與原數字做"&"運算,每次將原數字-1都會將原數字最右側的1置爲0,並且將該數字右側的所有0轉換爲1,然後再將該轉換後的數字與原數字相與,就相當於每次只把原數字最右側的一個1置爲0,於是原數字有多少個1就可以進行多少次這樣的運算。
public int NumberOf1(int n) {
int count = 0;
while (n != 0) {
count++;
n = n & (n-1);
}
return count;
}
十.數值的整數次方
直觀思路:直接採用循環的方式進行計算,需要考慮指數爲負數的情況,需要將結果取倒數
時間複雜度O(n)。
遞歸想法:n爲偶數,an=an/2 × an/2;
n爲奇數an=(a(n-1)/2)*(a(n-1)/2)*a
時間複雜度O(logn)
十一.反轉鏈表
思路:用三個節點分別存儲當前頭部的前一個節點,當前頭部,頭部的後一個節點。每次將當前頭部的指針指向前部,在將頭部的節點移動到當前頭部,之前的後部節點變爲head節點
public ListNode ReverseList(ListNode head) {
ListNode pre = null;
ListNode next = null;
//pre->head->next
//pre<-head next
//temp<-pre head->next
while (head != null) {
next = head.next;
head.next = pre;
//pre move to the next node
pre = head;
head = next;
}
return pre;
}
十二.合併兩個排序的鏈表
非常經典的一道題!
遞歸解法:
想法較爲簡單,首先判斷當前兩個鏈表是否爲空,若一個爲空則直接返回另一個,再判斷兩鏈表頭節點數值的大小,若 list1 > list2 則將list2.next和list1繼續合併,並將合併後的結果鏈接到list2上
public ListNode Merge(ListNode list1,ListNode list2) {
if (list1 == null) return list2;
else if (list2 == null) return list1;
if (list1.val > list2.val) {
list2.next = Merge(list2.next,list1);
return list2;
}
else {
list1.next = Merge(list1.next,list2);
return list1;
}
}
非遞歸解法:
多次出錯的原因: 搞清楚定義頭節點時應該將哪個部分進行初始化,應該將初始化空間的節點作爲程序中進行操作的節點,後指向該節點的node作爲真正的返回值,在結果處返回。
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode p = new ListNode(0);//就是這個地方!
ListNode ans = p;
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
p.next = list1;
p = p.next;
list1 = list1.next;
}
else {
p.next = list2;
p = p.next;
list2 = list2.next;
}
}
if (list1 != null) p.next = list1;
if (list2 != null) p.next = list2;
return ans.next;
}