劍指offer第六週(完)

劍指offer第六週

79. 滑動窗口的最大值

給定一個數組和滑動窗口的大小,請找出所有滑動窗口裏的最大值。

例如,如果輸入數組[2, 3, 4, 2, 6, 2, 5, 1]及滑動窗口的大小3,那麼一共存在6個滑動窗口,它們的最大值分別爲[4, 4, 6, 6, 6, 5]。

注意:

  • 數據保證k大於0,且k小於等於數組長度。
樣例
輸入:[2, 3, 4, 2, 6, 2, 5, 1] , k=3

輸出: [4, 4, 6, 6, 6, 5]

單調隊列模板 — 注意放的是下標

class Solution {
    public int[] maxInWindows(int[] nums, int k) {
        List<Integer> list = new ArrayList<>();
        ArrayDeque<Integer> q = new ArrayDeque<>();
        for(int i = 0; i < nums.length; i++){   
            // 當 隊尾的數 小於或等於 當前數nums[i]時,則 隊尾的數字就沒有用了,Poll();
            while(!q.isEmpty() && nums[q.peekLast()] <= nums[i])q.pollLast();
            // 如果 隊列的 大小大於等於k, 則代表隊首位置的數 畢業了;
            while(!q.isEmpty() && i - q.peek() >= k)q.poll();
            q.offer(i);
            if(i >= k - 1)list.add(nums[q.peek()]);
        }
        int[] res = new int[list.size()];
        for(int i = 0; i < list.size(); i++)res[i] = list.get(i);
        return res;
    }
}

80. 骰子的點數

將一個骰子投擲n次,獲得的總點數爲s,s的可能範圍爲n~6n。

擲出某一點數,可能有多種擲法,例如投擲2次,擲出3點,共有[1,2],[2,1]兩種擲法。

請求出投擲n次,擲出n~6n點分別有多少種擲法。

樣例1

輸入:n=1

輸出:[1, 1, 1, 1, 1, 1]

解釋:投擲1次,可能出現的點數爲1-6,共計6種。每種點數都只有1種擲法。所以輸出[1, 1, 1, 1, 1, 1]。

樣例2

輸入:n=2

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

解釋:投擲2次,可能出現的點數爲2-12,共計11種。每種點數可能擲法數目分別爲1,2,3,4,5,6,5,4,3,2,1。

      所以輸出[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]。

f【i】【j】 表示 扔i次 得到點數j的次數

第j次丟的點數是由j - 1丟的點數來的;

f[i][j] += f[i - 1][j - k];
class Solution {
    public int[] numberOfDice(int n) {
        int[][] f = new int[n + 1][6 * n + 1];
        f[0][0] = 1;
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= 6 * n; j ++)
                for(int k = 1; k <= Math.min(j,6); k++)
                    f[i][j] += f[i - 1][j - k];
        int[] res = new int[6 * n - n + 1];
        for(int i = n; i <= 6 * n; i ++)res[i - n] = f[n][i];
        
        return res;
    }
}

81. 撲克牌的順子

從撲克牌中隨機抽5張牌,判斷是不是一個順子,即這5張牌是不是連續的。

2~10爲數字本身,A爲1,J爲11,Q爲12,K爲13,大小王可以看做任意數字。

爲了方便,大小王均以0來表示,並且假設這副牌中大小王均有兩張。

樣例1

輸入:[8,9,10,11,12]

輸出:true

樣例2

輸入:[0,8,9,11,12]

輸出:true

排序,如果裏面有相同的數返回false,如果沒有,查看第一個不爲0的數與最大的數之間的差是否在4以內即可,大王可以隨意變換數字;

class Solution {
    public boolean isContinuous(int [] nums) {
        Arrays.sort(nums);
        for(int i = 1; i < nums.length; i ++)if(nums[i] > 0 && nums[i] == nums[i - 1])return false;
        
        for(int num : nums)
            if(num > 0) return nums[nums.length - 1] - num <= 4;
            
        return false;
    }
}

82.圓圈中最後剩下的數字

0, 1, …, n-1這n個數字(n>0)排成一個圓圈,從數字0開始每次從這個圓圈裏刪除第m個數字。

求出這個圓圈裏剩下的最後一個數字。

樣例

輸入:n=5 , m=3

輸出:3

反推,當最後面只剩他一個人的時候,他必定在第0個位置上;

當只剩兩人的時候,他的位置now在 now = pre(前一個位置) + m(數多少個數) % i (當前總人數)

也就是說在其位置前加m個人,則每次都不會選到它;

最後推出總人數爲n時,now當前的序號

class Solution {
    // now = pre(前一個位置) + m(數多少個數) % i (當前總人數)
    public int lastRemaining(int n, int m) {
        int pre = 0;
        for(int i = 2; i <= n; i ++){
            pre = (pre + m) % i;
        }
        return pre;
    }
}

83. 股票的最大利潤

假設把某股票的價格按照時間先後順序存儲在數組中,請問買賣 一次 該股票可能獲得的利潤是多少?

例如一隻股票在某些時間節點的價格爲[9, 11, 8, 5, 7, 12, 16, 14]。

如果我們能在價格爲5的時候買入並在價格爲16時賣出,則能收穫最大的利潤11。

樣例

輸入:[9, 11, 8, 5, 7, 12, 16, 14]

輸出:11

貪心:動態記錄最小值,將後面的值 - 最小值,來更新最大利潤

