算法練習---藍橋杯 && 力扣(更新中ING)

算法練習
算法練習
1.(力扣練習)最大連續數字組(動態規劃)
2.完全揹包問題(動態規劃)
3.扔雞蛋問題(動態規劃)
4.(力扣練習)比特位計數(技巧)
5.(力扣練習)除自身以外數組的乘積(技巧)
6.(力扣練習)尋找重複數(三方法)
7.(力扣練習)顏色分類(兩方法)
8.(力扣練習)房間與鑰匙(DFS)
9.(力扣練習)使用最小花費爬樓梯(動態規劃)
10.(力扣練習)求衆數 II(三方法)
11.(力扣練習)相對名次(map+排序)
12.(力扣練習)有效的井字遊戲(邏輯判斷)
13.(力扣練習)翻轉圖像(模擬)
14.(力扣練習)三數之和(雙指針)
15.(力扣練習)四數之和(雙指針)
16.(藍橋杯-算法訓練)Bit Compressor(搜索)
17.(藍橋杯-算法訓練)大小寫轉換(循環)
18.(藍橋杯-算法訓練)字符串合併
19.(藍橋杯-算法訓練) 大等於n的最小完全平方數(細心)
20.(力扣練習)在排序數組中查找元素的第一個和最後一個位置(二分)
21.(力扣練習)腐爛的橘子(多源廣度搜索)
22.(力扣練習)將數組分成和相等的三個部分(雙指針)
23.(力扣練習)多數元素(摩爾投票法)
24.(力扣練習)零錢兌換(動態規劃)
25.(力扣練習)最長上升子序列(動態規劃)
26.(力扣練習)島嶼的最大面積(廣度搜索)
27.(力扣練習)有效括號的嵌套深度(對題目的理解)
28.(力扣練習)機器人的運動範圍(廣度搜索)
29.(力扣練習) 翻轉字符串裏的單詞(雙指針,API)
30.(力扣練習)兩數相加 II(棧,頭插法)
31.(力扣練習)除自身以外數組的乘積(左右乘積列表)
32(力扣練習)順時針打印矩陣(深度搜索)

1.(力扣練習)最大連續數字組(動態規劃)
給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。

方法一:動態規劃

思路:正數加正數總是大於他們各自本身。

public int maxSubArray(int[] arr) {
int res = arr[0];
int sum = 0;

for (int i = 0; i < arr.length; i++) {
if (sum > 0)
sum = sum + arr[i];
else
sum = arr[i];
res = res > sum ? res : sum;
}
return res;
}
方法二:分治法

public int func2(int[] arr, int left, int right) {
if (left == right)
return arr[left];
int mid = (left + right) / 2;

return Math.max(func2(arr, left, mid), Math.max(func2(arr, mid + 1, right), allSubPart(arr, left, mid, right)));
}

private int allSubPart(int[] arr, int left, int mid, int right) {
int leftSum = 0;
int sum = 0;
for (int i = mid; i >= left; i–) {
sum += arr[i];
if (sum > leftSum) {
leftSum = sum;
}
}
sum = 0;
int rightSum = Integer.MIN_VALUE;
for (int i = mid + 1; i <= right; i++) {
sum += arr[i];
if (sum > rightSum) {
rightSum = sum;
}
}
return leftSum + rightSum;
}
2.完全揹包問題(動態規劃)
有n種重量和價值分別爲w[i]、v[i](1≤i≤n)的物品,從這些物品中挑選總重量不超過W的物品,求出挑選物品價值總和最大的挑選方案,這裏每種物品可以挑選任意多件

n=4

W = 7

w = {0,3,4,2}

v = {0,4,5,3}

0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
1 0 0 0 dp[1,3]=max(dp[0,3],dp[1,3-w[1]]+v[1])=4 4 4 dp[1,6]=max(dp[0,6],dp[1,6-w[1]2]+v[1]2)=8 8
2 0 0 0 4 dp[2,4]=max(dp[1,4],dp[2,4-w[2]]+v[2])=5 5 8 dp[2,7]=max(dp[1,7],dp[2,7-w[2]]+v[2])=9
3 0 0 3 4 dp[3,4]=max(dp[2,4],dp[3,4-w[3]2]+v[3]2)=6 dp[3,5]=max(dp[2,5],dp[3,5-w[3]]+v[3])=7 9 10
動態轉移方程式:

							dp[i,j] = max(dp[i,j], max(dp[i -1,j], dp[i,j - w[i] * k] + v[i] * k))

									   = max(dp[i,j], max(dp[i - 1,j], dp[i,j - 1]))

public int solve(int n, int[] weight, int[] value, int W) {
int[][] dp = new int[n][W + 1];
for (int i = 1; i < n; i++) {
for (int j = 1; j <= W; j++) {
for (int k = 0; k * weight[i] <= j; k++) {
if (j - weight[i] >= 0)
dp[i][j] = Math.max(dp[i][j], Math.max(dp[i - 1][j], dp[i][j - weight[i] * k] + value[i] * k));
else
dp[i][j] = Math.max(dp[i][j], Math.max(dp[i - 1][j], dp[i][j - 1]));
}
}
}
return dp[n - 1][W];
}
3.扔雞蛋問題(動態規劃)
有2個雞蛋,從100層樓上往下扔,以此來測試雞蛋的硬度。比如雞蛋在第9層沒有摔碎,在第10層摔碎了,那麼雞蛋不會摔碎的臨界點就是9層。

問:如何用最少的嘗試次數,測試出雞蛋不會摔碎的臨界點?

思路:

設x爲最大測試次數,則第一次就從x層開始

硬度爲x-1,則剩下的雞蛋需要測試x-1,加上本次1,則爲x次,滿足假設

若硬度大於等於x,則下次從x-1+x層開始

若硬度爲x+x-2則,總共需要x次,滿足條件

若硬度大於等於x+x-1,則下次從x+x-1+x-2開始

最壞情況,硬度爲100,則總次數爲:

x+x-1+x-2+ … + 1 = 100

x^2 + x - 200 = 0

x = 14

動態轉移方程式:

dp[i,j]=min(max(dp[i-k,j],dp[k-1,j-1])+1)

public int solve(int n, int x) {
int[][] dp = new int[n + 1][x + 1];
for (int i = 0; i < n + 1; i++) {
dp[i][1] = i;
}
for (int i = 1; i < n + 1; i++) {
for (int j = 2; j < x + 1; j++) {
int min = Integer.MAX_VALUE;
for (int k = 1; k <= i; k++) {
int x1 = dp[i - k][j];
int x2 = dp[k - 1][j - 1];
int max = Math.max(x1, x2) + 1;
min = Math.min(min, max);
}
dp[i][j] = min;
}
}
return dp[n][x];
}
4.(力扣練習)比特位計數(技巧)
給定一個非負整數 num。對於 0 ≤ i ≤ num 範圍中的每個數字 i ,計算其二進制數中的 1 的數目並將它們作爲數組返回。

