劍指offer第二週

劍指offer第二週

24.機器人的運動範圍

地上有一個 mm 行和 nn 列的方格,橫縱座標範圍分別是 0∼m−10∼m−1 和 0∼n−10∼n−1。

一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格。

但是不能進入行座標和列座標的數位之和大於 kk 的格子。

請問該機器人能夠達到多少個格子?

樣例1

輸入:k=7, m=4, n=5

輸出:20

樣例2

輸入:k=18, m=40, n=40

輸出:1484

解釋:當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。
      但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。

注意:

  1. 0<=m<=50
  2. 0<=n<=50
  3. 0<=k<=100

dfs, 如果機器人當前位置可以去,則去判斷上下左右能不能去;

class Solution {
    int ans = 0;
    boolean[][] vis;
    public int movingCount(int threshold, int rows, int cols){
        boolean[][] vis = new boolean[rows][cols];
        // System.out.println(rows + " " + cols);
        dfs(threshold,0,0,rows,cols);
        return ans;
    }
    public void dfs(int k, int x,int y,int rows,int cols){
        // System.out.println(x + " " + y);
        if(x >= rows || y >= cols || x < 0 || y < 0 || get(x,y) > k || vis[x][y])return ;
        else{
            vis[x][y] = true;
            ans ++;
            dfs(k,x + 1,y,rows,cols);
            dfs(k,x - 1,y,rows,cols);
            dfs(k,x,y + 1,rows,cols);
            dfs(k,x,y - 1,rows,cols);
        }
    }
    
    public int get(int i ,int j){
        int res = 0;
        while(i > 0){
            res += (i % 10);
            i /= 10;
        }
        while(j > 0){
            res += (j % 10);
            j /= 10;
        }
        return res;
    }
}

25.減繩子

給你一根長度爲 nn 繩子,請把繩子剪成 mm 段(mm、nn 都是整數,2≤n≤582≤n≤58 並且 m≥2m≥2)。

每段的繩子的長度記爲k[0]、k[1]、……、k[m]。k[0]k[1] … k[m] 可能的最大乘積是多少?

例如當繩子的長度是8時,我們把它剪成長度分別爲2、3、3的三段,此時得到最大的乘積18。

樣例

輸入:8

輸出:18

暴力,枚舉;

class Solution {
    public int maxProductAfterCutting(int length)
    {
        int maxLen = length / 2; 
        int res = 1;
        for(int i = 2; i <= maxLen; i ++){
            res = Math.max(res, cut(length,i));
        }
        return res;
    }
    // 每次 選取中間的數乘,必定最大;
    public int cut(int length,int cnt){
        int ans = 1;
        while(cnt > 0){
            int avg = length / cnt;
            ans *= avg;
            cnt --;
            length -= avg;
        }
        return ans;
    }
}

y總說小學生題:可我還是暴力做的;

結論:每個數能取3,則取三,否則取2;

證明:N = n1 + n2 + n3 + … nk + …nN;

當n1 >= 5時,必定可以分成 3 * (n1 - 3) > n1;

n1 = 4時 ,n1 = 2*2;

不會有1的情況;

class Solution {
    public int maxProductAfterCutting(int n)
    {
        if(n == 2)return 1;
        int res = 1;
        if(n % 3 == 1){
            res *= 4;
            n -= 4;
        }
        if(n % 3 == 2){
            res *= 2;
            n -= 2;
        }
        while(n >= 3){
            res *= 3;
            n -= 3;
        }
        return res;
    }
}

26.二進制中1的個數

輸入一個32位整數,輸出該數二進制表示中1的個數。

注意

  • 負數在計算機中用其絕對值的補碼來表示。

樣例1

輸入:9
輸出:2
解釋:9的二進制表示是1001,一共有2個1。

樣例2

輸入:-2
輸出:31
解釋:-2在計算機裏會被表示成11111111111111111111111111111110,
      一共有31個1。

樹狀數組中的lowbit,返回最後一個1的位置。(利用計算機中負數的補碼)

也可以利用 x & (x-1) 直接

class Solution {
    public int NumberOf1(int n)
    {
        int res = 0;
        while(n != 0){
            n = n & (n - 1);// 直接把最後一個1剪掉,那麼&的時候就消失啦,看各位看官喜歡哪種;
            // n -= lowbit(n); 同理
            res ++;
        }
        return res;
    }
    public int lowbit(int n){
        return n & (-n);
    }
}

27.數值的整數次方

實現函數double Power(double base, int exponent),求baseexponent次方。

不得使用庫函數,同時不需要考慮大數問題。

注意:

  • 不會出現底數和指數同爲0的情況
  • 當底數爲0時,指數一定爲正

樣例1

輸入:10 ,2

輸出:100

樣例2

輸入:10 ,-2  

輸出:0.01

經典快速冪

class Solution {
    public double Power(double base, int e) {
        double res = 1;
        long mi = e;
        if(e < 0){
            mi = -e;
            base = 1 / base;
        }
        while(mi > 0){
            if((mi & 1) == 1) res *= base;
            base *= base;
            mi >>= 1;
        }
        return res;
   } 
}

28.在O(1)時間刪除鏈表結點

