主頁 > 後端開發 > 2022SCNUOJ演算法課題目及解法總匯(JAVA)

2022SCNUOJ演算法課題目及解法總匯(JAVA)

2023-03-15 11:01:00 後端開發

輸入方式

必須引入的包java.util.*

  • list轉陣列的方式:
int[] nums = list.stream().mapToInt(Integer::intValue).toArray();
  • 或是通過String.split(" ")來分割
    舉例
 public static void main(String[] args) {
        Solution solution = new Solution();
        Scanner sc = new Scanner(System.in);
        String input = sc.nextLine();
        String[] data = https://www.cnblogs.com/kitorio/p/input.split(" ");
        int[] nums = new int[data.length];
        for (int i = 0; i < nums.length; i++) {
            nums[i] = Integer.parseInt(data[i]);
        }
        int k = sc.nextInt();
        System.out.println(solution.KEqualSumSubset(nums, k));
        sc.close();
    }
  • Stack的方法:emptypeek(查看頂部但不移出),pop(移出頂部),push(壓入頂部)

  • Queue的方法:empty,offer,poll,``peek(查看但不移出)`

  • Deque的方法:offerFirst,offerLast等等
    注意Queue和Deque的初始化方法是new LinkedList<>()

  • Arrays的sort方法

    Arrays.sort(arr,(a,b)->{return b - a;})	//自定義降序
    Arrays.sort(arr,(a,b)->{return b[1] - a[1];}) //二維陣列優先讓第二列按照降序排列
    
  • HashSet的方法:contains,removes,add,clear,size,

  • Hashmap的方法:put,get,removes,constainsKey,getOrDefault,putIfAbsent
    迭代方法

    Set<Map.Entry<Integer,String>> sets=maps.entrySet();
        for(Map.Entry<Integer,String> entry:sets){
            System.out.println("key="+entry.getKey()+"value="https://www.cnblogs.com/kitorio/p/+entry.getValue());
        }
    

分治

將一個復雜的問題分割成規模較小的相同問題,以便各個擊破,

1.最大二叉樹

給定一個不含重復元素的整數陣列 nums,以此陣列直接遞回構建的最大二叉樹,最大二叉樹定義如下:

  • 二叉樹的根是陣列 nums 中的最大元素,
  • 左子樹是通過陣列中最大值左邊部分遞回構造出的最大二叉樹,
  • 右子樹是通過陣列中最大值右邊部分遞回構造出的最大二叉樹,

回傳有給定陣列nums 構建的最大二叉樹 (前序遍歷輸出),

思路

遞回構建左子樹以及右子樹,每次都尋找所得陣列中的最大值,將左邊的部分劃分給左子樹,右邊劃分給右子樹,然后將最大值作為節點回傳,

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) {this.val = val;}
    TreeNode(int val,TreeNode left,TreeNode right){
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

class Solution {
    public TreeNode constructMaximunBinaryTree(int[] nums){
        return construct(nums,0,nums.length - 1);
    }

    public TreeNode construct(int[] nums,int left,int right){
        if(left > right) return null;
        int maxidx = left;
        for(int i = left + 1;i <= right;i++){
            if(nums[i] > nums[maxidx]){
                maxidx = i;
            }
        }
        TreeNode node = new TreeNode(nums[maxidx]);
        node.left = construct(nums,left,maxidx - 1);
        node.right = construct(nums,maxidx + 1,right);
        return node;
    }
}

public class Main {

    public void PreOrderRecur(TreeNode root){
//        if(root == null){
//            System.out.print("null ");
//            return;
//        }
        System.out.print(root.val + " ");
        if(root.left == null && root.right == null){
            return;
        }
        if(root.left != null){
            PreOrderRecur(root.left);
        }else{
            System.out.print("null ");
        }
        if(root.right != null){
            PreOrderRecur(root.right);
        }else{
            System.out.print("null ");
        }
    }

}

2.鏈表排序

思路

class ListNode{
    int val;
    ListNode next;
    ListNode() {};
    ListNode(int val) {this.val = val;}
    ListNode(int val,ListNode next) {this.val = val;this.next = next;}
}

class Solution{
    public ListNode sortListNode(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode splienode = split(head);
        head = sortListNode(head);
        splienode = sortListNode(splienode);
        return merge(head,splienode);

    }

    public ListNode split(ListNode head){
        ListNode dummyhead = new ListNode(0,head);
        ListNode fast = dummyhead,slow = dummyhead;
        while(fast != null && fast.next !=null){
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode ans = slow.next;
        slow.next = null;   //截斷
        return ans;
    }

    public ListNode merge(ListNode node1,ListNode node2){
        ListNode dummyhead = new ListNode(0);
        ListNode temp = dummyhead;
        ListNode temp1 = node1,temp2 = node2;
        while(temp1 != null && temp2 != null){
            //將較小值加入到合并后的串列中
            if(temp1.val <= temp2.val){
                temp.next = temp1;
                temp1 = temp1.next;
            }else{
                temp.next = temp2;
                temp2 = temp2.next;
            }
            temp = temp.next;
        }
        if(temp1 != null){
            temp.next = temp1;
        }else if(temp2 != null){
            temp.next = temp2;
        }
        return dummyhead.next;
    }

}

3.尋找多數

給定一個大小為 n 的整型陣列 a,找到其中的多數元素,多數元素是指在陣列中出現次數大于 ? n/2 ? 的元素,

思路1(分治)

若數a是陣列nums的眾數,那么當nums分為兩部分時,a至少是一部分的眾數,那么就可以將陣列分為左右兩部分,求出左半部分的眾數a1以及右半部分的眾數a2,再對其進行比較

public class Problem2 {
    public int majorityElement(int[] nums) {
        return majorityElementRec(nums, 0, nums.length - 1);
    }

    public int majorityElementRec(int[] nums, int left, int right) {
        // 特殊情況,不用尋找左右多數可以直接回傳
        if (left == right) {
            return nums[left];
        }
        // 將陣列分割為左右部分尋找其各自的多數
        int mid = (right - left) / 2 + left;
        int leftmajority = majorityElementRec(nums, left, mid);
        int rightmajority = majorityElementRec(nums, mid + 1, right);
        if (leftmajority == rightmajority) {
            return leftmajority;
        }

        int leftcount = countInRange(nums, leftmajority, left, right);
        int rightcount = countInRange(nums, rightmajority, left, right);
        return leftcount > rightcount ? leftmajority : rightmajority;
    }

    // 回傳num在nums中的個數
    public int countInRange(int[] nums, int num, int left, int right) {
        int count = 0;
        for (int i = left; i <= right; i++) {
            if (nums[i] == num) {
                count++;
            }
        }
        return count;
    }
}

思路2(排序)

直接進行排序,因為當a是多數時,它一定會出現在陣列的 ? n/2 ?下標位置

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];
        for(int i = 0;i < n;i++){
            nums[i] = sc.nextInt();
        }
        Arrays.sort(nums);
        System.out.print(nums[n/2]);
    }
}

4.找到最大子序和

給定一個整數陣列 nums,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),回傳其最大和,

思路1(分治)

取m=?(l+r)/2?,對[l+m]和[m+1,r]分治求解,所需要維護的為下述四個量:

  • lSum 表示 l,r內以 l為左端點的最大子段和,lSum=Max(左子區間的lSum,左子區間的iSum+右子區間的lSum)

  • rSum 表示 l,r內以 r 為右端點的最大子段和,其值同上

  • mSum 表示 l,r內的最大子段和,mSum=Max(左子區間的mSum,右子區間的mSum,左子區間的rSum+右子區間的lSum)

  • iSum 表示l,r 的區間和

public class Problem3 {
    // 便于維護每個狀態的這四個變數
    public class Status {
        public int lSum, rSum, mSum, iSum;

        public Status(int lSum, int rSum, int mSum, int iSum) {
            this.lSum = lSum;
            this.rSum = rSum;
            this.mSum = mSum;
            this.iSum = iSum;
        }
    }

    public int maxSubArray(int[] nums) {
        return getInfo(nums, 0, nums.length - 1).mSum;
    }

    public Status getInfo(int[] nums, int l, int r) {
        if (l == r) {
            return new Status(nums[l], nums[l], nums[l], nums[l]);
        }
        int m = (r - l) / 2 + l;
        Status lSub = getInfo(nums, l, m);
        Status rSub = getInfo(nums, m + 1, r);
        return pushUp(lSub, rSub);
    }

    // 合并
    public Status pushUp(Status lSub, Status rSub) {
        int iSum = lSub.iSum + rSub.iSum;
        int lSum = Math.max(lSub.lSum, lSub.iSum + rSub.lSum);
        int rSum = Math.max(rSub.rSum, lSub.rSum + rSub.iSum);
        int mSum = Math.max(Math.max(lSub.mSum, rSub.mSum), lSub.rSum + rSub.lSum);
        return new Status(lSum, rSum, mSum, iSum);
    }

}

思路2(動態規劃)

可以得到動態規劃轉移方程\(f(i) = max(f(i - 1) + nums[i],nums[i])\) ,其中由于\(f(i)\)只和 \(f(i-1)\)有關,可以用一個變數pre去維護,

class Solution {
    public int MaxSubArray(int[] nums){
        int pre = 0;
        int ans = nums[0];
        for(int num : nums){
            pre = Math.max(pre + num,num);
            ans = Math.max(ans,pre);
        }
        return ans;
    }
}

5.找到k個最小數

輸入整數陣列 arr,找出其中最小的 k 個數,

思路(分治/快速排序思想)

快排的劃分函式每次執行完后都會將陣列分為兩部分,小于分界值pivot的元素都會放在陣列左邊,而大于的則會放到陣列右邊,然后回傳分界值下標,而此處只需要處理劃分的其中一邊就可以了,

class Solution {
    public int[] smallestK(int[] arr, int k) {
        randomizedSelected(arr, 0, arr.length - 1, k);
        int[] vec = new int[k];
        for (int i = 0; i < k; ++i) {
            vec[i] = arr[i];
        }
        return vec;
    }

    private void randomizedSelected(int[] arr, int l, int r, int k) {
        if (l >= r) {
            return;
        }
        int pos = randomizedPartition(arr, l, r);
        int num = pos - l + 1;
        if (k == num) {
            return;
        } else if (k < num) {	//說明第k小的數在右側
            randomizedSelected(arr, l, pos - 1, k);
        } else {	//說明在左側
            randomizedSelected(arr, pos + 1, r, k - num);
        }
    }

    // 基于隨機的劃分,使前k小的數在陣列左側
    private int randomizedPartition(int[] nums, int l, int r) {
        int i = new Random().nextInt(r - l + 1) + l;
        swap(nums, r, i);
        return partition(nums, l, r);
    }

    private int partition(int[] nums, int l, int r) {
        int pivot = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
            if (nums[j] <= pivot) {
                i = i + 1;
                swap(nums, i, j);
            }
        }
        swap(nums, i + 1, r);
        return i + 1;
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

6.尋找第k個最大元素

思路(分治/快排思想)

快排思想的劃分中,每一次劃分都使得左邊的元素小于等于a,而a右邊的元素都大于等于它,也就是每一次都能確認一個元素的最終位置,所以只需要某次劃分得到的a為倒數第k個下標的時候就能找到答案,

由此可以知道,我們只需要在劃分得到的a小于目標下標時劃分右區間,大于目標下標的時候劃分左區間,直到它等于目標下標即可,(引入隨機化是為了加速劃分的程序)

class Solution {
    Random random = new Random();

    public int findKthLargest(int[] nums, int k) {
        return quickSelect(nums, 0, nums.length - 1, nums.length - k);
    }

    public int quickSelect(int[] a, int l, int r, int index) {
        int q = randomPartition(a, l, r);
        if (q == index) {
            return a[q];
        } else {
            return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index);
        }
    }

    public int randomPartition(int[] a, int l, int r) {
        int i = random.nextInt(r - l + 1) + l;
        swap(a, i, r);
        return partition(a, l, r);
    }

    public int partition(int[] a, int l, int r) {
        int x = a[r], i = l - 1;
        for (int j = l; j < r; ++j) {
            if (a[j] <= x) {
                swap(a, ++i, j);
            }
        }
        swap(a, i + 1, r);
        return i + 1;
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

7.找到k個最長重復字串

在一個字串 s 中找出 s 中的最長子串,且該字串的每一個字符出現次數都不少于 k,輸出該子串的長度,

思路

首先統計每個字符的出現次數,然后找到不滿足k次出現的字符作為劃分字符,然后逐步劃分統計長度

class Solution{
    public int FindMaxSubString(String s,int k){
        return divide(s,0,s.length() - 1,k);
    }

    public int divide(String s,int left,int right,int k){
        int[] count = new int[26];
        for(int i = left;i <= right;i++){
            count[s.charAt(i) - 'a']++;
        }
        int splitchar= -1;
        for(int i = 0;i < 26;i++){
            if(count[i] > 0 && count[i] < k){
                splitchar = i;
                break;
            }
        }
        if (splitchar == -1){
            return right - left + 1;
        }
        int ans = 0;
        int splitindex = left;
        while(splitindex <= right){
            //考慮到存在不滿足要求的字符放在left端的情況
            while(splitindex <= right && (s.charAt(splitindex) - 'a') == splitchar){
                splitindex++;
            }
            int start = splitindex;
            while(splitindex <= right && (s.charAt(splitindex) - 'a') != splitchar){
                splitindex++;
            }
            int length = divide(s,start,splitindex - 1,k);
            ans = Math.max(ans,length);
        }
        return ans;
    }
}

動態規劃

1.換硬幣

給定面值分別為2,5,7的硬幣,每種硬幣有無限個,給定一個N,求組成N最少需要的硬幣的數量,若無法組成則回傳-1.

思路

class Solution{
	public int SwapCoins(int n) {
		if(n <= 1 || n == 3) return -1;
		if(n == 2) return 1;
		int[] coins = {2,5,7};
		int[] dp = new int[n + 1];
		Arrays.fill(dp, n + 1);
		dp[0] = 0;
		dp[1] = 0;
		dp[2] = 1;
		dp[3] = 0;
		for(int i = 4;i <= n;i++) {
			for(int coin : coins) {
				if(i >= coin && i - coin != 1 && i - coin != 3) {
					dp[i] = Math.min(dp[i - coin] + 1, dp[i]);
				}
			}
		}
		return dp[n];
	}
}
進階版

給定不同面額的硬幣和一個總金額(面額、硬幣數、總金額均不超過 10),寫出函式來計算可以湊成總金額的硬幣組合數,假設每一種面額的硬幣有無限個,

思路

其實思路是類似的,前一題是想要最少的硬幣數,所以對每一種硬幣都進行嘗試然后找到其中所需數量最少的那個,這一題因為要求的是組合數所以直接加上dp[i-coin]的數量就可以,

class Solution {

    public int CoinsProblem(int sum, int[] coins) {
        Arrays.sort(coins);
        int[] dp = new int[sum + 1];
        dp[0] = 1;
        for (int coin : coins) {
            for (int i = coin; i <= sum; i++) {
                dp[i] += dp[i - coin];
            }
        }
        return dp[sum];
    }

}

2.走網格

m行n列的網格,從左上角(1,1)出發,每一步只能向下或者向右,問共有多少種方法可以走到右下角(m,n);

思路

對于最左邊的列以及最上面的行都只有一種方法

\(dp[i][j] = dp[i - 1][j] + dp[i][j - 1]\),而實際上每次都是依賴于\(dp[i - 1][j]\)\(dp[i][j - 1]\),所以可以將二維陣列優化為一維陣列

class Solution{
	public int walkinggrids(int m,int n) {
		int[] ans = new int[n];
		Arrays.fill(ans, 1);
		for(int i = 1;i < m;i++) {
			for(int j = 1;j < n;j++) {
				ans[j] += ans[j - 1];
			}
		}
		return ans[n - 1];
	}
}
進階版

m行n列的網格,從左上角出發,每一步只能向下或者向右,網格中有障礙物,障礙物用1表示,空位置用0表示,問共有多少種方法可以走到右下角(m,n);

思路

同上,但是遇到障礙物就置0,不用全部置1是因為在上一題,左上邊緣行列必然可行,而在此處不一定,

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[] ans = new int[n];
        ans[0] = obstacleGrid[0][0] == 0 ? 1 : 0;
        for(int i = 0;i <m;i++){
            for(int j = 0;j < n;j++){
                if(obstacleGrid[i][j] == 1){
                    ans[j] = 0;
                    continue;
                }
                if(j >= 1 && obstacleGrid[i][j] == 0){
                    ans[j] += ans[j - 1];
                }
            }
        }
        return ans[n - 1];
    }
}

3.爬樓梯

假設你正在爬樓梯,需要 n 階你才能到達樓頂,(n是正整數)每次你可以爬 1 或 2 個臺階,你有多少種不同的方法可以爬到樓頂呢?

思路

\(dp[i] = dp[i - 1] + dp[i - 2]\)

class Solution{
	public int walkingstairs(int n) {
		if(n <= 1) return 1;
		int[] dp = new int[n + 1];
		dp[0] = 1;
		dp[1] = 1;
		dp[2] = 2;
		for(int i = 3;i <= n;i++) {
			dp[i] = dp[i - 1] + dp[i - 2];
		}
		return dp[n];
	}
}
進階版:最小花費爬樓梯

給你一個整數陣列 cost ,其中 cost[i] 是從樓梯第 i 個臺階向上爬需要支付的費用,一旦你支付此費用,即可選擇向上爬一個或者兩個臺階,

你可以選擇從下標為 0 或下標為 1 的臺階開始爬樓梯,

請你計算并回傳達到樓梯頂部的最低花費,

思路

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        int[] dp = new int[n + 1];
        dp[0] = dp[1] = 0;
        for(int i = 2;i <= n;i++){
            dp[i] = Math.min(dp[i - 1] + cost[i - 1],dp[i - 2] + cost[i - 2]);
        }
        return dp[n];
    }
}

4.背包問題

一個背包有一定的承重W,有N件物品,每件物品都有自己的價值,記錄在陣列V中,也都有自己的重量,記錄在陣列W中,每件物品只能選擇要裝入還是不裝入背包,要求在不超過背包承重的前提下,選出的物品總價值最大, 假設物品數量為4 背包承重為10 每個商品重量為7,3,4,5 價值為42,12,40,25

思路

\[dp[i][j] = \begin{cases} max(dp[i - 1][j - W[i]] + V[i],dp[i - 1][j]),如果放得下背包\\ dp[i - 1][j],放不下背包 \end{cases} \]

class Solution{
    int[] W = {7,3,4,5};
    int[] V = {42,12,40,25};
    public int backetProblem(int n,int cap){
        //列標代表容量 行標代表所選的物品
        //該陣列存放的是當前選擇第i個物品時j承重的情況下所得到的最大價值
        int[][] dp = new int[n][cap+1];
        for(int i = 0;i < cap;i++){
            if(i >= W[0]){
                dp[0][i] = V[0];
            }
        }
        for(int i = 1;i < n;i++){
            for(int j = 1;j <= cap;j++){
                if(j >= W[i]){
                    //放得下
                    dp[i][j] = Math.max(dp[i-1][j - W[i]] + V[i],dp[i-1][j]);//前者為選擇,后者為不選
                }else{
                    //放不下
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n-1][cap];
    }
}

5.最長回文子串

求一個字串中的最長的回文子串

思路

class Solution {
    public String longsetPalindrome(String s) {
        if (s.length() == 1)
            return s;
        // dp(i,j) = dp(i + 1,j - 1)
        // dp[i][j] 代表s[i,j]是否為回文串
        int len = s.length();
        int maxlen = 1; // 最大回文串長度
        int begin = 0; // 回文串起始位置
        boolean[][] dp = new boolean[len][len];
        // init dp 所有長度為1的子串都是回文串
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }
        char[] chararray = s.toCharArray();
        // 開始列舉子串長度
        for (int L = 2; L <= len; L++) {
            // 列舉左邊界
            for (int i = 0; i < len; i++) {
                // 右邊界可計算得出
                int j = L + i - 1;
                if (j >= len) // 以防越界
                    break;
                if (chararray[i] != chararray[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) { // 只有兩個字符
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                if (dp[i][j] && L > maxlen) {
                    maxlen = L;
                    begin = i;
                }
            }
        }
        return s.substring(begin, begin + maxlen);
    }

}

6.最長公共子序列

給定兩個字串 text1 和 text2,回傳這兩個字串的最長 公共子序列 的長度,如果不存在 公共子序列 ,回傳 0

思路

從1開始防止越界

\[dp[i][j] = \begin{cases} dp[i - 1][j - 1] + 1,text_1[i - 1] = text_2[j - 1]\\ max(dp[i - 1][j],dp[i][j - 1]),text_1[i - 1] ≠ text_2[j - 1] \end{cases} \]

image.png

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int len1 = text1.length(), len2 = text2.length();
        int[][] dp = new int[len1 + 1][len2 + 1];
        for (int i = 1; i <= len1; i++) {
            char c1 = text1.charAt(i - 1);
            for (int j = 1; j <= len2; j++) {
                char c2 = text2.charAt(j - 1);
                if (c1 == c2) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[len1][len2];
    }

}

7.打家劫舍

你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警,

給定一個代表每個房屋存放金額的非負整數陣列,計算你 不觸動警報裝置的情況下 ,一夜之內能夠偷竊到的最高金額,

思路

\[dp[i] = max(dp[i - 1],dp[i - 2] + nums[i]) \]

class Solution {
    public int stolen(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < n; i++) {
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
        }
        return dp[n - 1];
    }

}
進階版

你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有一定的現金,這個地方所有的房屋都 圍成一圈 ,這意味著第一個房屋和最后一個房屋是緊挨著的,同時,相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警

給定一個代表每個房屋存放金額的非負整數陣列,計算你 在不觸動警報裝置的情況下 ,今晚能夠偷竊到的最高金額,

思路

同上題,但是要保證不能同時盜竊第一間房子和最后一間房子,

考慮分為兩種情況進行考慮,即分別對[0,n-1],[1,n]的房子進行考慮,也就是不偷第一件房子和不偷最后一間房子

class Solution {
    public int rob(int[] nums) {
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        } else if (length == 2) {
            return Math.max(nums[0], nums[1]);
        }
        return Math.max(robRange(nums, 0, length - 2), robRange(nums, 1, length - 1));
    }

    public int robRange(int[] nums, int start, int end) {
        int first = nums[start], second = Math.max(nums[start], nums[start + 1]);
        for (int i = start + 2; i <= end; i++) {
            int temp = second;
            second = Math.max(first + nums[i], second);
            first = temp;
        }
        return second;
    }
}

8.擺動序列

如果連續數字之間的差嚴格地在正數和負數之間交替,則數字序列稱為 擺動序列 ,第一個差(如果存在的話)可能是正數或負數,僅有一個元素或者含兩個不等元素的序列也視作擺動序列,

  • 例如, [1, 7, 4, 9, 2, 5] 是一個 擺動序列 ,因為差值 (6, -3, 5, -7, 3) 是正負交替出現的,
  • 相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是擺動序列,第一個序列是因為它的前兩個差值都是正數,第二個序列是因為它的最后一個差值為零, 子序列 可以通過從原始序列中洗掉一些(也可以不洗掉)元素來獲得,剩下的元素保持其原始順序,

給你一個整數陣列 nums ,回傳 nums 中作為 擺動序列 的 最長子序列的長度 ,

思路

可以分為上升擺動序列和下降擺動序列分別進行考慮

class Solution {

    public int WiggleMaxLength(int[] nums) {
        int n = nums.length;
        if (n == 1)
            return n;
        int up = 1, down = 1;
        for (int i = 1; i < n; i++) {
            if (nums[i] > nums[i - 1]) {
                up = Math.max(down + 1, up);
            } else if (nums[i] < nums[i - 1]) {
                down = Math.max(up + 1, down);
            }
        }
        return Math.max(up, down);
    }

}

9.戳氣球

有 n 個氣球,編號為0 到 n - 1,每個氣球上都標有一個數字,這些數字存在陣列 nums 中,

現在要求你戳破所有的氣球,戳破第 i 個氣球,你可以獲得$ nums[i - 1]nums[i] nums[i + 1]$枚硬幣, 這里的 i - 1 和 i + 1 代表和 i 相鄰的兩個氣球的序號,如果 i - 1或 i + 1 超出了陣列的邊界,那么就當它是一個數字為 1 的氣球,

求所能獲得硬幣的最大數量

思路

可以觀察到戳氣球的操作會導致兩個氣球從不相鄰變成相鄰,因此倒過來看這些操作,就會變成每一次添加一個氣球

class Solution {

    public int PokeBallon(int[] nums) {
        int n = nums.length;
        int[][] dp = new int[n + 2][n + 2];
        int[] coins = new int[n + 2];
        for (int i = 1; i <= n; i++) {
            coins[i] = nums[i - 1];
        }
        coins[0] = coins[n + 1] = 1;
        // 注意此處需要從下往上計算
        // 因為所依賴的dp[k][j]在從上往下算的時候根本還沒參與計算
        // i的變化程序可以視作氣球向左逐漸添加的程序
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 2; j <= n + 1; j++) {
                for (int k = i + 1; k < j; k++) {
                    dp[i][j] = Math.max(dp[i][j], coins[i] * coins[k] * coins[j] + dp[i][k] + dp[k][j]);
                }
            }
        }
        return dp[0][n + 1];
    }

}

貪心

一般來說都是做點排序,選擇當前看來最好的選擇,考慮的都是區域最優解

1.最長回文串

給定一個包含大寫字母和小寫字母的字串,找到通過這些字母構造成的最長的回文串,在構造程序中,請注意區分大小寫,比如 "Aa" 不能當做一個回文字串,解釋:第一個例子可以構成"dccaccd", 它的長度是 7,

  • 字串的長度不會超過 1010

思路

先統計字串中所有字母的個數再進行選擇,只要大于1均可使用,等于1的只能使用一次,

class Solution {
    public int longestPalindrome(String s) {
        int ans = 0;
        int[] hash = new int[52];
        int n = s.length();
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (c < 'a') {
                hash[c - 'A']++;
            } else {
                hash[c - 'a' + 26]++;
            }
        }
        Arrays.sort(hash);
        for (int i = 51; i >= 0; i--) {
            ans += hash[i] / 2 * 2;
            if (hash[i] % 2 == 1 && ans % 2 == 0)
                ans++;

        }
        return ans;
    }

}

2.移掉K位數字

給你一個以字串表示的非負整數 num 和一個整數 k ,移除這個數中的 k 位數字,使得剩下的數字最小,請你以字串形式回傳這個最小的數字,

  • 1 <= k <= num.length <= 1000
  • num 僅由若干位數字(0 - 9)組成
  • 除了 0 本身之外,num 不含任何前導零

思路

主要目的就是保證左邊的數都是最小的數,所以考慮用堆疊結構去進行維護(先進后出,每次都與左邊的資料比較把相對最小的留下來),但是記住堆疊結構存盤的資料輸出時是倒序的要轉換過來,

class Solution {
    public String removeKNum(String num, int k) {
        // 保證左邊的數都是最小的數
        int length = num.length();
        StringBuilder ans = new StringBuilder();
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < length; i++) {
            char digit = num.charAt(i);
            while (!stack.isEmpty() && k > 0 && digit < stack.peek()) {
                stack.pop();
                k--;
            }
            stack.push(digit);
        }
        while (k-- > 0) {
            stack.pop();
        }
        int n = stack.size();
        ans.setLength(n);
        for (int i = n - 1; i >= 0; i--) {
            char digit = stack.pop();
            ans.setCharAt(i, digit);
        }
        int i = 0;
        while (ans.length() != 0 && ans.charAt(i) == '0') {
            ans.deleteCharAt(i);
        }
        return ans.length() == 0 ? "0" : ans.toString();
    }

}

3.盛最多的水

給你 n 個非負整數 a1,a2,...,an,每個數代表坐標中的一個點 (i, ai) ,在坐標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0) ,找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水,

說明:你不能傾斜容器,

思路

用雙指標,在左右兩邊每次都取最長的那個柱子逐漸往中間縮,取計算程序中的最大值,

lass Solution{
    public int MaxWater(int[] nums){
        int ans = 0;
        int left = 0;
        int right = nums.length - 1;
        while(left < right){
            int v = Math.min(nums[left],nums[right]) * (right - left);
            ans = Math.max(ans,v);
            if(nums[left] > nums[right]){
                right--;
            }else{
                left++;
            }
        }
        return ans;
    }
}

4.分發糖果

n 個孩子站成一排,給你一個整數陣列 ratings 表示每個孩子的評分, 你需要按照以下要求,給這些孩子分發糖果:

  • 每個孩子至少分配到 1 個糖果,
  • 相鄰的孩子中,評分高的孩子必須獲得更多的糖果,

請你給每個孩子分發糖果,計算并回傳需要準備的最少糖果數目 ,

思路

先從左到右遍歷一遍,處理應該比自己左邊的孩子多一顆糖果的情況

再從右到左遍歷一遍,處理右邊的孩子的情況

class Solution {

    public int PartitionCandys(int[] ratings) {
        int ans = 0;
        int n = ratings.length;
        int[] left = new int[n];
        left[0] = 1;
        for (int i = 1; i < n; i++) {
            if (ratings[i] > ratings[i - 1]) {
                left[i] = left[i - 1] + 1;
            } else {
                left[i] = 1;
            }
        }
        int right = 1;
        ans += Math.max(left[n - 1], right);
        for (int i = n - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                right++;
            } else {
                right = 1;
            }
            ans += Math.max(left[i], right);
        }
        return ans;
    }

}

5.跳躍游戲

給定一個非負整數陣列 nums ,你最初位于陣列的 第一個下標 ,

陣列中的每個元素代表你在該位置可以跳躍的最大長度

判斷你是否能夠到達最后一個下標,

思路

每次都取自己能跳到的最遠距離

class Solution {

    public boolean JumpGame(int[] nums) {
        int k = 0;
        for (int i = 0; i < nums.length; i++) {
            if (i > k)
                return false;
            k = Math.max(k, i + nums[i]);
        }
        return true;
    }

}
進階版

在上面的基礎上回傳跳到最后一個下標時的最小跳躍次數,但是保證一定可以跳到最后的位置

思路

正向查找可以到達的最大位置(也可以反向查找離終點最遠的那個作為出發位置,這里不給出)

class Solution {
    public int jump(int[] nums) {
        int length = nums.length;
        int end = 0;
        int maxPosition = 0; 
        int steps = 0;
        for (int i = 0; i < length - 1; i++) {
            maxPosition = Math.max(maxPosition, i + nums[i]); 
            if (i == end) {
                end = maxPosition;
                steps++;
            }
        }
        return steps;
    }
}

回溯

  1. 針對所給問題定義解空間
  2. 確定易于搜索的解空間結構
  3. 以深度優先搜索的方式搜索解空間,中間可以用剪枝函式避免無效搜索

1.括號生成

數字 n 代表生成括號的對數,請你設計一個函式,用于能夠生成所有可能的并且 有效的 括號組合,

有效括號組合需滿足:左括號必須以正確的順序閉合,

提示:

  • 1 <= n <= 8

  • 請按照括號生成的層數有大到小排序,如輸入3時,先生成((())),最后再生成 ()()()

思路

class Solution {
    // 1.(始終在左邊,同理右邊必須有)
    // 2.出現)時,(的數量大于等于)
    // 3.使用回溯法務必記住在遞回后將自己執行的操作撤回,如這里的deletecharat
    List<String> ans = new ArrayList<>();
    StringBuilder element = new StringBuilder();

    public List<String> GenenrateParantheses(int n) {
        back_tracking(0, 0, 0, n);
        return ans;
    }

    public void back_tracking(int numsleft, int numsright, int index, int n) {
        // numsleft:(的數量
        // numsright:)的數量
        // index:當前字串的字符數
        // n:上限
        if (index == 2 * n) {
            ans.add(element.toString());
            return;
        }
        if (numsleft < n) {
            element.append("(");
            back_tracking(numsleft + 1, numsright, index + 1, n);
            element.deleteCharAt(element.length() - 1);
        }
        if (numsright < n && numsright < numsleft) {
            element.append(")");
            back_tracking(numsleft, numsright + 1, index + 1, n);
            element.deleteCharAt(element.length() - 1);
        }
    }

}

2.組合問題

給定兩個整數 n 和 k,回傳范圍 [1, n] 中所有可能的 k 個數的組合,

你需要按順序回傳答案,

思路

class Solution {

    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> element = new ArrayList<>();

    public List<List<Integer>> Combine(int n, int k) {
        back_tracking(n, k, 1);
        return ans;
    }

    public void back_tracking(int n, int k, int startindex) {
        if (element.size() == k) {
            ans.add(new ArrayList<>(element)); // 拷貝
            return;
        }
        // 剪枝版本
        // 剪枝原理為如果還沒選中的長度為x,已選中的為y,若x+y<k則沒有繼續往下探查的必要
        for (int i = startindex; i <= n - (k - element.size()) + 1; i++) {
            element.add(i);
            back_tracking(n, k, i + 1);
            element.remove(element.size() - 1);
        }

        // 無剪枝版本
        // for (int i = startindex; i <= n; i++) {
        // element.add(i);
        // back_tracking(n, k, i + 1);
        // element.remove(element.size() - 1);
        // }
    }

}
進階:全排列

給定一個不含重復數字的陣列 nums ,回傳其 所有可能的全排列 ,你可以 按任意順序 回傳答案,

思路

在組合的基礎上建立一個 visited 陣列用以標記是否已經填入這個數字

class Solution {
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    List<Integer> element = new ArrayList<Integer>();
    public List<List<Integer>> permute(int[] nums) {
        int[] visited = new int[nums.length];
        backtracking(nums,visited);
        return result;
    }

    public void backtracking(int[] nums,int[] visited){
        if(element.size() == nums.length){
            result.add(new ArrayList<Integer>(element));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(visited[i] == 1) continue;
            element.add(nums[i]);
            visited[i] = 1;
            backtracking(nums,visited);
            visited[i] = 0;
            element.remove(element.size() - 1);
        }
    }
}
進階:全排列II

給定一個可包含重復數字的序列 nums按任意順序 回傳所有不重復的全排列,

思路

先排序,這樣相同數字就會被歸到一起,然后在全排列的基礎上判斷當前數字是否等于上一個數字并且是否被選擇

class Solution {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    List<Integer> element = new ArrayList<Integer>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        int[] visited = new int[nums.length];
        backtracking(nums,visited);
        return ans;
    }

    public void backtracking(int[] nums,int[] visited){
        if(element.size() == nums.length){
            ans.add(new ArrayList<Integer>(element));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(visited[i] == 1 || (i > 0 && nums[i-1] == nums[i] && visited[i - 1] == 0)) continue;
            element.add(nums[i]);
            visited[i] = 1;
            backtracking(nums,visited);
            visited[i] = 0;
            element.remove(element.size() - 1);
        }
    }
}
進階:組合總和

給你一個 無重復元素 的整數陣列 candidates 和一個目標整數 target ,找出 candidates 中可以使數字和為目標數 target 的 所有 不同組合 ,并以串列形式回傳,你可以按 任意順序 回傳這些組合,

candidates 中的 同一個 數字可以 無限制重復被選取 ,如果至少一個數字的被選數量不同,則兩種組合是不同的,

對于給定的輸入,保證和為 target 的不同組合數少于 150 個

思路

類似全排列,但是這里由于可以重復選擇,因此不需要visited陣列

class Solution {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    List<Integer> element = new ArrayList<Integer>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        back_tracking(candidates,target,0);
        return ans;
    }

    public void back_tracking(int[] nums,int diff,int begin){
        if(diff == 0){
            ans.add(new ArrayList<>(element));
            return;
        }
        for(int i = begin;i < nums.length;i++){
            if(diff < nums[i])  break;	//剪枝,如果當前值大于差值就不必選擇了
            element.add(nums[i]);
            back_tracking(nums,diff - nums[i],i);
            element.remove(element.size() - 1);
        }
    }
}
進階:優美的排列

假設有從 1 到 n 的 n 個整數,用這些整數構造一個陣列 perm(下標從 1 開始),只要滿足下述條件 之一 ,該陣列就是一個 優美的排列 :

  • perm[i] 能夠被 i 整除
  • i 能夠被 perm[i] 整除

給你一個整數 n ,回傳可以構造的 優美排列 的 數量

思路

在排列的基礎上判斷當前數字是否可以被整除

class Solution {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    List<Integer> element = new ArrayList<Integer>();
    int[] perm;

    public int BeautyArrange(int n) {
        perm = new int[n + 1];
        boolean[] visited = new boolean[n + 1];
        for (int i = 1; i <= n; i++) {
            perm[i] = i;
        }
        back_tracking(n, visited);
        return ans.size();
    }

    public void back_tracking(int n, boolean[] visited) {
        if (element.size() == n) {
            ans.add(new ArrayList<>(element));
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (visited[i]) {
                continue;
            } else {
                int k = element.size() + 1;
                if (perm[i] % k == 0 || k % perm[i] == 0) {
                    element.add(k);
                    visited[i] = true;
                    back_tracking(n, visited);
                    element.remove(element.size() - 1);
                    visited[i] = false;

                }
            }
        }
    }

}

3.找出所有子集的異或總和再求和

一個陣列的 異或總和 定義為陣列中所有元素按位 XOR 的結果;如果陣列為 空 ,則異或總和為 0 ,

例如,陣列 [2,5,6] 的 異或總和 為 2 XOR 5 XOR 6 = 1 , 給你一個陣列 nums ,請你求出 nums 中每個 子集 的 異或總和 ,計算并回傳這些值相加之 和 ,

注意:在本題中,元素 相同 的不同子集應 多次 計數,

陣列 a 是陣列 b 的一個 子集 的前提條件是:從 b 洗掉幾個(也可能不洗掉)元素能夠得到 a ,

思路

分兩種情況回溯,分別是選擇當前數字ornot

class Solution {

    Integer ans = 0;

    public Integer SumXorSubset(int[] nums) {
        back_tracking(nums, 0, 0);
        return ans;
    }

    public void back_tracking(int[] nums, int val, int idx) {
        if (idx == nums.length) {
            ans += val;
            return;
        }
        back_tracking(nums, val ^ nums[idx], idx + 1); // 選擇當前數字
        back_tracking(nums, val, idx + 1); // 不選擇當前數字
    }

}

4.分割回文串

給你一個字串 s,請你將 s 分割成一些子串,使每個子串都是 回文串 ,回傳 s 所有可能的分割方案,

回文串 是正著讀和反著讀都一樣的字串,

思路

先判斷該字串是不是回文串,如果是回文串再對其進行分割

class Solution {
    boolean[][] isPalindrome;
    List<List<String>> ans = new ArrayList<List<String>>();
    List<String> element = new ArrayList<String>();
    int n;

    public List<List<String>> SplitPalindrome(String s) {
        // 先判斷回文串在進行分割
        n = s.length();
        isPalindrome = new boolean[n][n];
        for (int i = 0; i < n; i++) {
            isPalindrome[i][i] = true;
        }
        for (int L = 2; L <= n; L++) {
            for (int i = 0; i < n; i++) {
                int j = L + i - 1;
                if (j >= n)
                    break;
                if (s.charAt(i) != s.charAt(j)) {
                    isPalindrome[i][j] = false;
                } else {
                    if (j - i < 3) {
                        isPalindrome[i][j] = true;
                    } else {
                        isPalindrome[i][j] = isPalindrome[i + 1][j - 1];
                    }
                }
            }
        }

        back_tracking(s, 0);
        return ans;
    }

    public void back_tracking(String s, int start) {
        if (start == s.length()) {
            ans.add(new ArrayList<String>(element));
        }
        for (int i = start; i < n; i++) {
            if (isPalindrome[start][i]) {
                element.add(s.substring(start, i + 1));
                back_tracking(s, i + 1);
                element.remove(element.size() - 1);
            }
        }
    }

}

5.電話號碼字母組合

給定一個僅包含數字 2-9 的字串,回傳所有它能表示的字母組合,答案按字母順序回傳,

給出數字到字母的映射如下(與電話按鍵相同),注意 1 不對應任何字母,

img

思路

class Solution {
    List<String> ans = new ArrayList<>();
    StringBuffer element = new StringBuffer();
    String[] number = { "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };

    public List<String> PhoneLetterCombination(String s) {
        if (s.length() == 0)
            return ans;
        back_tracking(s, 0);
        return ans;
    }

    public void back_tracking(String s, int index) {
        if (index == s.length()) {
            ans.add(element.toString());
            return;
        }
        String digitletters = number[s.charAt(index) - '2'];
        for (int i = 0; i < digitletters.length(); i++) {
            element.append(digitletters.charAt(i));
            back_tracking(s, index + 1);
            element.deleteCharAt(element.length() - 1);
        }

    }

}

6.大禮包

在商店中, 有 n 件在售的物品,每件物品都有對應的價格,然而,也有一些大禮包,每個大禮包以優惠的價格捆綁銷售一組物品,

給你一個整數陣列 price 表示物品價格,其中 price[i] 是第 i 件物品的價格,另有一個整數陣列 needs 表示購物清單,其中 needs[i] 是需要購買第 i 件物品的數量,

還有一個陣列 special 表示大禮包,special[i] 的長度為 n + 1 ,其中 special[i][j] 表示第 i 個大禮包中內含第 j 件物品的數量,且 special[i][n] (也就是陣列中的最后一個整數)為第 i 個大禮包的價格,

回傳 確切 滿足購物清單所需花費的最低價格,你可以充分利用大禮包的優惠活動,你不能購買超出購物清單指定數量的物品,即使那樣會降低整體價格,任意大禮包可無限次購買,

思路

先過濾掉沒用的禮包,然后再對滿足條件的禮包回溯選擇,對符合要求的組合進行計算選擇最小的那個

class Solution {
    int ans;
    int[] buy;

    public int BigPresent(int[] price, int[][] special, int[] need, int n) {
        buy = new int[price.length];
        for (int i = 0; i < price.length; i++) {
            ans += price[i] * need[i];
        }
        // 過濾掉無用的大禮包
        List<int[]> filterspecial = new ArrayList<int[]>();
        for (int[] sp : special) {
            int totalCount = 0, totalPrice = 0;
            boolean isadd = true;
            for (int i = 0; i < price.length; i++) {
                if (sp[i] > need[i]) { // 禮包本身所含的東西已經大于了的話就沒有必要
                    isadd = false;
                    break;
                }
                totalCount += sp[i];
                totalPrice += price[i] * sp[i];
            }
            if (isadd && totalCount > 0 && totalPrice > sp[price.length]) { // 如果夠優惠的話則可以選擇
                filterspecial.add(sp);
            }
        }
        if (filterspecial.size() == 0)
            return ans;
        back_tracking(price, filterspecial, need, 0, 0);
        return ans;
    }

    public void back_tracking(int[] price, List<int[]> special, int[] need, int money, int idx) {
        if (idx == special.size()) { // 遍歷完禮包后判斷是否值得
            int extra = 0;
            for (int i = 0; i < need.length; i++) {
                extra += (need[i] - buy[i]) * price[i];
            }
            ans = Math.min(ans, money + extra);
            return;
        }
        for (int i = idx; i <= special.size(); i++) {
            int j = 0;
            for (; j < need.length && i < special.size(); j++) { // 判斷該禮包是否可以選擇
                if (buy[j] + special.get(i)[j] > need[j]) {
                    break;
                }
            }
            if (j == need.length || i == special.size()) { // 已經確認可以購買或是已經遍歷完可以選擇的禮包
                for (j = 0; j < need.length && i < special.size(); j++) { // 購買
                    buy[j] += special.get(i)[j];
                }
                if (i < special.size()) {
                    money += special.get(i)[need.length]; // 付錢
                }
                back_tracking(price, special, need, money, i); // 遞回
                for (j = 0; j < need.length && i < special.size(); j++) {
                    buy[j] -= special.get(i)[j]; // 回溯退貨
                }
                if (i < special.size()) {
                    money -= special.get(i)[need.length]; // 回溯退款
                }
            }
        }
    }

}

7.分K個等和子串

Given an integer array nums and an integer k, return true if it is possible to divide this array into k non-empty subsets whose sums are all equal.

思路

先找到目標值,如果不能整除k就沒有意義,再對目標值進行回溯選擇,這里相當于分配k個桶,然后將數字裝到桶中直到剛好裝滿,所有數字能夠全部裝完就是符合條件,

class Solution {
    int n;
    int target;

    public boolean KEqualSumSubset(int[] nums, int k) {
        int sum = 0;
        n = nums.length;
        for (int i = 0; i < n; i++) {
            sum += nums[i];
        }
        if (sum % k != 0) // 如果不等于0說明沒辦法整除
            return false;
        // 降序排列,讓值大的先進行選擇,加快速度
        Arrays.sort(nums);
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
            left++;
            right--;
        }
        target = sum / k;
        int[] subsets = new int[k];
        return back_tracking(nums, subsets, 0, k);
    }

    public boolean back_tracking(int[] nums, int[] subsets, int index, int k) {
        if (index == nums.length) {
            return true;
        }
        for (int i = 0; i < k; i++) {
            if (i > 0 && subsets[i] == subsets[i - 1]) // 剪枝:第一個數字可以直接放進去,而相同的子集效果相同可以跳過
                continue;
            if (subsets[i] + nums[index] > target)
                continue;
            subsets[i] += nums[index];
            if (back_tracking(nums, subsets, index + 1, k))
                return true;
            subsets[i] -= nums[index];
        }
        return false;
    }

}

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/546911.html

標籤:Java

上一篇:Maven學習筆記1:Maven基本介紹和安裝配置

下一篇:學習筆記——Python基礎

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more