輸入: 2
輸出: [0,1,1]
輸入: 5
輸出: [0,1,1,2,1,2]
public int[] countBits(int num) {
int ans[]=new int[num+1];
for(int i=0;i<=num;i++){
ans[i]=Function(i);
}
return ans;
}
int Function(int n) {
/*
* 思路:
* 先每給進行計算,找到1,
* 然後每兩個,每四個,每八個,每十六個
* 0101 0101 0101 0101 0101 0101 0101 0101
* 0011 0011 0011 0011 0011 0011 0011 0011
* 0000 1111 0000 1111 0000 1111 0000 1111
* 0000 0000 1111 1111 0000 0000 1111 1111
* 0000 0000 0000 0000 1111 1111 1111 1111
*
* >> 右位移 << 左位移 <<< 無符號位移
* 爲什麼要右位移:
* 因爲0x55555555比較的是單數爲的,右位移後變成雙數位的也可以計算1的數量
* 此處可以改爲:
*
* 爲什麼不左位移:
* 因爲int行的數字,最大爲32爲整型,左位移可能超界
* */
n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
return n;
}
5.(力扣練習)除自身以外數組的乘積(技巧)
給定長度爲 n 的整數數組 nums,其中 n > 1,返回輸出數組 output ,其中 output[i] 等於 nums 中除 nums[i] 之外其餘各元素的乘積。

示例:

輸入: [1,2,3,4]
輸出: [24,12,8,6]
說明: 請不要使用除法,且在 O(n) 時間複雜度內完成此題。

進階:
你可以在常數空間複雜度內完成這個題目嗎?( 出於對空間複雜度分析的目的,輸出數組不被視爲額外空間。)

思路:

除自身外其他元素:分爲左邊部分,右邊部分

left:從左到右,依次累乘,每次賦值,然後累乘

right:從右到左,依次累乘,每次賦值,然後累乘

public int[] productExceptSelf(int[] nums) {
int len = nums.length;
int[] res = new int[len];

int right = 1;
int left = 1;

for (int i = 0; i < len; i++) {
res[i] = left;//初始化 對數據進行賦值
left *= nums[i];
}
for (int i = len - 1; i >= 0; i–) {
res[i] *= right;//對數據進行二次賦值
right *= nums[i];
}

return res;
}
6.(力扣練習)尋找重複數(三方法)
給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

示例 1:

輸入: [1,3,4,2,2]
輸出: 2
示例 2:

輸入: [3,1,3,4,2]
輸出: 3
說明:

不能更改原數組(假設數組是隻讀的)。
只能使用額外的 O(1) 的空間。
時間複雜度小於 O(n^2) 。
數組中只有一個重複的數字,但它可能不止重複出現一次。

方法一:快慢指針

思路:

使用快慢指針找到循環體,然後依次遍歷循環體和本體,找到重複值:

