Leetcode劍指offer(八)

波仔頭發表於2020-10-30

61、和為s的連續正數序列(57-2、Easy)

1)題目要求

輸入一個正整數 target ,輸出所有和為 target 的連續正整數序列(至少含有兩個數)。

序列內的數字由小到大排列,不同序列按照首個數字從小到大排列。

示例 1:

輸入:target = 9
輸出:[[2,3,4],[4,5]]
示例 2:

輸入:target = 15
輸出:[[1,2,3,4,5],[4,5,6],[7,8]]

限制:

1 <= target <= 10^5

2)我的解法

1、超時

class Solution {
    public int[][] findContinuousSequence(int target) {
        double sum=0;
        int l=0,r=0;

        List<int[]> temp=new ArrayList<>();

        for(int i=1;i<target/2+1;i++){
            l=i;
            r=target/2+1;
            while(l<r){
                sum=(l+r)*(r-l+1)/2.0;

                if(sum==target){
                    int[] tem=new int[r-l+1];
                    int index=0;
                    for(int k=l;k<=r;k++)tem[index++]=k;
                    temp.add(tem);
                    break;
                }
                else if(sum>target)r--;
                else break;
            }
        }

        int[][] res=new int[temp.size()][];
        int index=0;
        for(int[] shuzu:temp){
            res[index++]=shuzu;
        }
        return res;
    }
}

2、滑動視窗

class Solution {
    public int[][] findContinuousSequence(int target) {
        double sum=0;
        int l=1,r=2;

        List<int[]> temp=new ArrayList<>();

        for(int i=1;i<target/2+1;i++){
            l=i;
            while(l<r){
                sum=(l+r)*(r-l+1)/2.0;

                if(sum==target){
                    int[] tem=new int[r-l+1];
                    int index=0;
                    for(int k=l;k<=r;k++)tem[index++]=k;
                    temp.add(tem);
                    r++;
                    break;
                }
                else if(sum<target)r++;
                else break;
            }
        }

        int[][] res=new int[temp.size()][];
        int index=0;
        for(int[] shuzu:temp){
            res[index++]=shuzu;
        }
        return res;
    }
}

3)其他解法

1、

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

public int[][] findContinuousSequence(int target) {
    int i = 1; // 滑動視窗的左邊界
    int j = 1; // 滑動視窗的右邊界
    int sum = 0; // 滑動視窗中數字的和
    List<int[]> res = new ArrayList<>();

    while (i <= target / 2) {
        if (sum < target) {
            // 右邊界向右移動
            sum += j;
            j++;
        } else if (sum > target) {
            // 左邊界向右移動
            sum -= i;
            i++;
        } else {
            // 記錄結果
            int[] arr = new int[j-i];
            for (int k = i; k < j; k++) {
                arr[k-i] = k;
            }
            res.add(arr);
            // 左邊界向右移動
            sum -= i;
            i++;
        }
    }

    return res.toArray(new int[res.size()][]);
}

作者:nettee
連結:link
來源:力扣(LeetCode)

2、數學解法

減1,看能不能分成兩個數

減2,看能不能分成3個數,

以此類推

class Solution {
    public int[][] findContinuousSequence(int target) {
        
        List<int[]> result = new ArrayList<>();
        int i = 1;

        while(target>0)
        {
            target -= i++;
            if(target>0 && target%i == 0)
            {
                int[] array = new int[i];
                for(int k = target/i, j = 0; k < target/i+i; k++,j++)
                {
                    array[j] = k;
                }
                result.add(array);
            }
        }
        Collections.reverse(result);
        return result.toArray(new int[0][]);       
    }
}

作者:VaporMax
連結:link
來源:力扣(LeetCode)

4)自己的優化程式碼

