劍指offer-Java刷題心得

劍指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;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章