public int findDuplicate(int[] nums) {
//循環檢測–快慢指針
int fast = nums[0];
int slow = nums[0];
do {//找到循環
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
int ptr1 = nums[0];
int ptr2 = fast;
while (ptr1 != ptr2) {//循環和數組間的遍歷,找到重複元素
ptr1 = nums[ptr1];
ptr2 = nums[ptr2];
}
return ptr1;
}
方法二:排序依次遍歷

public int findDuplicate(int[] nums) {
Arrays.sort(nums);
int ptr = 1;
while (nums[ptr] != nums[ptr - 1])
ptr++;
return nums[ptr];
}
方法三:使用set特性保存數組

public int findDuplicate(int[] nums) {
Set set = new HashSet<>();
for (int i = 0, len = nums.length; i < len; i++) {
if (set.contains(nums[i]))
return nums[i];
set.add(nums[i]);
}
return -1;
}
7.(力扣練習)顏色分類(兩方法)
給定一個包含紅色、白色和藍色,一共 n 個元素的數組,原地對它們進行排序,使得相同顏色的元素相鄰,並按照紅色、白色、藍色順序排列。

此題中,我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。

注意:
不能使用代碼庫中的排序函數來解決這道題。

示例:

輸入: [2,0,2,1,1,0]
輸出: [0,0,1,1,2,2]
進階:

一個直觀的解決方案是使用計數排序的兩趟掃描算法。
首先,迭代計算出0、1 和 2 元素的個數,然後按照0、1、2的排序,重寫當前數組。
你能想出一個僅使用常數空間的一趟掃描算法嗎?

方法一:快速排序(O(nlogn))

public void sortColors(int[] nums) {
quickSort(nums, 0, nums.length - 1);
}

private void quickSort(int[] nums, int right, int left) {
    if (right >= left)
        return;
    else {
        int r = right;
        int l = left;
        int temp = nums[r];
        while (r != l) {
            while (r != l) {
                if (nums[l] < temp) {
                    nums[r] = nums[l];
                    break;
                } else
                    l--;
            }
            while (r != l) {
                if (nums[r] > temp) {
                    nums[l] = nums[r];
                    break;
                } else
                    r++;
            }
        }
        int index = r;
        nums[index] = temp;
        quickSort(nums, right, index - 1);
        quickSort(nums, index + 1, left);
    }
}

方法二:荷蘭國旗問題

思路:

用三個指針:

				left 當ptr值爲0時與left替換,left自加,ptr自加(保證left之前一定是0)

				right 當ptr爲2時與right替換,right自減(保證right之後的一定是2,ptr不變,因爲可能替換後是1,0,所以交給下一次循環判斷,是否是1,或者0)

public void sortColors(int[] nums) {
/*
* 思路:
* left 當ptr值爲0時與left替換,left自加,ptr自加
* right 當ptr爲2時與right替換,right自減,ptr自加
* */
int left = 0;
int right = nums.length - 1;
int ptr = 0;
int temp;
while (ptr <= right) {
if (nums[ptr] == 0) {
temp = nums[ptr];
nums[ptr] = nums[left];
nums[left] = temp;
left++;
ptr++;
} else if (nums[ptr] == 2) {
temp = nums[ptr];
nums[ptr] = nums[right];
nums[right] = temp;
right–;
} else
ptr++;
}
}
8.(力扣練習)房間與鑰匙(DFS)
有 N 個房間,開始時你位於 0 號房間。每個房間有不同的號碼:0,1,2,…,N-1,並且房間裏可能有一些鑰匙能使你進入下一個房間。

在形式上,對於每個房間 i 都有一個鑰匙列表 rooms[i],每個鑰匙 roomsi 由 [0,1,…,N-1] 中的一個整數表示,其中 N = rooms.length。

鑰匙 roomsi = v 可以打開編號爲 v 的房間。最初,除 0 號房間外的其餘所有房間都被鎖住。

你可以自由地在房間之間來回走動。如果能進入每個房間返回 true,否則返回 false。

方法一:DFS

思路:

通過第一次遍歷進入房間,然後通過該房間鑰匙依次進入下一個房間,若房間中鑰匙遍歷完則返回。

public boolean canVisitAllRooms(List<List> rooms) {
int size = rooms.size();
boolean[] openRooms = new boolean[size];//設置房間數量
visitRoom(rooms, 0, openRooms);//進入第一個房間
for (boolean b : openRooms) {//判斷是否每個房間都以開放
if(!b)
return false;
}
return true;
}

private void visitRoom(List<List<Integer>> rooms, int room, boolean[] openRooms) {
    //如果進入過該房間則退出,否則進入該房間,並遍歷所有的鑰匙
    if (openRooms[room])
        return;
    else {
        openRooms[room] = true;
        for (int i = 0, size = rooms.get(room).size(); i < size; i++) {
            int index = rooms.get(room).get(i);//獲取下一個鑰匙
            visitRoom(rooms, index, openRooms);//進入對應的房間
        }
    }
}

9.(力扣練習)使用最小花費爬樓梯(動態規劃)
數組的每個索引做爲一個階梯,第 i個階梯對應着一個非負數的體力花費值 costi。

每當你爬上一個階梯你都要花費對應的體力花費值,然後你可以選擇繼續爬一個階梯或者爬兩個階梯。

您需要找到達到樓層頂部的最低花費。在開始時,你可以選擇從索引爲 0 或 1 的元素作爲初始階梯。

方法一:動態規劃

思路:

dp方程式:

	dp[i] = cost[i] + min(dp[i-1],dp[i-2])(i>=2)

public int minCostClimbingStairs(int[] cost) {
//原始dp動態方程式
int len = cost.length;
int[] dp = new int[len + 1];

    for (int i = 0; i < len + 1; i++) {
        if (i == 0 || i == 1)
            dp[i] = 0;
        else {
            dp[i] = Math.min(dp[i - 1] + cost[i-1], dp[i - 2] + cost[i-2]);
        }
    }

    return dp[len];
}

public int minCostClimbingStairs(int[] cost) {
//第一次優化dp動態方程式,在原來數組基礎上保存數據
int len = cost.length;

    for (int i = 2; i < len; i++) {
        cost[i] = cost[i] + Math.min(cost[i - 1], cost[i - 2]);
    }

    return Math.min(cost[len - 1], cost[len - 2]);
}

public int minCostClimbingStairs(int[] cost) {
//第二次優化,使用兩個變量保存每次更新的兩個值
int len = cost.length;
int f1 = 0, f2 = 0;

    for (int i = len - 1; i >= 0; --i) {//倒序查詢
        int f0 = cost[i] + Math.min(f1, f2);
        f2 = f1;
        f1 = f0;
    }
    return Math.min(f1, f2);
}

10.(力扣練習)求衆數 II(三方法)
給定一個大小爲 n 的數組,找出其中所有出現超過 ⌊ n/3 ⌋ 次的元素。

說明: 要求算法的時間複雜度爲 O(n),空間複雜度爲 O(1)。

方法一:暴力法

思路:

使用map計數

public List majorityElement(int[] nums) {
//原始方法,使用map計數(效率低,內存消耗高,書寫簡單)
Map<Integer, Integer> map = new HashMap<>();
List ans = new ArrayList<>();
for (int i = 0, len = nums.length; i < len; i++) {
if (map.containsKey(nums[i]))
map.put(nums[i], map.get(nums[i]) + 1);
else
map.put(nums[i], 1);
if (map.get(nums[i]) > len / 3 && !ans.contains(nums[i]))
ans.add(nums[i]);
}
return ans;
}
方法二:排序比較

思路:

排序然後然後尋找大於1/3的數

大於1/3的數必然出現在1/6,1/2,5/6的位置,

所以我們記錄此位置的數字,

然後只要記錄出現次數,則可以知道大於1/3的元素

public List majorityElement(int[] nums) {
// 排序,然後計數(效率提高,內存消耗沒有改變)
List ans = new ArrayList<>();
if (nums.length == 0)
return ans;
Arrays.sort(nums);
int len = nums.length;
int A = nums[len / 6], B = nums[len / 2], C = nums[len * 5 / 6];
int countA = 0, countB = 0, countC = 0;
for (int i = 0; i < len; i++) {
if (nums[i] == A)
countA++;
if (nums[i] == B)
countB++;
if (nums[i] == C)
countC++;
if (countA > len / 3 && !ans.contains(A))
ans.add(A);
if (countB > len / 3 && !ans.contains(B))
ans.add(B);
if (countC > len / 3 && !ans.contains©)
ans.add©;
}
return ans;
}
方法三:摩爾計數法

思路:

因爲題目要求選出大於1/3的元素,

可知最多有兩個元素

所以使用摩爾投票法,設置兩個‘代表’

然後使用摩爾投票方法:

若是選擇元素則加一,

若不是選擇元素則減一,

若減至零,則換‘代表’

然後判斷選擇的代表是否全部滿足要求

public List majorityElement(int[] nums) {
// 摩爾投票法
List ans = new ArrayList<>();
int A = nums[0], B = nums[0];//設置候選組
int countA = 0, countB = 0;
for (int i = 0, len = nums.length; i < len; i++) {
if (nums[i] == A) {//若當前值爲A則加
countA++;
} else if (nums[i] == B) {
countB++;
} else if (countA == 0) {//若countA爲零則換值
A = nums[i];
countA++;
} else if (countB == 0) {
B = nums[i];
countB++;
} else {
countA–;
countB–;
}
}
countA = countB = 0;
for (int i = 0, len = nums.length; i < len; i++) {//重新比較,是當前選擇的元素是否大於1/3
if (nums[i] == A) {
countA++;
}
if (nums[i] == B) {
countB++;
}
if (countA > len / 3 && !ans.contains(A)) {
ans.add(A);
continue;
}
if (countB > len / 3 && !ans.contains(B)) {
ans.add(B);
continue;
}
}
return ans;
}
11.(力扣練習)相對名次(map+排序)
給出 N 名運動員的成績,找出他們的相對名次並授予前三名對應的獎牌。前三名運動員將會被分別授予 “金牌”,“銀牌” 和“ 銅牌”(“Gold Medal”, “Silver Medal”, “Bronze Medal”)。

(注:分數越高的選手,排名越靠前。)

方法一:map+排序

思路:

先用map保存當前數組的相對位置,

在排序,然後根據排序後的位置,

用相對位置得到原來位置處的名次

public String[] findRelativeRanks(int[] nums) {
// map+排序
Map<Integer, Integer> map = new HashMap<>();
int len = nums.length;
String[] ans = new String[len];
for (int i = 0; i < len; i++)
map.put(nums[i], i);//保存排序前的相對位置
Arrays.sort(nums);//排序
int gold = 4;
for (int i = len - 1; i >= 0; i–) {
if (i == len - 1)
ans[map.get(nums[i])] = “Gold Medal”;//獲取排序前的相對位置,然後保存在排序前的相對位置
else if (i == len - 2)
ans[map.get(nums[i])] = “Silver Medal”;
else if (i == len - 3)
ans[map.get(nums[i])] = “Bronze Medal”;
else
ans[map.get(nums[i])] = “” + gold++;
}
return ans;
}
12.(力扣練習)有效的井字遊戲(邏輯判斷)
用字符串數組作爲井字遊戲的遊戲板 board。當且僅當在井字遊戲過程中,玩家有可能將字符放置成遊戲板所顯示的狀態時,才返回 true。

該遊戲板是一個 3 x 3 數組,由字符 " ",“X” 和 “O” 組成。字符 " " 代表一個空位。

以下是井字遊戲的規則:

玩家輪流將字符放入空位(" ")中。
第一個玩家總是放字符 “X”,且第二個玩家總是放字符 “O”。
“X” 和 “O” 只允許放置在空位中,不允許對已放有字符的位置進行填充。
當有 3 個相同(且非空)的字符填充任何行、列或對角線時,遊戲結束。
當所有位置非空時,也算爲遊戲結束。
如果遊戲結束,玩家不允許再放置字符。

方法一:分類討論

思路:

當 X < O,X >= O + 2 返回false;

當 X == O,判斷X是否三連,是則返回false;

當 X == O + 1,判斷O是否三連,是則返回false;

以上情況均不滿足則返回true

public boolean validTicTacToe(String[] board) {

    int X = 0, O = 0;
    for (int i = 0, len = board.length; i < len; i++) {
        for (int j = 0; j < 3; j++) {
            char c = board[i].charAt(j);
            if (c == 'X')
                X++;
            if (c == 'O')
                O++;
        }
    }
    if (X < O)
        return false;
    if (X >= O + 2)
        return false;
    //判斷是否只有唯一一種顏色三連
    if (X == O && judge(board, 'X'))
        return false;
    if (X == O + 1 && judge(board, 'O'))
        return false;
    return true;
}

private boolean judge(String[] board, char c) {
    if (board[0].charAt(0) == c &&
            board[0].charAt(1) == board[0].charAt(0) &&
            board[0].charAt(2) == board[0].charAt(0))
        return true;
    if (board[0].charAt(0) == c &&
            board[1].charAt(0) == board[0].charAt(0) &&
            board[2].charAt(0) == board[0].charAt(0))
        return true;
    if (board[0].charAt(0) == c &&
            board[1].charAt(1) == board[0].charAt(0) &&
            board[2].charAt(2) == board[0].charAt(0))
        return true;
    if (board[0].charAt(1) == c &&
            board[0].charAt(1) == board[1].charAt(1) &&
            board[2].charAt(1) == board[0].charAt(1))
        return true;
    if (board[0].charAt(2) == c &&
            board[0].charAt(2) == board[1].charAt(2) &&
            board[0].charAt(2) == board[2].charAt(2))
        return true;
    if (board[0].charAt(2) == c &&
            board[1].charAt(1) == board[0].charAt(2) &&
            board[0].charAt(2) == board[2].charAt(0))
        return true;
    if (board[1].charAt(0) == c &&
            board[1].charAt(1) == board[1].charAt(0) &&
            board[1].charAt(0) == board[1].charAt(2))
        return true;

    if (board[2].charAt(0) == c &&
            board[2].charAt(1) == board[2].charAt(0) &&
            board[2].charAt(0) == board[2].charAt(2))
        return true;
    return false;
}

13.(力扣練習)翻轉圖像(模擬)
給定一個二進制矩陣 A,我們想先水平翻轉圖像,然後反轉圖像並返回結果。

水平翻轉圖片就是將圖片的每一行都進行翻轉,即逆序。例如,水平翻轉 [1, 1, 0] 的結果是 [0, 1, 1]。

反轉圖片的意思是圖片中的 0 全部被 1 替換, 1 全部被 0 替換。例如,反轉 [0, 1, 1] 的結果是 [1, 0, 0]。

方法一:模擬

思路:

在原來數組上進行翻轉和反轉

把原來數組分成兩邊,兩邊對換,然後在對換過程中進行反轉

public int[][] flipAndInvertImage(int[][] A) {
// 原本數組上進行交換和翻轉
int len = A[0].length;
for (int i = 0, size = A.length; i < size; i++) {
for (int j = 0; j < (len + 1) / 2; j++) { // 取當前數組的一半
int temp = A[i][j] ^ 1;// 對左邊的元素進行反轉
A[i][j] = A[i][len - j - 1] ^ 1;// 交換兩邊元素,對右邊元素進行反轉
A[i][len - j - 1] = temp;
}
}
return A;
}
14.(力扣練習)三數之和(雙指針)
給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。

注意:答案中不可以包含重複的三元組。

方法一:回溯(超時)

public List<List> threeSum(int[] nums) {
// 回溯 超時
List<List> ans = new ArrayList<>();
if (nums.length <= 2 || nums == null)
return ans;
Arrays.sort(nums);
func(ans, new ArrayList(), nums, 0);
return ans;
}

private void func(List<List<Integer>> ans, ArrayList<Integer> list, int[] nums, int count) {
    if (list.size() == 3) {
        if (list.get(0) + list.get(1) + list.get(2) == 0)
            ans.add(new ArrayList<>(list));
        else
            return;
    } else {
        if (list.size() == 0 && nums[count] > 0)// 大於零則退出循環
            return;
        for (int i = count, len = nums.length; i < len; i++) {
            if (i > count && nums[i] == nums[i - 1])// 去重
                continue;
            list.add(nums[i]);
            func(ans, list, nums, i + 1);
            list.remove(list.size() - 1);
        }
    }
}

方法二:雙指針

思路:

第一個指針 i 指向第一個數字,1.去重,2.保證第一個數非正數

第二個指針 L 指向第二個數字,1.內部去重

第三個指針 R 指向第三個數字,1.內部去重

public List<List> threeSum(int[] nums) {
// 雙指針
List<List> ans = new ArrayList<>();
if (nums.length <= 2 || nums == null)
return ans;
Arrays.sort(nums);
for (int i = 0, len = nums.length; i < len; i++) {
if (nums[i] > 0)// 大於零則退出
break;
if (i > 0 && nums[i] == nums[i - 1]) continue; // 外部去重
int L = i + 1;
int R = len - 1;
while (L < R) {
int sum = nums[i] + nums[L] + nums[R];
if (sum == 0) {
ans.add(Arrays.asList(nums[i], nums[R], nums[L]));
// 內部去重
while (L < R && nums[L] == nums[L + 1]) L++;
while (L < R && nums[R] == nums[R - 1]) R–;
L++;
R–;
}
else if (sum < 0) L++;
else if (sum > 0) R–;
}
}
return ans;
}
15.(力扣練習)四數之和(雙指針)
給定一個包含 n 個整數的數組 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。

注意:

答案中不可以包含重複的四元組。

方法一:雙指針

思路:

與14題類似

public List<List> fourSum(int[] nums, int target) {
List<List> ans = new ArrayList<>();
if (nums.length < 4 || nums == null)
return ans;
Arrays.sort(nums);//排序
for (int i = 0, len = nums.length; i < len; i++) {//取第一個數字
if (i > 0 && nums[i] == nums[i - 1])//第一次去重 去除與第一個數字相同的結果
continue;
for (int j = i + 1; j < len; j++) {//取第二個數字
if (j > i + 1 && nums[j] == nums[j - 1])//第二次去重,去除與第二個數字相同的結果
continue;
int L = j + 1;//取第三個數字
int R = len - 1;//取第四個數字
while (L < R) {//遍歷內部所有情況
int sum = nums[i] + nums[j] + nums[L] + nums[R];
if (sum == target) {
ans.add(Arrays.asList(nums[i], nums[j], nums[L], nums[R]));//相等保存
while (L < R && nums[L] == nums[L + 1]) L++;//第三次去重,去重與第三個數字相同的結果
while (L < R && nums[R] == nums[R - 1]) R–;//第四次去重,去重與第四個數字相同的結果
L++;
R–;
} else if (sum < target) L++;
else if (sum > target) R–;
}
}
}
return ans;
}
可以優化十秒

public List<List> fourSum(int[] nums, int target) {
List<List> ans = new ArrayList<>();
if (nums.length < 4 || nums == null)
return ans;
Arrays.sort(nums);
for (int i = 0, len = nums.length; i <= len - 4; i++) {
if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target)//優化判斷當前開始遍歷情況最小值是否大於目標值
break;
if (i > 0 && nums[i] == nums[i - 1])
continue;
for (int j = i + 1; j <= len - 3; j++) {
if (j > i + 1 && nums[j] == nums[j - 1])
continue;
if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target)//優化遍歷當前內部遍歷情況最小值是否大於目標值
continue;
int L = j + 1;
int R = len - 1;
while (L < R) {
int sum = nums[i] + nums[j] + nums[L] + nums[R];
if (sum == target) {
ans.add(Arrays.asList(nums[i], nums[j], nums[L], nums[R]));
while (L < R && nums[L] == nums[L + 1]) L++;
while (L < R && nums[R] == nums[R - 1]) R–;
L++;
R–;
} else if (sum < target) L++;
else if (sum > target) R–;
}
}
}
return ans;
}
16.(藍橋杯-算法訓練)Bit Compressor(搜索)
數據壓縮的目的是爲了減少存儲和交換數據時出現的冗餘。這增加了有效數據的比重並提高了傳輸速率。有一種壓縮二進制串的方法是這樣的:
  加粗樣式將連續的n個1替換爲n的二進制表示(注:替換髮生當且僅當這種替換減少了二進制串的總長度)
  (譯者注:連續的n個1的左右必須是0或者是串的開頭、結尾)
  比如:11111111001001111111111111110011會被壓縮成10000010011110011。原串長爲32,被壓縮後串長爲17.
  這種方法的弊端在於,有時候解壓縮算法會得到不止一個可能的原串,使得我們無法確定原串究竟是什麼。請你寫一個程序來判定我們能否利用壓縮後的信息來確定原串。給出原串長L,原串中1的個數N,以及壓縮後的串。
  L<=16 Kbytes,壓縮後的串長度<=40 bits。