class Solution {
    public int[][] findContinuousSequence(int target) {
        double sum=0;
        int l=1,r=2;

        List<int[]> temp=new ArrayList<>();

            while(l<target/2+1){
                sum=(l+r)*(r-l+1)/2.0;

                if(sum==target){
                    int[] tem=new int[r-l+1];
                    int index=0;
                    for(int k=l;k<=r;k++)tem[index++]=k;
                    temp.add(tem);
                    l++;r++;
                }
                else if(sum<target)r++;
                else l++;
            }

    
        return temp.toArray(new int[temp.size()][]);
    }
}

5)學到的東西

滑動視窗

62、翻轉單詞順序(58-1、Easy)

1)題目要求

輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字元的順序不變。為簡單起見,標點符號和普通字母一樣處理。例如輸入字串"I am a student. “,則輸出"student. a am I”。

示例 1:

輸入: “the sky is blue”
輸出: “blue is sky the”
示例 2:

輸入: " hello world! "
輸出: “world! hello”
解釋: 輸入字串可以在前面或者後面包含多餘的空格,但是反轉後的字元不能包括。
示例 3:

輸入: “a good example”
輸出: “example good a”
解釋: 如果兩個單詞間有多餘的空格,將反轉後單詞間的空格減少到只含一個。

說明:

無空格字元構成一個單詞。
輸入字串可以在前面或者後面包含多餘的空格,但是反轉後的字元不能包括。
如果兩個單詞間有多餘的空格,將反轉後單詞間的空格減少到只含一個。

2)我的解法

class Solution {
    public String reverseWords(String s) {
        String[] words=s.split("\\s+");
        StringBuilder sb=new StringBuilder();
        for(int i=words.length-1;i>=0;i--)sb.append(words[i]+" ");
        return sb.toString().trim();
    }
}

3)其他解法

1、

在這裡插入圖片描述

class Solution {
    public String reverseWords(String s) {
        s = s.trim(); // 刪除首尾空格
        int j = s.length() - 1, i = j;
        StringBuilder res = new StringBuilder();
        while(i >= 0) {
            while(i >= 0 && s.charAt(i) != ' ') i--; // 搜尋首個空格
            res.append(s.substring(i + 1, j + 1) + " "); // 新增單詞
            while(i >= 0 && s.charAt(i) == ' ') i--; // 跳過單詞間空格
            j = i; // j 指向下個單詞的尾字元
        }
        return res.toString().trim(); // 轉化為字串並返回
    }
}

2、

在這裡插入圖片描述

在這裡插入圖片描述

class Solution {
    public String reverseWords(String s) {
        String[] strs = s.trim().split(" "); // 刪除首尾空格,分割字串
        StringBuilder res = new StringBuilder();
        for(int i = strs.length - 1; i >= 0; i--) { // 倒序遍歷單詞列表
            if(strs[i].equals("")) continue; // 遇到空單詞則跳過
            res.append(strs[i] + " "); // 將單詞拼接至 StringBuilder
        }
        return res.toString().trim(); // 轉化為字串,刪除尾部空格,並返回
    }
}

作者:jyd
連結: link
來源:力扣(LeetCode)

4)自己的優化程式碼

1、

class Solution {
    public String reverseWords(String s) {
        StringBuilder sb=new StringBuilder();

        int i=s.length()-1,j=s.length()-1;

        while(i>=0&&j>=0){
            while(j>=0&&s.charAt(j)==' ')j--;
            if(j<0)break;
            i=j;
            while(i>=0&&s.charAt(i)!=' ')i--;

            sb.append(" "+s.substring(i+1,j+1));

            j=i;
        }

        return sb.toString().trim();
    }
}

2、

class Solution {
    public String reverseWords(String s) {
        String[] words = s.split(" ");
        StringBuilder sb = new StringBuilder();
        for(int i = words.length - 1; i >= 0; i--){
            if(words[i].equals("")) continue;
            sb.append(words[i]).append(" ");
        }
        return sb.toString().trim();
    }
}

5)學到的東西

s.substring(start,end) [start,end)

split中的正規表示式越複雜,效率越低

63、左旋轉字串(58-2、Easy)

1)題目要求

