從根到葉的二進制數(shù)之和
題目:給出一棵二叉樹,其上每個結(jié)點的值都是 0 或 1 。每一條從根到葉的路徑都代表一個從最高有效位開始的二進制數(shù)。例如,如果路徑為 0 -> 1 -> 1 -> 0 -> 1,那么它表示二進制數(shù) 01101,也就是 13 。對樹上的每一片葉子,我們都要找出從根到該葉子的路徑所表示的數(shù)字。以 10^9 + 7 為模,返回這些數(shù)字之和。
思路:這道題的思路就是遍歷每一條到葉節(jié)點的二進制數(shù)字,然后取模10的9次方+7,直到遍歷到葉子節(jié)點,則把這個數(shù)值加到結(jié)果sum中。我先去嘗試實現(xiàn)了。
思路沒問題,比想的還要簡單,這個參考了昨天的那道數(shù)組中被5整除的數(shù)的技巧。我直接貼代碼:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int sum ;
public int sumRootToLeaf(TreeNode root) {
getNode(root,0);
return sum;
}
public void getNode(TreeNode root ,int i){
if(root==null) return;
i = (i*2+root.val)%1000000007;
//葉子節(jié)點值加到sum中
if(root!=null && root.left==null && root.right==null) sum += i;
getNode(root.left,i);
getNode(root.right,i);
}
}
因為這個性能直接超過百分百,所以我也不額外看題解了,這道題真的就是一個遍歷+二進制用十進制表示的方法,都很簡單,思路通則路路通。
說起來昨天我做的那個被5整除還是在群友的提點下完成的,表白我群友~好了,下一題。
除數(shù)博弈
題目:愛麗絲和鮑勃一起玩游戲,他們輪流行動。愛麗絲先手開局。最初,黑板上有一個數(shù)字 N 。在每個玩家的回合,玩家需要執(zhí)行以下操作:
選出任一 x,滿足 0 < x < N 且 N % x == 0 。
用 N - x 替換黑板上的數(shù)字 N 。
如果玩家無法執(zhí)行這些操作,就會輸?shù)粲螒颉V挥性趷埯惤z在游戲中取得勝利時才返回 True,否則返回 false。假設(shè)兩個玩家都以最佳狀態(tài)參與游戲。
示例 1:
輸入:2
輸出:true
解釋:愛麗絲選擇 1,鮑勃無法進行操作。
示例 2:
輸入:3
輸出:false
解釋:愛麗絲選擇 1,鮑勃也選擇 1,然后愛麗絲無法進行操作。
提示:
1 <= N <= 1000
思路:就是如題所示。我感覺這道題很復(fù)雜啊,有個最優(yōu)解的問題,就是怎么做能讓對面輸,所以這個除數(shù)可以很多,但是如何選擇最好的那個。。太懵了,我先去看看題目有啥規(guī)律。
好了,算是找到規(guī)律了。但是不知道對不對。我反正是自己一個個數(shù)導(dǎo)的,又是眼睛看出來的規(guī)律。
我去提交了,看看對不對。
事實證明就是這么簡單,這道題就這樣吧。
兩地調(diào)度
題目:公司計劃面試 2N 人。第 i 人飛往 A 市的費用為 costs[i][0],飛往 B 市的費用為 costs[i][1]。返回將每個人都飛到某座城市的最低費用,要求每個城市都有 N 人抵達。
示例:
輸入:[[10,20],[30,200],[400,50],[30,20]]
輸出:110
解釋:
第一個人去 A 市,費用為 10。
第二個人去 A 市,費用為 30。
第三個人去 B 市,費用為 50。
第四個人去 B 市,費用為 20。
最低總費用為 10 + 30 + 50 + 20 = 110,每個城市都有一半的人在面試。
提示:
1 <= costs.length <= 100
costs.length 為偶數(shù)
1 <= costs[i][0], costs[i][1] <= 1000
思路:這個題第一反應(yīng)是復(fù)雜,甚至都想到了動態(tài)規(guī)劃(最近在學(xué)dp,看到啥都想到),后來要實現(xiàn)了才發(fā)現(xiàn)沒有那么難。其實可以這么想,一個人去a和去b兩種選擇??隙ㄊ且ヒ粋€地方,主要區(qū)別就是錢的差值。打個比方:a這個人去A100,去B110.然后b這個人去A10,去B50。我們不用考慮100,110,10,50這四個數(shù)的比較,只要知道a去A比B便宜10.而b去A比B便宜40??隙ㄊ沁x擇便宜的多的,也就是a去B,b去A。這個邏輯繞明白了就能明白這道題的做法了。
其實我說的比較亂,因為思路也是才理出來。但是方向應(yīng)該是對的。每個人只要和自身比較就行了,不要和別人比較。目前的想法是都假設(shè)去A。然后再比較去B要便宜的錢數(shù)(如果本身B比A大這個錢數(shù)就是負數(shù)。)然后找出一般的人選擇便宜的錢數(shù)最多的。差不多就這樣,我去實現(xiàn)了先。
這個題做出來了,中間有一些波折,有一個測試案例沒通過,這個題的參數(shù)是數(shù)組型數(shù)組,java中創(chuàng)建只能一個個往里填,所以我沒辦法用debug調(diào)試知道到底哪里錯了,所以很痛苦,眼睛看數(shù)組都花了也沒看出錯誤,所以中間試圖打算將數(shù)組換成隊列來做這道題,就是之前刷算法用到的一個自帶排序的queue。PriorityBlockingQueue。不過還好改動不小,并且在改動的時候靈機一動感覺找到了不正確的點,所以~~~過了,哈哈,一波三折啊有么有?
我直接貼實現(xiàn)的代碼:
class Solution {
public int twoCitySchedCost(int[][] costs) {
int sum = 0;
//去a和去b的差值不會大于999,所以數(shù)組長度2000是包含正負
int[] ar = new int[2000];
for(int [] i :costs ){
sum += i[0];
//這個i[1]-i[0]是說去b比去a貴的。如果是正數(shù)要減去
ar[i[1]-i[0]+1000] ++;
}
int index = 0;
int i = 0;
while(i<costs.length/2){
while(ar[index]>0&&i<costs.length/2){
sum += (index-1000);
ar[index]--;
i++;
}
index++;
}
return sum;
}
}
這個題用到了常用的技巧數(shù)組下標代替數(shù)值,因為這個值可能是負的所以我這里都統(tǒng)一加了1000、一開始我錯誤的點就是第二個while中只判斷了ar[index]大于0而沒判斷i,這也算是一個失誤了,思慮不周而且這種情況我自己測試案例沒測出來,講真leetcode測試案例很強大啊。
這個性能是超過百分之九十九的,所以我也不再換方法了。不過講真,我現(xiàn)在也覺得之前我想的那個排序隊列也是可以實現(xiàn)的,只不過我是真的懶得大刀。
這道題就到這里吧,感覺雖然只做了這一道題,但是不斷在復(fù)習(xí)之前學(xué)到的知識,比如數(shù)組下標代替數(shù)值 ,還有隊列啊,其實不斷刷題的過程也是一個復(fù)習(xí)的過程~~下一題。
距離順序排列矩陣單元格
題目:給出 R 行 C 列的矩陣,其中的單元格的整數(shù)坐標為 (r, c),滿足 0 <= r < R 且 0 <= c < C。另外,我們在該矩陣中給出了一個坐標為 (r0, c0) 的單元格。返回矩陣中的所有單元格的坐標,并按到 (r0, c0) 的距離從最小到最大的順序排,其中,兩單元格(r1, c1) 和 (r2, c2) 之間的距離是曼哈頓距離,|r1 - r2| + |c1 - c2|。(你可以按任何滿足此條件的順序返回答案。)
示例 1:
輸入:R = 1, C = 2, r0 = 0, c0 = 0
輸出:[[0,0],[0,1]]
解釋:從 (r0, c0) 到其他單元格的距離為:[0,1]
示例 2:
輸入:R = 2, C = 2, r0 = 0, c0 = 1
輸出:[[0,1],[0,0],[1,1],[1,0]]
解釋:從 (r0, c0) 到其他單元格的距離為:[0,1,1,2]
[[0,1],[1,1],[0,0],[1,0]] 也會被視作正確答案。
示例 3:
輸入:R = 2, C = 3, r0 = 1, c0 = 2
輸出:[[1,2],[0,2],[1,1],[0,1],[1,0],[0,0]]
解釋:從 (r0, c0) 到其他單元格的距離為:[0,1,1,2,2,3]
其他滿足題目要求的答案也會被視為正確,例如 [[1,2],[1,1],[0,2],[1,0],[0,1],[0,0]]。
提示:
1 <= R <= 100
1 <= C <= 100
0 <= r0 < R
0 <= c0 < C
思路:這個題我第一遍審題沒明白,然后畫圖理解的:首先行列確定了這個二維塊的大小。其次這個r0,c0確定了起始點。然后我們從起始點(距離是0,肯定是最小的)開始按照距離一個個列出來。
當(dāng)然還有一個距離的規(guī)律:
第一個點最多有4個距離1的點,8個距離2 的點,12個距離3的點,其實不用往下繼續(xù)標也能看出來,應(yīng)該是16個距離4的點,20個距離5的點。暫時還不知道這個規(guī)律有沒有用,反正先放著。
其實這個自己畫兩遍就能理解題意了,這個最外層的點是不算的。
- 比如1行1列,則只有一個點0 0 是有效的。
- 一行兩列則只有原點 0 0和 0 1是有效的。別的點都在邊邊上,不算有效。
- 同樣道理,2行三列拋出去最外層的邊邊,相當(dāng)于位于2-3之間的有效,也就是6個點。
其實這里要說一下,因為坐標的值從0開始,所以點數(shù)哪怕不包含最外面的,但是恰好了,1不包含最外的但是包含0一個點,2不包含最外的2但是包含0,1也是兩個點。所以點數(shù)就是行列數(shù)(其實用二維數(shù)組更好理解,元素個數(shù)就是兩個長度的乘積)
到這為止,起碼返回值中的點的個數(shù)是知道了的。然后一個點分行列兩個坐標。坐標好得,關(guān)鍵是距離的大小。最笨的辦法就是都算出返回值,挨個算距離。
但是其實我感覺也不見得沒有簡單算法。比如我們可以以給定點為基點,直接在獲取坐標的時候就按照距離獲取。當(dāng)然了這些都是想法, 我要去擼代碼實現(xiàn)了。
emmmm....首先實現(xiàn)了,其次沒什么簡單算法,我沒想出來,最后暴力sort解決的,我先貼代碼:
class Solution {
public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
int[][] res = new int[R*C][2];
int k = 0;
for(int i = R-1;i>=0;i--){
for(int j = C-1;j>=0;j--){
res[k] = new int[]{i,j};
k++;
}
}
Arrays.sort(res,new Comparator<Object>() {
@Override
public int compare(Object arg0, Object arg1) {
int[] aa = (int[])arg0;
int[] bb = (int[])arg1;
int a = Math.abs(aa[0]-r0)+Math.abs(aa[1]-c0);
int b = Math.abs(bb[0]-r0)+Math.abs(bb[1]-c0);
return Integer.compare(a,b);
}
});
return res;
}
}
分兩步:
- 獲取所有的點
- 給點按照距離排序
自己寫的排序規(guī)則。然后性能湊合,雖然執(zhí)行速度不盡人意,但是內(nèi)存消耗超過百分百,也算是有點優(yōu)點了。
然后雖然我沒想出來。但是我還是覺得應(yīng)該是有簡單算法的啊,明明知道規(guī)律卻寫不出來,就是 給定r0 和c0和實際的大小的判斷。。。哎,我決定還是直接看排行第一的代碼吧。
很好,直接給我解惑了。我當(dāng)時有這個想法,但是一來比較麻煩,二來不確定是不是對的所以沒有堅持,但是大體分四種情況都想到了,這種我沒做出來別人做到了的感覺,,,嘖嘖,貼上代碼瞻仰瞻仰:
class Solution {
public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
int[][] ans=new int[R*C][2];
ans[0][0]=r0;
ans[0][1]=c0;
int point_x=r0,point_y=c0;
int order=1;
while(order<R*C){
point_x--;
while(point_x<r0){
if(point_x>=0&&point_y<=C-1){
ans[order][0]=point_x;
ans[order++][1]=point_y;
}
point_x++;
point_y++;
}
while(point_y>c0){
if(point_x<=R-1&&point_y<=C-1){
ans[order][0]=point_x;
ans[order++][1]=point_y;
}
point_x++;
point_y--;
}
while(point_x>r0){
if(point_x<=R-1&&point_y>=0){
ans[order][0]=point_x;
ans[order++][1]=point_y;
}
point_x--;
point_y--;
}
while (point_y<c0){
if(point_x>=0&&point_y>=0){
ans[order][0]=point_x;
ans[order++][1]=point_y;
}
point_x--;
point_y++;
}
}
return ans;
}
}
好的吧,我仔細看了第一二三的代碼,都是這樣分情況直接往里插入就是準確的,其實我也是前期準備做了一堆,寫代碼的時候懶了。。。哈哈,反正就這樣吧。這道題過。
移動石子直到連續(xù)
題目:三枚石子放置在數(shù)軸上,位置分別為 a,b,c。每一回合,我們假設(shè)這三枚石子當(dāng)前分別位于位置 x, y, z 且 x < y < z。從位置 x 或者是位置 z 拿起一枚石子,并將該石子移動到某一整數(shù)位置 k 處,其中 x < k < z 且 k != y。當(dāng)你無法進行任何移動時,即,這些石子的位置連續(xù)時,游戲結(jié)束。要使游戲結(jié)束,你可以執(zhí)行的最小和最大移動次數(shù)分別是多少? 以長度為 2 的數(shù)組形式返回答案:answer = [minimum_moves, maximum_moves]
示例 1:
輸入:a = 1, b = 2, c = 5
輸出:[1, 2]
解釋:將石子從 5 移動到 4 再移動到 3,或者我們可以直接將石子移動到 3。
示例 2:
輸入:a = 4, b = 3, c = 2
輸出:[0, 0]
解釋:我們無法進行任何移動。
提示:
1 <= a <= 100
1 <= b <= 100
1 <= c <= 100
a != b, b != c, c != a
思路:我完全不知道這個題是怎么混進來的、感覺很簡單啊。我不知道有什么坑,所以去試試。做出來再說。
好了,做出來了,反正就是很簡單的一個邏輯,不過中間也有坑,本來我以為a,b,c小到大和xyz是對應(yīng)的,現(xiàn)在才知道是隨機的,所以要知道最大值最小值和中間值。另外一步到位可以跳過y去另一邊,反正說到底都是面上的東西,不復(fù)雜。直接貼代碼:
class Solution {
public int[] numMovesStones(int a, int b, int c) {
int max = a>b?a:b;
max =max>c?max:c;
int min = a>b?b:a;
min = min>c?c:min;
int mid = a+b+c-min-max;
int[] res = new int[2];
res[1] += max-1-mid;
res[1] += mid-1-min;
if(mid==min+1 && max==mid+1){
res[0] = 0;
}else if(mid!=min+1 && max!=mid+1 && mid!=min+2 && max!=mid+2){
res[0] =2;
}else{
res[0]=1;
}
return res;
}
}
有效的回旋鏢
題目:回旋鏢定義為一組三個點,這些點各不相同且不在一條直線上。給出平面上三個點組成的列表,判斷這些點是否可以構(gòu)成回旋鏢。
示例 1:
輸入:[[1,1],[2,3],[3,2]]
輸出:true
示例 2:
輸入:[[1,1],[2,2],[3,3]]
輸出:false
提示:
points.length == 3
points[i].length == 2
0 <= points[i][j] <= 100
思路:這樣的題我做過類似的,但是這個題讓我有點疑問啊,題目說的意思是不是只要能構(gòu)成三角形的三個點就有效?不用扔前扔后距離相等?我記得上一個回旋鏢的題是有這個要求的啊,我去拿個demo試試。
好的,已經(jīng)確定能組成三角形就行了。
那這個題不是簡單的的不得了了么?只要三個點橫縱坐標不一樣且與x/y軸夾角不一樣就行了。我去實現(xiàn)了。
這個實現(xiàn)一波三折,因為是程序代碼,涉及到除法int型會取整,所以小數(shù)除大數(shù)都是0.沒法比較,所以我的想法太理所當(dāng)然了。pass
然后自己想了半天,最后求助百度了,其實這個題就是判斷三點是不是一線。
所以說最終就這么做完了。
其實這個算是學(xué)到了新的數(shù)學(xué)知識了吧。
而且不用這種辦法另外一種就是兩點連線,判斷這個第三個點在不在線上,想想都麻煩,還是這個方法最簡單。這個題就這樣吧
class Solution {
public boolean isBoomerang(int[][] points) {
int x1 = points[0][0];
int y1 = points[0][1];
int x2 = points[1][0];
int y2 = points[1][1];
int x3 = points[2][0];
int y3 = points[2][1];
//令R1=x2-x1,C1=y2-y1;R2=x3-x2,C2=y3-y2。如果R1*C2=R2*C1,那么這三個點就在同一條線上。
if((x2-x1)*(y3-y2)==(y2-y1)*(x3-x2))return false;
return true;
}
}
今天的筆記就記到這里,如果稍微幫到你了記得點個喜歡點個關(guān)注,也祝大家工作順順利利!