輸入格式
  第一行兩個整數L,N,含義同問題描述
  第二行一個二進制串,表示壓縮後的串
輸出格式
  輸出"YES"或"NO"或"NOT UNIQUE"(不包含引號)
  分別表示:
  YES:原串唯一
  NO:原串不存在
  NOT UNIQUE:原串存在但不唯一

方法一:dfs(借鑑:https://blog.csdn.net/yi_qing_z/article/details/88084875)

思路:

注意:1,10,11,110情況

int L, l, N;
char[] data = new char[50];
int answer;

public int fun(int L, int N, String str) {
    this.L = L;
    this.N = N;
    l = str.length();
    for (int i = 1; i < l + 1; i++)
        data[i] = str.charAt(i - 1);
    dfs(1, 0, 0);
    return answer;
}

/*
* i:指向短串位置的指針
* length:解壓後串的長度
* num_1:解壓後1的個數
* */
void dfs(int i, long length, long num_1) { //i是原串第i個字符,length是變換後的串長,num_1是變換後串的1累計個數

    /*
     * 思路:
     * 連續n個1的壓縮條件
     * 1.兩頭爲開始,結束
     * 2.一頭爲開始或者結束,一頭爲0
     * 3.兩頭爲0
     * 4.壓縮長度必須大於2
     *   所以:1,10,11,110此類不能壓縮
     * */
    if (i == l) {       //最後一個位置
        dfs(i + 1, length + 1, num_1 + data[i] - '0');
        return;
    }
    if (i > l) {   //結束條件
        if (length == L && num_1 == N)
            answer++;
        return;
    }
    if (length > L || num_1 > N || answer >= 2)    //不符合條件,返回
        return;
    /*
     * 1. 0   不解壓
     * 2. 11  不解壓
     * */
    if (data[i] == '0') {
        dfs(i + 1, length + 1, num_1);
        return;
    }
    if (data[i - 1] == '1')
        return;
    long temp = 0;
    /*
     * 10
     * 110
     * 可能解壓
     * */
    if (data[i + 1] == '0')
        dfs(i + 1, length + 1, num_1 + 1);
    if (data[i + 1] == '1' && data[i + 2] != '1')
        dfs(i + 2, length + 2, num_1 + 2);
    for (int j = i; j <= l; j++) {
        temp *= 2;//從i開始轉化二進制位10進制,計算1的數量
        temp += data[j] - '0';
        if (temp + num_1 > N || temp + length > L)
            break;
        if (temp > j - i + 1 && data[j + 1] != '1')        //壓縮的串1比原來多,並且下一個位置爲0或者末尾,代表轉換結束
            dfs(j + 1, temp + length, num_1 + temp);
    }
}

17.(藍橋杯-算法訓練)大小寫轉換(循環)
輸入一個字符串,將大寫字符變成小寫、小寫變成大寫,然後輸出

方法一:循環

public String changeString(String str) {
String ans = “”;
for (int i = 0, len = str.length(); i < len; i++) {
char c = str.charAt(i);
if (c >= ‘a’ && c <= ‘z’)
ans += (char) (c - 32);
else if (c >= ‘A’ && c <= ‘Z’)
ans += (char) (c + 32);
else
ans += c;
}
return ans;
}
18.(藍橋杯-算法訓練)字符串合併
輸入兩個字符串,將其合併爲一個字符串後輸出。

方法一:

public String addString(String str1, String str2) {
return str1 + str2;
}
19.(藍橋杯-算法訓練) 大等於n的最小完全平方數(細心)
輸出大等於n的最小的完全平方數。
若一個數能表示成某個自然數的平方的形式,則稱這個數爲完全平方數
Tips:注意數據範圍

方法一:

思路:

n<=0

n==最小完全平方數

n>最小完全平方數

注意範圍,使用long保存數字

public static long getTheMinNum(long n) {
if (n <= 0)
return 0;
long temp = (long) Math.sqrt(n);
if (temp * temp == n)
return n;
temp++;
return temp * temp;
}
20.(力扣練習)在排序數組中查找元素的第一個和最後一個位置(二分)
給定一個按照升序排列的整數數組 nums,和一個目標值 target。找出給定目標值在數組中的開始位置和結束位置。

你的算法時間複雜度必須是 O(log n) 級別。

如果數組中不存在目標值,返回 [-1, -1]。

方法一:二分

//二分 最優
public int[] searchRange(int[] nums, int target) {
    int[] ans = {-1, -1};
    int index = (0 + nums.length) / 2;
    if (nums.length != 0)
        find(ans, nums, target, index, 0, nums.length - 1);
    return ans;
}

private void find(int[] ans, int[] nums, int target, int index, int l, int r) {
    if (nums[index] == target) {//當前值等於目標值
        int left = index;
        int right = index;
        while (left >= 0 && nums[left] == target)
            left--;
        while (right <= nums.length - 1 && nums[right] == target)
            right++;
        ans[0] = ++left;
        ans[1] = --right;
        return;
    }
    if (l >= r) {//不滿足條件
        return;
    }
    if (l < r) {
        if (nums[index] < target) {//當前值小於目標值
            find(ans, nums, target, (r + index + 1) / 2, index + 1, r);
        } else {//當前值大於目標值
            find(ans, nums, target, (l + index - 1) / 2, l, index - 1);
        }
    }
}

方法二:雙指針

思路:

線性搜索

// 雙指針
public int[] searchRange(int[] nums, int target) {
int[] ans = {-1, -1};
if (nums.length != 0) {
int l = 0;
int r = nums.length - 1;
while (l <= r) {
if (nums[l] == target)//左指針指向目標值
ans[0] = l;
if (nums[r] == target)//右指針指向目標值
ans[1] = r;
if (ans[0] == -1)//左指針移動
l++;
if (ans[1] == -1)//右指針移動
r–;
if (ans[0] != -1 && ans[1] != -1)
break;
}
}
return ans;
}
21.(力扣練習)腐爛的橘子(多源廣度搜索)
在給定的網格中,每個單元格可以有以下三個值之一:

值 0 代表空單元格;
值 1 代表新鮮橘子;
值 2 代表腐爛的橘子。
每分鐘,任何與腐爛的橘子(在 4 個正方向上)相鄰的新鮮橘子都會腐爛。

返回直到單元格中沒有新鮮橘子爲止所必須經過的最小分鐘數。如果不可能,返回 -1。

方法一:多源廣度搜索

思路:

用隊列保存以腐爛的橘子,

用鍵值對保存橘子開始腐爛的

int[] change_I = {1, 0, -1, 0};
int[] change_J = {0, 1, 0, -1};

public int orangesRotting(int[][] grid) {
    int ans = 0;
    int I = grid.length;
    int J = grid[0].length;
    Map<Integer, Integer> map = new HashMap<>();
    Queue<Integer> queue = new ArrayDeque<>();
    for (int i = 0; i < I; i++)
        for (int j = 0; j < J; j++) {
            if (grid[i][j] == 2) {
                int index = i * J + j;//記錄位置
                queue.add(index);
                map.put(index, 0);//記錄當前位置腐敗時間
            }
        }
    while (!queue.isEmpty()) {
        int index = queue.remove();
        int i = index / J;
        int j = index % J;
        for (int k = 0; k < 4; k++) {
            int newI = i + change_I[k];
            int newJ = j + change_J[k];
            if (0 <= newI && newI < I && 0 <= newJ && newJ < J && grid[newI][newJ] == 1) {
                grid[newI][newJ] = 2;
                int newIndex = newI * J + newJ;//保存下一個腐爛的橘子
                queue.add(newIndex);
                int time = map.get(index) + 1;
                map.put(newIndex, time);
                ans = time;
            }
        }
    }
    for (int[] i : grid)//查詢是否有未腐爛的橘子
        for (int j : i)
            if (j == 1)
                return -1;
    return ans;
}

22.(力扣練習)將數組分成和相等的三個部分(雙指針)
給你一個整數數組 A,只有可以將其劃分爲三個和相等的非空部分時才返回 true,否則返回 false。

形式上,如果可以找出索引 i+1 < j 且滿足 (A[0] + A[1] + … + A[i] == A[i+1] + A[i+2] + … + A[j-1] == A[j] + A[j-1] + … + A[A.length - 1]) 就可以將數組三等分。

方法一:雙指針

思路:

計算出平均值,指定兩個指針,一個從前,一個從後,進行檢索,知道分別滿足條件,及分成三分

public boolean canThreePartsEqualSum(int[] A) {
int len = A.length;
if (len < 3)//長度小於3返回false
return false;
int sum = 0;
for (int i = 0; i < len; i++)
sum += A[i];//求總長
if (sum % 3 != 0)
return false;
int l = 0;
int r = len - 1;
int average = sum / 3;
int averageL = A[l];
int averageR = A[r];
while (l < r) {
if (averageL == average && averageR == average && l + 1 < r) //滿足條件返回true
return true;
//兩邊開始遍歷,找到平均節點
if (averageL == average)
averageR += A[–r];
else if (averageR == average)
averageL += A[++l];
else {
averageR += A[–r];
averageL += A[++l];
}

    }
    return false;
}

23.(力扣練習)多數元素(摩爾投票法)
給定一個大小爲 n 的數組,找到其中的多數元素。多數元素是指在數組中出現次數大於 ⌊ n/2 ⌋ 的元素。

你可以假設數組是非空的,並且給定的數組總是存在多數元素。

方法一:排序

思路:先排序,然後取中值

//排序 2ms 41.9MB
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
方法二:摩爾投票法

思路:使用一個臨時變量保存當前值的個數,當爲零的時候換下一個數重新開始計數

//摩爾投票法 3ms 42.1MB
public int majorityElement(int[] nums) {
int ans = nums[0];
int len = nums.length;
int count = 1;//計數
int i = 1;
while (count <= len / 2 && i < len) {
if (nums[i] != ans) {//不等於當前值
count–;
} else {//等於當前值
count++;
}
if (count == 0) {//當計數器爲零時
ans = nums[i];
count++;
}
i++;
}
return ans;
}
24.(力扣練習)零錢兌換(動態規劃)
給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。

方法一:動態規劃(自下而上)

思路:

public int coinChange(int[] coins, int amount) {
int len = coins.length;
int[] dp = new int[amount + 1];
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
int min = -1;
for (int j = 0; j < len; j++) {
int index = i - coins[j];
if (index >= 0 && dp[index] != -1) {
if (min == -1)
min = dp[index] + 1;
else
min = Math.min(min, dp[index] + 1);
} else
continue;
}
dp[i] = min;
}
return dp[amount];
}
25.(力扣練習)最長上升子序列(動態規劃)
給定一個無序的整數數組,找到其中最長上升子序列的長度。