字串的左旋轉操作是把字串前面的若干個字元轉移到字串的尾部。請定義一個函式實現字串左旋轉操作的功能。比如,輸入字串"abcdefg"和數字2,該函式將返回左旋轉兩位得到的結果"cdefgab"。

示例 1:

輸入: s = “abcdefg”, k = 2
輸出: “cdefgab”
示例 2:

輸入: s = “lrloseumgh”, k = 6
輸出: “umghlrlose”

限制:

1 <= k < s.length <= 10000

2)我的解法

1、

class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n,s.length())+s.substring(0,n);
    }
}

2、

class Solution {
    public String reverseLeftWords(String s, int n) {
        return (s+s).substring(n,s.length()+n);
    }
}

3)其他解法

1、StringBuilder

class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder res = new StringBuilder();
        for(int i = n; i < s.length(); i++)
            res.append(s.charAt(i));
        for(int i = 0; i < n; i++)
            res.append(s.charAt(i));
        return res.toString();
    }
}

利用求餘運算,可以簡化程式碼。

class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder res = new StringBuilder();
        for(int i = n; i < n + s.length(); i++)
            res.append(s.charAt(i % s.length()));
        return res.toString();
    }
}

2、只能用String的話

class Solution {
    public String reverseLeftWords(String s, int n) {
        String res = "";
        for(int i = n; i < s.length(); i++)
            res += s.charAt(i);
        for(int i = 0; i < n; i++)
            res += s.charAt(i);
        return res;
    }
}

利用求餘運算,可以簡化程式碼。

class Solution {
    public String reverseLeftWords(String s, int n) {
        String res = "";
        for(int i = n; i < n + s.length(); i++)
            res += s.charAt(i % s.length());
        return res;
    }
}

作者:jyd
連結:link
來源:力扣(LeetCode)

4)自己的優化程式碼

class Solution {
    public String reverseLeftWords(String s, int n) {
        return (s+s).substring(n,s.length()+n);
    }
}

效率:substring> stringbuilder> +

在這裡插入圖片描述

5)學到的東西

substring原始碼

public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

	
	public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

64、滑動視窗的最大值(59-1、Easy)

1)題目要求

給定一個陣列 nums 和滑動視窗的大小 k,請找出所有滑動視窗裡的最大值。

示例:

輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:

滑動視窗的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

提示:

你可以假設 k 總是有效的,在輸入陣列不為空的情況下,1 ≤ k ≤ 輸入陣列的大小。

2)我的解法

1、暴力

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length==0)return new int[0];
        int[] result=new int[nums.length-k+1];

        int i=0;
        while(i<nums.length-k+1){
            result[i]=Integer.MIN_VALUE;
            for(int j=i;j<k+i;j++)result[i]=Math.max(result[i],nums[j]);
            i++;
        }

        return result;
    }
}

3)其他解法

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        for(int j = 0, i = 1 - k; j < nums.length; i++, j++) {
            if(i > 0 && deque.peekFirst() == nums[i - 1])
                deque.removeFirst(); // 刪除 deque 中對應的 nums[i-1]
            while(!deque.isEmpty() && deque.peekLast() < nums[j])
                deque.removeLast(); // 保持 deque 遞減
            deque.addLast(nums[j]);
            if(i >= 0)
                res[i] = deque.peekFirst();  // 記錄視窗最大值
        }
        return res;
    }
}

可以將 “未形成視窗” 和 “形成視窗後” 兩個階段拆分到兩個迴圈裡實現。程式碼雖變長,但減少了冗餘的判斷操作。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        for(int i = 0; i < k; i++) { // 未形成視窗
            while(!deque.isEmpty() && deque.peekLast() < nums[i])
                deque.removeLast();
            deque.addLast(nums[i]);
        }
        res[0] = deque.peekFirst();
        for(int i = k; i < nums.length; i++) { // 形成視窗後
            if(deque.peekFirst() == nums[i - k])
                deque.removeFirst();
            while(!deque.isEmpty() && deque.peekLast() < nums[i])
                deque.removeLast();
            deque.addLast(nums[i]);
            res[i - k + 1] = deque.peekFirst();
        }
        return res;
    }
}