class Solution {
    public int maxDiff(int[] nums) {
        if(nums.length < 2) return 0;
        int minn = nums[0], n = nums.length, res = 0;
        for(int i = 1; i < n; i++){
            res = Math.max(res,nums[i] - minn);
            minn = Math.min(minn,nums[i]);
        }
        return res;
    }
}

84.求1+2+…+n

求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。

樣例

輸入:10

輸出:55

等差數列求和

class Solution {
    public int getSum(int n) {
        long temp = (1l + n) * n;
        return (int) (temp / 2);
    }
}

題目說不能用乘除法,故用遞歸

class Solution {
    public int getSum(int n) {
        return dfs(n);
    }
    
    public int dfs(int n){
        if(n == 0)return 0;
        int res = n;
        res += dfs(n - 1);
        return res;
    }
}

85. 不用加減乘除做加法

寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、×、÷ 四則運算符號。

樣例

輸入:num1 = 1 , num2 = 2

輸出:3
class Solution {
    // num2 k 0;
    // 左移後 至少 k + 1 0;
    // 異或 ^ 1 1 = 0, 1 0 = 1, 0 0 = 0,  相當於不進位加法
    //  & 當 兩個都是1時  進位, 再左移 相當於 進位的數,相加則爲 原來的數
    public int add(int num1, int num2) {
        while(num2 > 0){
            int sum = num1 ^ num2;
            num2 = (num1 & num2) << 1;
            num1 = sum;
        }
        return num1;
    }
}

86. 構建乘積數組

給定一個數組A[0, 1, …, n-1],請構建一個數組B[0, 1, …, n-1],其中B中的元素B[i]=A[0]×A[1]×… ×A[i-1]×A[i+1]×…×A[n-1]

不能使用除法。

樣例

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

輸出:[120, 60, 40, 30, 24]

思考題

  • 能不能只使用常數空間?(除了輸出的數組之外)

先枚舉前一半,然後後一半

class Solution {
    public int[] multiply(int[] A) {
        int[] res = new int[A.length];
        for(int i = 0, p = 1; i < A.length; i ++){
            res[i] = p;
            p *= A[i];
        }
        
        for(int i = A.length - 1,p = 1; ~i != 0; i --){
            res[i] *= p;
            p *= A[i];
        }
        return res;
    }
}

87. 把字符串轉換成整數

請你寫一個函數StrToInt,實現把字符串轉換成整數這個功能。

當然,不能使用atoi或者其他類似的庫函數。

樣例

輸入:"123"

輸出:123

注意:

你的函數應滿足下列條件:

  1. 忽略所有行首空格,找到第一個非空格字符,可以是 ‘+/−’ 表示是正數或者負數,緊隨其後找到最長的一串連續數字,將其解析成一個整數;
  2. 整數後可能有任意非數字字符,請將其忽略;
  3. 如果整數長度爲0,則返回0;
  4. 如果整數大於INT_MAX(2^31 − 1),請返回INT_MAX;如果整數小於INT_MIN(−2^31) ,請返回INT_MIN;

模擬題

class Solution {
    public int strToInt(String str) {
        int maxx = Integer.MAX_VALUE, minn = Integer.MIN_VALUE;
        if(str.length() == 0) return 0;
        // res 答案, flag 記錄符號,digit記錄 res的長度
        int res = 0, flag = 1,digit = 0;
        // 去掉首位空字符串
        str = str.trim();
        // 判斷是否是 非字符串開始,如果是直接break
        boolean start = false;
        for(int i = 0; i < str.length(); i ++){
            if(str.charAt(i) == '-'){flag = -1;continue;}
            if(str.charAt(i) == '+')continue;
            if(str.charAt(i) > '9' || str.charAt(i) < '0'){
                if(start)continue;
                else break;
            }
            // 接下來要加的數字
            int bit = (str.charAt(i) - '0');
            if(flag == 1){
                if((res < maxx / 10 || digit == 9 && bit <= 7 && res / 1000000000 <= 2))res = res * 10 + bit;
                else return maxx;
            }else{
                if((res < maxx / 10 || digit == 9 && bit <= 8 && res / 1000000000 <= 2))res = res * 10 + bit;
                else return minn;
            }
            digit ++;
            start = true;
        }
        return res * flag;
    }
}

88. 樹中兩個結點的最低公共祖先

給出一個二叉樹,輸入兩個樹節點,求它們的最低公共祖先。

一個樹節點的祖先節點包括它本身。

注意:

  • 輸入的二叉樹不爲空;
  • 輸入的兩個節點一定不爲空,且是二叉樹中的節點;

樣例

二叉樹[8, 12, 2, null, null, 6, 4, null, null, null, null]如下圖所示:
    8
   / \
  12  2
     / \
    6   4

1. 如果輸入的樹節點爲2和12,則輸出的最低公共祖先爲樹節點8。

2. 如果輸入的樹節點爲2和6,則輸出的最低公共祖先爲樹節點2。

從根開始尋找左右子樹,如果發現某棵樹就是要找的p,或者q,則直接返回p,q如果左邊找到 p,q 右邊沒找到,那麼左邊的就是最近祖先,如果都找到則返回其公共祖先,root,如果都沒找到,返回null;

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null)return null;
        if(root == p || root == q)return root;
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        
        if(left != null && right != null) return root;
        if(left == null && right != null)return right;
        if(right == null && left != null)return left;
        return null;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章