方法一:動態規劃

思路:

public int lengthOfLIS(int[] nums) {
int ans = 0;
int len = nums.length;
int[] dp = new int[len];
if (len != 0)
dp[0] = ans = 1;
else
return ans;
for (int i = 1; i < len; i++) {
int max = 1;
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i])
max = Math.max(max, dp[j] + 1);
}
dp[i] = max;
ans = ans > max ? ans : max;
}
return ans;
}
26.(力扣練習)島嶼的最大面積(廣度搜索)
給定一個包含了一些 0 和 1的非空二維數組 grid , 一個 島嶼 是由四個方向 (水平或垂直) 的 1 (代表土地) 構成的組合。你可以假設二維矩陣的四個邊緣都被水包圍着。

找到給定的二維數組中最大的島嶼面積。(如果沒有島嶼,則返回面積爲0。

方法一:廣度搜索

思路:

int[] changeX = {1, 0, -1, 0};
int[] changeY = {0, 1, 0, -1};

public int maxAreaOfIsland(int[][] grid) {
    int ans = 0;
    Queue<Integer> queue = new ArrayDeque<>();
    int X = grid.length;
    int Y = grid[0].length;
    for (int i = 0; i < X; i++) {
        for (int j = 0; j < Y; j++) {
            if (grid[i][j] == 1) {
                int max = 1;
                grid[i][j] = 0;
                int index = i * Y + j;
                queue.add(index);
                while (!queue.isEmpty()) {
                    int newIndex = queue.poll();
                    for (int k = 0; k < 4; k++) {
                        int newX = newIndex / Y + changeX[k];
                        int newY = newIndex % Y + changeY[k];
                        if (0 <= newX && newX < X && 0 <= newY && newY < Y && grid[newX][newY] == 1) {
                            max++;
                            queue.add(newX * Y + newY);
                            grid[newX][newY] = 0;
                        }
                    }
                }
                ans = ans > max ? ans : max;
            }
        }
    }
    return ans;
}

27.(力扣練習)有效括號的嵌套深度(對題目的理解)
有效括號字符串 僅由 “(” 和 “)” 構成,並符合下述幾個條件之一:

空字符串
連接,可以記作 AB(A 與 B 連接),其中 A 和 B 都是有效括號字符串
嵌套,可以記作 (A),其中 A 是有效括號字符串
類似地,我們可以定義任意有效括號字符串 s 的 嵌套深度 depth(S):

s 爲空時,depth("") = 0
s 爲 A 與 B 連接時,depth(A + B) = max(depth(A), depth(B)),其中 A 和 B 都是有效括號字符串
s 爲嵌套情況,depth("(" + A + “)”) = 1 + depth(A),其中 A 是有效括號字符串
例如:"","()()",和 “()(()())” 都是有效括號字符串,嵌套深度分別爲 0,1,2,而 “)(” 和 “(()” 都不是有效括號字符串。

給你一個有效括號字符串 seq,將其分成兩個不相交的子序列 A 和 B,且 A 和 B 滿足有效括號字符串的定義(注意:A.length + B.length = seq.length)。

現在,你需要從中選出 任意 一組有效括號字符串 A 和 B,使 max(depth(A), depth(B)) 的可能取值最小。

返回長度爲 seq.length 答案數組 answer ,選擇 A 還是 B 的編碼規則是:如果 seq[i] 是 A 的一部分,那麼 answer[i] = 0。否則,answer[i] = 1。即便有多個滿足要求的答案存在,你也只需返回 一個。

示例 1:

輸入:seq = “(()())”
輸出:[0,1,1,1,1,0]
示例 2:

輸入:seq = “()(())()”
輸出:[0,0,0,1,1,0,1,1]

方法一:找規律

思路:

把括號分成兩部分,每部分最深層數只有一層->根據括號位置,給括號編號,奇數爲1,偶數爲0

public int[] maxDepthAfterSplit(String seq) {
int[] ans = new int[seq.length()];
int idx = 0;
for (char c : seq.toCharArray()) {
/*
* 對於‘(’奇數在一層,偶數在零層
* 對於‘)’相對左括號想右偏移一,所以判斷層數時加一
*/
ans[idx++] = c == ‘(’ ? idx & 1 : ((idx + 1) & 1);
}
return ans;
}
28.(力扣練習)機器人的運動範圍(廣度搜索)
地上有一個m行n列的方格,從座標 [0,0] 到座標 [m-1,n-1] 。一個機器人從座標 [0, 0] 的格子開始移動,它每次可以向左、右、上、下移動一格(不能移動到方格外),

也不能進入行座標和列座標的數位之和大於k的格子。例如,當k爲18時,機器人能夠進入方格 [35, 37] ,

因爲3+5+3+7=18。但它不能進入方格 [35, 38],因爲3+5+3+8=19。請問該機器人能夠到達多少個格子?

示例 1:

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

輸入:m = 3, n = 1, k = 0
輸出:1

提示:

1 <= n,m <= 100

0 <= k <= 20

方法一:廣度搜索

思路:

因爲機器人不能走橫縱座標超過k的格子,

所以題目轉化爲->機器人所能走的最大格子數

所以先獲取機器人所能走的格子

然後使用廣度搜索模板即可

public int movingCount(int m, int n, int k) {
int ans = 0;
boolean[][] map = new boolean[m][n];
int[] changeX = {0, 1, 0, -1};
int[] changeY = {1, 0, -1, 0};
// 獲取機器人所能走的格子
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (getSum(i) + getSum(j) <= k) {
map[i][j] = true;
} else {
map[i][j] = false;
}
}
}
// 廣度搜索模板
Queue queue = new ArrayDeque<>();
ans++;
map[0][0] = false;
queue.add(0);
while (!queue.isEmpty()) {
int index = queue.poll();
int x = index / n;
int y = index % n;
for (int count = 0; count < 4; count++) {
int nx = x + changeX[count];
int ny = y + changeY[count];
if (0 <= nx && nx < m && 0 <= ny && ny < n && map[nx][ny]) {
map[nx][ny] = false;// 當走過後記錄,以免重複搜索
ans++;
queue.add(nx * n + ny);
}
}
}
return ans;
}

private int getSum(int num) {
    int sum = 0;
    while (num != 0) {
        sum += num % 10;
        num = num / 10;
    }
    return sum;
}

public int movingCount(int m, int n, int k) {
// 優化,因爲要遍歷全部路徑,所以其實機器上只需要從左下角走完所有的可選擇路徑即可
// 即:只需要走右上,即可
int ans = 0;
int[] changeX = {0, 1};
int[] changeY = {1, 0};
int[][] map = new int[m][n]; //默認全部爲走過
Queue queue = new ArrayDeque<>();
ans++;
map[0][0] = 1;// 走過做標記
queue.add(0);
while (!queue.isEmpty()) {
int index = queue.poll();
int x = index / n;
int y = index % n;
for (int count = 0; count < 2; count++) {
int nx = x + changeX[count];
int ny = y + changeY[count];
if (nx < m && ny < n && getSum(nx) + getSum(ny) <= k && map[nx][ny] == 0) {// 判斷是否允許走入
map[nx][ny] = 1;
ans++;
queue.add(nx * n + ny);
}
}
}
return ans;
}

private int getSum(int num) {
    int sum = 0;
    while (num != 0) {
        sum += num % 10;
        num = num / 10;
    }
    return sum;
}

29.(力扣練習) 翻轉字符串裏的單詞(雙指針,API)
給定一個字符串,逐個翻轉字符串中的每個單詞

示例 1:

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

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

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

說明:

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

方法一:調用AIP

思路:

先句子首尾去空白,然後分割,然後倒置輸出

用時:279ms,內存消耗: 40.8MB

public String reverseWords(String s) {
String ns = “”, ans = “”;
// 去除多餘的空格
for (int i = 0, len = s.length(); i < len; i++) {
char c = s.charAt(i);
if (i == 0 && c == ’ ') {
while (i < len && s.charAt(i) == ’ ') {
i++;
}
i–;
continue;
}
if (i > 0 && c == ’ ’ && s.charAt(i - 1) == ’ ') {
continue;
}
ns += c;
}
String[] strings = ns.split(" ");
for (int i = strings.length - 1; i >= 0; i–) {
ans += i == 0 ? strings[i] : strings[i] + " ";
}
return ans;
}
方法一優化:

用時7ms,內存消耗: 39.9MB

public String reverseWords(String s) {
// 除去開頭和末尾的空白字符
s = s.trim();
// 正則匹配連續的空白字符作爲分隔符分割
List wordList = Arrays.asList(s.split("\s+"));
Collections.reverse(wordList);
return String.join(" ", wordList);
}
方法二:雙指針

用時:3ms,內存消耗:40.1MB

思路:

句子首尾去空白,然後倒置查詢,然後使用兩個指針指向單詞首尾

ps:這裏使用StringBuffer保存比使用String保存更快

String保存:用時:15ms,內存消耗:40.1MB

public String reverseWords(String s) {
// 除去開頭和末尾的空白字符
s = s.trim();
StringBuffer sb = new StringBuffer();
// i指向單詞開始,j指向單詞結束
int i = s.length() - 1, j = i;
while (i >= 0) {
// 讀取單詞開頭
while (i >= 0 && s.charAt(i) != ’ ') {
i–;
}
sb.append(s.substring(i + 1, j + 1) + " ");
// 跳過空白
while (i >= 0 && s.charAt(i) == ’ ') {
i–;
}
j = i;
}
return sb.toString().trim();
}
30.(力扣練習)兩數相加 II(棧,頭插法)
給你兩個 非空 鏈表來代表兩個非負整數。數字最高位位於鏈表開始位置。它們的每個節點只存儲一位數字。將這兩數相加會返回一個新的鏈表。

你可以假設除了數字 0 之外,這兩個數字都不會以零開頭。

進階:

如果輸入鏈表不能修改該如何處理?換句話說,你不能對列表中的節點進行翻轉。

示例:

輸入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 8 -> 0 -> 7

方法一:棧

思路:

把兩個鏈表存入棧中,從末尾讀取,然後使用頭插法,保存到head鏈表中

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack stack1 = new Stack<>();
Stack stack2 = new Stack<>();
while (l1 != null) {
stack1.add(l1);
l1 = l1.next;
}
while (l2 != null) {
stack2.add(l2);
l2 = l2.next;
}
int num = 0;
ListNode head = null;
while (!stack1.isEmpty() || !stack2.isEmpty() || num > 0) {
int sum = num;
sum += stack1.isEmpty() ? 0 : stack1.pop().val;
sum += stack2.isEmpty() ? 0 : stack2.pop().val;
// 頭插法
ListNode node = new ListNode(sum % 10);
node.next = head;
head = node;
num = sum / 10;
}
return head;
}
class ListNode {
public int val;
public ListNode next;

public ListNode(int x) {
    val = x;
}

}
31.(力扣練習)除自身以外數組的乘積(左右乘積列表)
給你一個長度爲 n 的整數數組 nums,其中 n > 1,返回輸出數組 output ,其中 output[i] 等於 nums 中除 nums[i] 之外其餘各元素的乘積。