作者:jyd
連結: link
來源:力扣(LeetCode)

4)自己的優化程式碼

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length==0)return new int[0];
        int[] result=new int[nums.length-k+1];  

        Deque<Integer> de=new LinkedList<>();

        for(int i=0;i<k;i++){//第一個視窗
            while(!de.isEmpty()&&de.peekLast()<nums[i])de.removeLast();
            de.offerLast(nums[i]);
        }
        result[0]=de.peekFirst();

        for(int i=k;i<nums.length;i++){//之後的視窗
            if(de.peekFirst()==nums[i-k])de.removeFirst();//如果之前那個視窗的最大值在視窗的最左邊,則把它去掉

            while(!de.isEmpty()&&de.peekLast()<nums[i])de.removeLast();
            de.offerLast(nums[i]);
            result[i-k+1]=de.peekFirst();//deque永遠降序,則最大值永遠在第一個
        }

        return result;
    }
}

5)學到的東西

雙端佇列、單調佇列,保持佇列降序

多刷幾遍

65、佇列的最大值(59-2、Medium)

1)題目要求

請定義一個佇列並實現函式 max_value 得到佇列裡的最大值,要求函式max_value、push_back 和 pop_front 的均攤時間複雜度都是O(1)。

若佇列為空,pop_front 和 max_value 需要返回 -1

示例 1:

輸入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
輸出: [null,null,null,2,1,2]
示例 2:

輸入:
[“MaxQueue”,“pop_front”,“max_value”]
[[],[],[]]
輸出: [null,-1,-1]

限制:

1 <= push_back,pop_front,max_value的總運算元 <= 10000
1 <= value <= 10^5

2)我的解法

class MaxQueue {
    Queue<Integer> q=new LinkedList<>();
    Deque<Integer> de=new LinkedList<>();//單調佇列
    public MaxQueue() {

    }
    
    public int max_value() {
        return de.isEmpty()?-1:de.peekFirst();
    }
    
    public void push_back(int value) {
        
        while(!de.isEmpty()&&de.peekLast()<value)de.pollLast();
        de.offerLast(value);

        q.offer(value);

    }
    
    public int pop_front() {
        if(q.isEmpty())return -1;

        if(q.peek().equals(de.peekFirst()))de.pollFirst();//因為佇列中存的為Integer,不能用==
        
        return q.poll();
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */

3)其他解法

class MaxQueue {
    Queue<Integer> q;
    Deque<Integer> d;

    public MaxQueue() {
        q = new LinkedList<Integer>();
        d = new LinkedList<Integer>();
    }
    
    public int max_value() {
        if (d.isEmpty()) {
            return -1;
        }
        return d.peekFirst();
    }
    
    public void push_back(int value) {
        while (!d.isEmpty() && d.peekLast() < value) {
            d.pollLast();
        }
        d.offerLast(value);
        q.offer(value);
    }
    
    public int pop_front() {
        if (q.isEmpty()) {
            return -1;
        }
        int ans = q.poll();
        if (ans == d.peekFirst()) {
            d.pollFirst();
        }
        return ans;
    }
}

作者:LeetCode-Solution
連結:link
來源:力扣(LeetCode)

4)自己的優化程式碼

class MaxQueue {
    Queue<Integer> q=new LinkedList<>();
    Deque<Integer> de=new LinkedList<>();//單調佇列
    public MaxQueue() {

    }
    
    public int max_value() {
        return de.isEmpty()?-1:de.peekFirst();
    }
    
    public void push_back(int value) {
        
        while(!de.isEmpty()&&de.peekLast()<value)de.pollLast();
        de.offerLast(value);

        q.offer(value);

    }
    
