leetcode刷題記錄(7)

1.路徑總和

題目:

給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。

說明: 葉子節點是指沒有子節點的節點。

思路:遞歸遍歷所有節點,然後判斷是否有和目標值相同的成員。

var hasPS = (node, sum, currtS = 0) => {
  if (!node) return false;
  currtS += node.val;
  if (!node.left && !node.right) {
    return currtS === sum;
  }
  return hasPS(node.left, sum, currtS) || hasPS(node.right, sum, currtS);
};
var hasPathSum = function (root, sum) {
  if (!root) return false;
  return hasPS(root, sum);
};

這個是遞加的思路,。其實遞減的思路是一樣的,並且還不需要聲明另一方法

var hasPathSum = function (root, sum) {
  if (!root) return false;
  sum -= root.val;
  if (!root.left && !root.right) {
    return !sum;
  }
  return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);
};

有遞歸就有循環,來看一下循環的實現,其實就是用對象的形式保存了當前的節點以及該節點對應的剩餘值。如果實現過深拷貝的可能知道,這個方式和深拷貝的做法很像。

var hasPathSum = function (root, sum) {
    if (!root) return false
    let stack = []
    stack.push({
        root: root,
        sum: root.val
    })
    while (stack.length) {
        let node = stack.pop()
        if (!node.root.left && !node.root.right) {
            if (node.sum === sum) return true
        }
        if (node.root.left) {
            stack.push({
                root: node.root.left,
                sum: node.sum + node.root.left.val
            })
        }
        if (node.root.right) {
            stack.push({
                root: node.root.right,
                sum: node.sum + node.root.right.val
            })
        }
    }
    return false
};

2.楊輝三角

題目:給定一個非負整數 numRows,生成楊輝三角的前 numRows 行。

思路:和斐波那契數列很像。用前一輪的結果計算下一輪的結果。所以我們只需要設定前幾項的結果,然後遍歷去計算就行。

var generate = function(numRows) {
  if (!numRows) return [];
  if (numRows === 1) return [[1]];
  var res = [[1]];
  while (numRows > 1) {
    const item = res[res.length - 1];
    const v = [];
    item.forEach((t, i) => {
      if (i) {
        v.push(item[i - 1] + t);
      } else {
        v.push(1);
      }
    });
    v.push(1);
    res.push(v);
    numRows--;
  }
  return res
};

3.楊輝三角 II

題目:給定一個非負索引 k,其中 k ≤ 33,返回楊輝三角的第 行。

思路:首先,我們可以確定,楊輝三角是左右對稱的,第k行有k+1個成員,所以對第k行,我們只需要計算前Math.ceid(k+1/2)項的結果。

然後,我們用遞歸實現,一個參數表示當前第幾行,另一個參數表示前一輪的結果。

var getNext = (last, row) => {
  if (!row) return last;
  if (!last) last = [];
  var v = [],
    l = last.length,
    t = Math.floor(l / 2) + 1;
  for (let i = 0; i < t; i++) {
    v[i] = v[l - i] = !i ? 1 : last[i - 1] + last[i];
  }
  return getNext(v, row - 1);
};
var getRow = function (rowIndex) {
  return getNext(null, rowIndex+1);
};

4.買賣股票的最佳時機

題目:

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多隻允許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。

注意:你不能在買入股票前賣出股票。

思路:聲明兩個變量,一個裝最大值,一個裝最小值。單次遍歷,每次更新最大值和最小值,最後得到最大值和最小值。

這裏有個問題,就是最大值和最小值的順序問題,所以我們在更新最小值時,也要更新最大值。然後每次更新最大值時,要和之前的最大值比較

var maxProfit = function(prices) {
  let l = prices.length;
  if (l < 2) return 0;
  let min = Infinity,
    max = -Infinity,
    res = 0;
  for (let i = 0; i < l; i++) {
    if (prices[i] > max) {
      max = prices[i];
      res = Math.max(max - min, res);
    }
    if (prices[i] < min) {
      min = max = prices[i];
    }
  }
  return res;
};

問題:變量太多,可以適當減少

優化方向:最大值不需要用一個變量記錄,循環的時候,每次取到的值和當前的最小值比較即可,大於的話計算差,然後和最大值比較。

var maxProfit = function(prices) {
  var res = 0;
  var min = Infinity;
  prices.forEach((i) => {
    if (i < min) {
      min = i;
    } else {
      if (i > min) {
        res = Math.max(res, i - min);
      }
    }
  });
  return res;
};

5.買賣股票的最佳時機 II

題目:

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。

注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

思路:其實這題想清楚了就很簡單,最重要的是思路。

因爲可以進行多次交易,所以每次只需要和上一項的值比較即可,。如果比上一項的值大,就累加差,如果更小,就不變。

股票的最大收益其實就是相鄰兩項差的正值之和

var maxProfit = function(prices) {
  let res = 0,
    a = prices[0];
  prices.forEach((i) => {
    if (i > a) res += i - a;
    a = i;
  });
  return res;
};

6.驗證迴文串

題目:

給定一個字符串,驗證它是否是迴文串,只考慮字母和數字字符,可以忽略字母的大小寫。

說明:本題中,我們將空字符串定義爲有效的迴文串。

思路:先取出所有的數字及字母字符串,然後遍歷新的字符串,看一下某下標和鏡像下標的值是否相等

var isPalindrome = function(s) {
  const l = s.length;
  if (l < 2) return true;
  const a = s.replace(/\W|_/g, "");
  let v = 0,
    b = 0;
  for (let i = 0, s = Math.ceil(a.length / 2); i < s; i++) {
    v = Number(a[i]);
    b = Number(a[a.length - 1 - i]);
    if (!Number.isNaN(v) && !Number.isNaN(b)) {
      if (v !== b) return false;
    } else if (Number.isNaN(v) && Number.isNaN(b)) {
      v = a.charCodeAt(i);
      b = a.charCodeAt(a.length - 1 - i);
      if (!(v === b || v === b + 32 || v === b - 32)) return false;
    } else {
      return false;
    }
  }
  return true;
};

問題:比較太多,且分類太多,對於字符串用了unicode值去比較,流程繁瑣

優化方向:先全部轉換爲小寫字符,直接比較

var isPalindrome = function(s) {
  const l = s.length;
  if (l < 2) return true;
  const a = s.replace(/\W|_/g, "").toLowerCase();
  for (let i = 0, s = a.length / 2; i < s; i++) {
    if (a[i] !== a[a.length - 1 - i]) return false;
  }
  return true;
};

7.只出現一次的數字

題目:

給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。

說明:

你的算法應該具有線性時間複雜度。 你可以不使用額外空間來實現嗎?

思路:先來最簡單的吧

var singleNumber = function(nums) {
return nums.find(i=>nums.filter(item=>item===i).length==1)
};

問題:其實重複遍歷了很多次,談不上算法,只能說是解法

優化方向:用一個對象記錄某個數是否出現,沒有出現就累加,出現過就減,最終的結果就是隻出現一次的數

var singleNumber = function(nums) {
  let map = {};
  let res = 0;
  nums.forEach((i) => {
    if (map[i] === undefined) {
      res += i;
      map[i] = 1;
    } else {
      res -= i;
    }
  });
  return res;
};

優化方向:位運算中的異或。

a^0=a,a^a=0.

所以累計異或即可

var singleNumber = function(nums) {
return nums.reduce((res,i)=>res^i)
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章