示例:

輸入: [1,2,3,4]
輸出: [24,12,8,6]

提示:題目數據保證數組之中任意元素的全部前綴元素和後綴(甚至是整個數組)的乘積都在 32 位整數範圍內。

說明: 請不要使用除法,且在 O(n) 時間複雜度內完成此題。

進階:
你可以在常數空間複雜度內完成這個題目嗎?( 出於對空間複雜度分析的目的,輸出數組不被視爲額外空間。)

方法一:左右乘積列表

思路:

用兩個列表保存從左到右,和從右到左的乘積和

保存方式爲錯位保存

public int[] productExceptSelf(int[] nums) {
// 獲取當前數組長度
int len = nums.length;
// 定義返回數組,因爲題目說明大小小於int所以可以直接使用int類型
int[] res = new int[len];
int l = 1, r = 1;
for (int i = 0; i < len; i++) {
// 初始化 對數據進行賦值
// 錯位保存,不保存當前值,保存前一個值
res[i] = l;
l *= nums[i];
}
for (int i = len - 1; i >= 0; i–) {
// 對數據進行二次賦值
res[i] *= r;
r *= nums[i];
}
return res;
}
32(力扣練習)順時針打印矩陣(深度搜索)
輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字。

示例 1:

輸入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
輸出:[1,2,3,6,9,8,7,4,5]
示例 2:

輸入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
輸出:[1,2,3,4,8,12,11,10,9,5,6,7]

限制:

0 <= matrix.length <= 100
0 <= matrix[i].length <= 100
注意:本題與主站 54 題相同:https://leetcode-cn.com/problems/spiral-matrix/

方法一:深度搜索

思路:

每次按一個方向搜索,達到結束標誌的時候換方向

結束標誌:超出界限或者被訪問過

int[] changeX = {0, 1, 0, -1};
int[] changeY = {1, 0, -1, 0};
int X, Y;
boolean[][] flag;

public int[] spiralOrder(int[][] matrix) {
    X = matrix.length;
    if (X == 0) {
        return new int[0];
    }
    Y = matrix[0].length;
    if (Y == 0) {
        return new int[0];
    }
    int[] re = new int[X * Y];
    flag = new boolean[X][Y];
    dfs(re, matrix, 0, 0, 0, 0);
    return re;
}

private boolean dfs(int[] re, int[][] matrix, int x, int y, int k, int index) {
    // 判斷是否到結束值
    if (x < 0 || x >= X || y < 0 || y >= Y || flag[x][y]) {
        return false;
    }
    re[index] = matrix[x][y];
    flag[x][y] = true;
    int nx = x + changeX[k], ny = y + changeY[k];
    // 訪問玩該方向所有值
    if (dfs(re, matrix, nx, ny, k, index + 1)) {
        return true;
    }
    // 換方向
    k = (k + 1) % 4;
    nx = x + changeX[k];
    ny = y + changeY[k];
    return dfs(re, matrix, nx, ny, k, index + 1);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章