    public int pop_front() {
        if(q.isEmpty())return -1;

        if(q.peek().equals(de.peekFirst()))de.pollFirst();//因為佇列中存的為Integer,不能用==
        
        return q.poll();
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */

5)學到的東西

雙端佇列,單調佇列找最大值

66、n個骰子的點數(60、Easy)

1)題目要求

把n個骰子扔在地上,所有骰子朝上一面的點數之和為s。輸入n,列印出s的所有可能的值出現的概率。

你需要用一個浮點數陣列返回答案,其中第 i 個元素代表這 n 個骰子所能擲出的點數集合中第 i 小的那個的概率。

示例 1:

輸入: 1
輸出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
示例 2:

輸入: 2
輸出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

限制:

1 <= n <= 11

2)我的解法

動態規劃

class Solution {

    public double[] twoSum(int n) {

        double base=Math.pow((1.0/6.0),n);

        int[][] dp=new int[n+1][6*n+1];//dp[i][j]即i個骰子投出和為j的可能的方案數

        for(int i=1;i<=6;i++)dp[1][i]=1;//一個骰子的情況下,1-6均只有一種情況
        
        for(int i=2;i<=n;i++){
            for(int j=i;j<=i*6;j++){
                
                for(int k=1;k<=6;k++){
                    if(j-k>=i-1&&j-k<=(i-1)*6)dp[i][j]+=dp[i-1][j-k];//dp[i-1][j-k]和dp[0][k]搭配
					
                    
                }
            }
        }

        double[] res=new double[6*n-n+1];

        for(int i=n;i<=6*n;i++){
            res[i-n]=dp[n][i]*base;
        }

        return res;
    }
}

3)其他解法

class Solution {
public:
    vector<double> twoSum(int n) {
        int dp[15][70];
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= 6; i ++) {
            dp[1][i] = 1;
        }
        for (int i = 2; i <= n; i ++) {
            for (int j = i; j <= 6*i; j ++) {
                for (int cur = 1; cur <= 6; cur ++) {
                    if (j - cur <= 0) {
                        break;
                    }
                    dp[i][j] += dp[i-1][j-cur];
                }
            }
        }
        int all = pow(6, n);
        vector<double> ret;
        for (int i = n; i <= 6 * n; i ++) {
            ret.push_back(dp[n][i] * 1.0 / all);
        }
        return ret;
    }
}; 

在這裡插入圖片描述

class Solution {
public:
    vector<double> twoSum(int n) {
        int dp[70];
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= 6; i ++) {
            dp[i] = 1;
        }
        for (int i = 2; i <= n; i ++) {
            for (int j = 6*i; j >= i; j --) {
                dp[j] = 0;
                for (int cur = 1; cur <= 6; cur ++) {
                    if (j - cur < i-1) {
                        break;
                    }
                    dp[j] += dp[j-cur];
                }
            }
        }
        int all = pow(6, n);
        vector<double> ret;
        for (int i = n; i <= 6 * n; i ++) {
            ret.push_back(dp[i] * 1.0 / all);
        }
        return ret;
    }
};

作者:huwt
連結:link
來源:力扣(LeetCode)

4)自己的優化程式碼

class Solution {

    public double[] twoSum(int n) {

        double base=Math.pow((1.0/6.0),n);

        //int[][] dp=new int[n+1][6*n+1];//dp[i][j]即i個骰子投出和為j的可能的方案數

        int[] dp=new int[6*n+1];

        for(int i=1;i<=6;i++)dp[i]=1;
        
        for(int i=2;i<=n;i++){
            for(int j=6*i;j>=i;j--){//j從大到小
                dp[j]=0;
                for(int k=1;k<=6;k++){
                    if(j-k>=i-1&&j-k<=(i-1)*6)dp[j]+=dp[j-k];//dp[i-1][j-k]和dp[0][k]搭配
                    
                }
            }
        }

        double[] res=new double[6*n-n+1];

        for(int i=n;i<=6*n;i++){
            res[i-n]=dp[i]*base;
        }

        return res;
    }
}