給定單向鏈表的一個節點指針,定義一個函數在O(1)時間刪除該結點。

假設鏈表一定存在,並且該節點一定不是尾節點。

樣例

輸入:鏈表 1->4->6->8
      刪掉節點:第2個節點即6(頭節點爲第0個節點)

輸出:新鏈表 1->4->8

由於此題爲單鏈表,無法得到前驅節點,故要刪除此節點,將下一個節點的值賦給自己,並且刪除下一個節點的值

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

29.刪除鏈表中重複的節點

在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留。

樣例1

輸入:1->2->3->3->4->4->5

輸出:1->2->5

樣例2

輸入:1->1->1->2->3

輸出:2->3

本菜雞,pre,cur,next利用三節點刪除代碼。(冗餘)

class Solution {
    public ListNode deleteDuplication(ListNode head) {
        ListNode dump = new ListNode(-1);
        dump.next = head;
        ListNode temp = head,pre = dump;
        while(temp != null ){
            ListNode cur = temp.next;
            if(cur != null && temp.val == cur.val){
                while(cur != null && temp.val == cur.val){
                    cur = cur.next;
                }
                pre.next = cur;
                // pre = temp;
                temp = cur;
            }else{
                pre = temp;
                temp = cur;
            }
        }
        return dump.next;
    }
}

y總整潔代碼,以啞節點代替pre,判斷next走了多少步,如果只走一步,則代表不用刪,如果多走了則必定有相同元素,刪;

class Solution {
    public ListNode deleteDuplication(ListNode head) {
        ListNode dump = new ListNode(-1);
        dump.next = head;
        ListNode temp = dump;
        while(temp.next != null ){
            ListNode cur = temp.next;
            while(cur != null && temp.next.val == cur.val){
                cur = cur.next;
            }
            if(temp.next.next == cur)temp = temp.next;
            else temp.next = cur;
        }
        return dump.next;
    }
}

32.調整數組順序使奇數位於偶數前面

輸入一個整數數組,實現一個函數來調整該數組中數字的順序。

使得所有的奇數位於數組的前半部分,所有的偶數位於數組的後半部分。

樣例

輸入:[1,2,3,4,5]

輸出: [1,3,5,2,4]

利用快排的原理

class Solution {
    public void reOrderArray(int [] array) {
        int i = 0, j = array.length - 1;
        while(i < j){
            while(i < j && (array[i] & 1) == 1)i++;
            while(j > i && (array[j] & 1) == 0)j--;
            if(i < j){
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
    }
}

33.鏈表中倒數第k個節點

輸入一個鏈表,輸出該鏈表中倒數第k個結點。

注意:

  • k >= 0;
  • 如果k大於鏈表長度,則返回 NULL;

樣例

輸入:鏈表:1->2->3->4->5 ,k=2

輸出:4

經典快慢指針

class Solution {
    public ListNode findKthToTail(ListNode head, int k) {
        ListNode slow = head,fast = head;
        while(k-- > 0){
            if(fast == null)return null;
            fast = fast.next;
        }
        while(fast != null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

34.鏈表中環的入口節點

給定一個鏈表,若其中包含環,則輸出環的入口節點。

若其中不包含環,則輸出null

樣例

QQ截圖20181202023846.png

給定如上所示的鏈表:
[1, 2, 3, 4, 5, 6]
2
注意,這裏的2表示編號是2的節點,節點編號從0開始。所以編號是2的節點就是val等於3的節點。

則輸出環的入口節點3.

由ListNode結構體得出,此鏈爲單鏈表,單鏈表最多隻會有一個環,故可判斷,當一個點被重複走過時,此點爲環的入口(入口會被最先經過兩次)

故:哈希做法 時間複雜度O(n),空間O(n)

class Solution {
    Set<ListNode> set = new HashSet<>();
    public ListNode entryNodeOfLoop(ListNode head) {
        ListNode temp = head;
        while(temp != null){
            if(set.isEmpty() || !set.contains(temp))set.add(temp);
            else return temp;
            temp = temp.next;
        }
        return null;
    }
}

最優解:雙鏈表 時間複雜度O(n),空間O(1)

假設它們相遇的時候爲y,起點到環入口爲x,那麼快指針走了2*(x+y) 慢指針走(x+y)

得出快指針在環內走x+2y ,減去離環起點的距離y,故x+y 一定是環的整數倍

所以從相遇點c,再走x步,一定能到起點b;

https://www.acwing.com/media/article/image/2019/01/06/1_54311a0411-QQ%E5%9B%BE%E7%89%8720180531162503.png

class Solution {
    public ListNode entryNodeOfLoop(ListNode head) {
        ListNode slow = head,fast = head;
        while(fast != null && slow != null){
            slow = slow.next;
            fast = fast.next;
            // 防止 無環情況下 NullPointerException
            if(fast != null)fast = fast.next;
            else return null;
            // 當成環,讓fast 再走 x步,回到起點(x+y)是圈的整數倍
            if(slow == fast){
                slow = head;
                while(slow != fast){
                    slow = slow.next;
                    fast = fast.next;
                }
                return slow;
            }
        }
        return null;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章