5)學到的東西

動態規劃

空間優化:(從大到小)

67、撲克牌中的順子(61、Easy)

1)題目要求

從撲克牌中隨機抽5張牌,判斷是不是一個順子,即這5張牌是不是連續的。2~10為數字本身,A為1,J為11,Q為12,K為13,而大、小王為 0 ,可以看成任意數字。A 不能視為 14。

示例 1:

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

示例 2:

輸入: [0,0,1,2,5]
輸出: True

限制:

陣列長度為 5

陣列的數取值為 [0, 13] .

2)我的解法

class Solution {
    public boolean isStraight(int[] nums) {
        Map<Integer,Integer> hash=new HashMap<>();
        int start=14;
        for(int i=0;i<nums.length;i++){
            if(nums[i]==0){
                if(!hash.containsKey(0))hash.put(0,1);
                else hash.put(0,hash.get(0)+1);
            }
            else {
                if(nums[i]<start)start=nums[i];
                hash.put(nums[i],1);
            }
        }
        for(int i=1;i<5;i++){
            if(hash.containsKey(start+i))continue;
            else{
                if(hash.containsKey(0)&&hash.get(0)>0)hash.put(0,hash.get(0)-1);
                else return false;
            }
        }
        return true;
    }
}

3)其他解法

在這裡插入圖片描述

在這裡插入圖片描述

class Solution {
    public boolean isStraight(int[] nums) {
        Set<Integer> repeat = new HashSet<>();
        int max = 0, min = 14;
        for(int num : nums) {
            if(num == 0) continue; // 跳過大小王
            max = Math.max(max, num); // 最大牌
            min = Math.min(min, num); // 最小牌
            if(repeat.contains(num)) return false; // 若有重複,提前返回 false
            repeat.add(num); // 新增此牌至 Set
        }
        return max - min < 5; // 最大牌 - 最小牌 < 5 則可構成順子
    }
}


在這裡插入圖片描述

class Solution {
    public boolean isStraight(int[] nums) {
        int joker = 0;
        Arrays.sort(nums); // 陣列排序
        for(int i = 0; i < 4; i++) {
            if(nums[i] == 0) joker++; // 統計大小王數量
            else if(nums[i] == nums[i + 1]) return false; // 若有重複,提前返回 false
        }
        return nums[4] - nums[joker] < 5; // 最大牌 - 最小牌 < 5 則可構成順子
    }
}

作者:jyd
連結: link
來源:力扣(LeetCode)

4)自己的優化程式碼

1、

class Solution {
    public boolean isStraight(int[] nums) {
        Arrays.sort(nums);
        int joker=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]==0)joker++;
            else if(i>0&&nums[i]==nums[i-1])return false;

        }
        return nums[4]-nums[joker]<5;
    }
}

2、

class Solution {
    public boolean isStraight(int[] nums) {
        Set<Integer> set=new HashSet<>();

        int max=0,min=14;
        for(int i=0;i<nums.length;i++){
            if(nums[i]==0)continue;
            if(set.contains(nums[i]))return false;
            if(nums[i]>max)max=nums[i];
            if(nums[i]<min)min=nums[i];

            set.add(nums[i]);    

        }
        return max-min<5;
    }
}

5)學到的東西

思想:不重複且max-min<5的 ==順子

68、 圓圈中最後剩下的數字(62、Easy)

1)題目要求

0,1,n-1這n個數字排成一個圓圈,從數字0開始,每次從這個圓圈裡刪除第m個數字。求出這個圓圈裡剩下的最後一個數字。

例如,0、1、2、3、4這5個數字組成一個圓圈,從數字0開始每次刪除第3個數字,則刪除的前4個數字依次是2、0、4、1,因此最後剩下的數字是3。

示例 1:

輸入: n = 5, m = 3
輸出: 3
示例 2:

輸入: n = 10, m = 17
輸出: 2

限制:

1 <= n <= 10^5
1 <= m <= 10^6

2)我的解法

1、暴力(超時)

class Solution {
    public int lastRemaining(int n, int m) {
        if(m==1)return n-1;
        
        Node cur=new Node(0);
        Node head=cur;
        int i=1;
        while(i<n){
            cur.next=new Node(i++);
            cur=cur.next;
        }
        cur.next=head;

        cur=head;
        while(cur.next!=cur){
            for(int j=1;j<m-1;j++)cur=cur.next;
            cur.next=cur.next.next;
            cur=cur.next;
        }
        return cur.val;
    }
}
class Node{
    int val;
    Node next;
    Node(int data){
        this.val=data;
    }
}

3)其他解法

1、

在這裡插入圖片描述

class Solution {
    public int lastRemaining(int n, int m) {
        ArrayList<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        int idx = 0;
        while (n > 1) {
            idx = (idx + m - 1) % n;
            list.remove(idx);
            n--;
        }
        return list.get(0);
    }
}

2、

在這裡插入圖片描述

在這裡插入圖片描述

class Solution {
    public int lastRemaining(int n, int m) {
        int ans = 0;
        // 最後一輪剩下2個人,所以從2開始反推
        for (int i = 2; i <= n; i++) {
            ans = (ans + m) % i;
        }
        return ans;
    }
}

作者:sweetieeyi
連結:link
來源:力扣(LeetCode)

4)自己的優化程式碼

1、

class Solution {
    public int lastRemaining(int n, int m) {
        List<Integer> list=new ArrayList<>();
        for(int i=0;i<n;i++)list.add(i);

        int index=0;
        while(n>1){
            index=(index+m-1)%n;
            list.remove(index);
            n--;
        }
        return list.get(0);
    }
}

2、數學方法

class Solution {
    public int lastRemaining(int n, int m) {
        int ans=0;
        for(int i=2;i<=n;i++)ans=(ans+m)%i;
        return ans;
    }
}

5)學到的東西

利用陣列,降低刪除的時間消耗

找規律

多刷幾遍

69、股票的最大利潤(63、Medium)

1)題目要求

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

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格。
示例 2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

限制:

0 <= 陣列長度 <= 10^5

2)我的解法

class Solution {
    public int maxProfit(int[] prices) {
        int result=0,start=Integer.MAX_VALUE;
        for(int i=0;i<prices.length;i++){
            if(prices[i]<=start)start=prices[i];
            else result=Math.max(result,prices[i]-start);
        }
        return result;
    }
}

3)其他解法

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

class Solution {
    public int maxProfit(int[] prices) {
        int cost = Integer.MAX_VALUE, profit = 0;
        for(int price : prices) {
            cost = Math.min(cost, price);
            profit = Math.max(profit, price - cost);
        }
        return profit;
    }
}

作者:jyd
連結:link
來源:力扣(LeetCode)

4)自己的優化程式碼

class Solution {
    public int maxProfit(int[] prices) {
        int result=0,start=Integer.MAX_VALUE;
        for(int i=0;i<prices.length;i++){
            start=Math.min(prices[i],start);
            result=Math.max(result,prices[i]-start);
        }
        return result;
    }
}

5)學到的東西

動態規劃

70、求1+2+…+n(64、Medium)

1)題目要求

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

示例 1:

輸入: n = 3
輸出: 6
示例 2:

輸入: n = 9
輸出: 45

限制:

1 <= n <= 10000

2)我的解法

這題好奇葩。。。

沒想出來

3)其他解法

在這裡插入圖片描述

class Solution {
    public int sumNums(int n) {
        boolean x = n > 1 && (n += sumNums(n - 1)) > 0;
        return n;
    }
}

作者:jyd
連結: link
來源:力扣(LeetCode)

4)自己的優化程式碼

class Solution {
    public int sumNums(int n) {
        int sum=n;
        boolean flag=n>0&&(sum+=sumNums(n-1))>0;
        return sum;
    }
}

5)學到的東西

&&和||的短路

相關文章