【算法面試】leetcode最多見的150道前端面試題 --- 簡單題下(44題)

本文題目選自 LeetCode 精選 TOP 面試題,這些題在本身和同事親身經歷中,確實遇到的概率在百分之80%以上(成都和北京的前端崗位)。javascript

本篇是簡單題(下)20題左右,上半部分詳見# 簡單題上(22題左右)前端

二叉樹(DFS)

二叉樹前中後遍歷套路詳解

前序遍歷題目以下:java

root節點是A節點(下圖的A節點),而後讓你按照下圖數字的順序依次打印出節點。git

d7948dc5e50e70cc84cfbd0e0cf989da40eb96167f03b710392be45b8c415662.png

咱們能夠看到這其中的規律,就是深度優先遍歷,先遍歷左子樹,再遍歷右子樹,這裏咱們不用遞歸,由於一些大廠嚴格要求二叉樹遍歷不用遞歸,遞歸太簡單了。面試

重點思路就是:深度優先遍歷,先遍歷左子樹,再遍歷右子樹算法

因此,咱們須要一套如何遍歷一顆二叉樹,而且是先左子樹,再右子樹的通用模板,以下數組

var Traversal = function(root) {
    const stack = [];
    while (root || stack.length){
      while(root){
        stack.push(root);
        root = root.left;
      }
      root = stack.pop();
      root = root.right;
    }
    return res;
};
複製代碼

咱們結合圖片發現這個遍歷產生的總體壓棧的順序是markdown

  • A、B、D入棧,
  • D出棧
  • B出棧
  • E入棧
  • E出棧
  • A出棧
  • C入棧
  • C出棧
  • F入棧
  • F出棧

咱們把上面入棧的元素按順序排列一下就是,A、B、D、E、C、F,而這就是前序遍歷的順序!解答完畢!數據結構

是否是頗有意思,下面的中序遍歷,咱們看看出棧順序是否是中序遍歷的要求:D、B、E、A、C、F(這就是中序遍歷的要求,好了,兩個題解決)app

放具體前序遍歷代碼:

var preorderTraversal = function(root) {
    // 初始化數據
    const res =[];
    const stack = [];
    while (root || stack.length){
      while(root){
        res.push(root.val);
        stack.push(root);
        root = root.left;
      }
      root = stack.pop();
      root = root.right;
    }
    return res;
};
複製代碼

中序遍歷是一個意思,在前序遍歷的基礎上改造一下 image.png

var preorderTraversal = function(root) {
    // 初始化數據
    const res =[];
    const stack = [];
    while (root || stack.length){
      while(root){
        stack.push(root);
        root = root.left;
      }
      root = stack.pop();
      res.push(root.val);
      root = root.right;
    }
    return res;
};
複製代碼

後序遍歷有點不太同樣,可是套路是同樣的,咱們須要先遍歷右子樹,再遍歷左子樹,反着來,就能夠了,代碼以下:

image.png

var postorderTraversal = function(root) {
  // 初始化數據
    const res =[];
    const stack = [];
    while (root || stack.length){
      while(root){
        stack.push(root);
        res.unshift(root.val);
        root = root.right;
      }
      root = stack.pop();
      root = root.left;
    }
    return res;
};
複製代碼

對稱二叉樹

這個題簡而言之就是判斷一個二叉樹是對稱的,好比說:

二叉樹 [1,2,2,3,4,4,3] 是對稱的。

1
   / \
  2   2
 / \ / \
3  4 4  3
複製代碼

可是下面這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的:

1
   / \
  2   2
   \   \
   3    3

複製代碼

思路:

遞歸解決:

  • 判斷兩個指針當前節點值是否相等
  • 判斷 A 的右子樹與 B 的左子樹是否對稱
  • 判斷 A 的左子樹與 B 的右子樹是否對稱
function isSame(leftNode, rightNode){
    if(leftNode === null && rightNode === null) return true;
    if(leftNode === null || rightNode === null) return false;
    return leftNode.val === rightNode.val && isSame(leftNode.left, rightNode.right) && isSame(leftNode.right, rightNode.left)
}
var isSymmetric = function(root) {
    if(!root) return root;
    return isSame(root.left, root.right);
};
複製代碼

二叉樹的最大深度

這個題在面試滴滴的時候遇到過,主要是掌握二叉樹遍歷的套路

  • 只要遍歷到這個節點既沒有左子樹,又沒有右子樹的時候
  • 說明就到底部了,這個時候若是以前記錄了深度,就能夠比較是否比以前記錄的深度大,大就更新深度
  • 而後以此類推,一直比較到深度最大的
var maxDepth = function(root) {
    if(!root) return root;
    let ret = 1;
    function dfs(root, depth){
        if(!root.left && !root.right) ret = Math.max(ret, depth);
        if(root.left) dfs(root.left, depth+1);
        if(root.right) dfs(root.right, depth+1);
    }
    dfs(root, ret);
    return ret
};
複製代碼

將有序數組轉化爲二叉搜索樹

咱們先看題:

給你一個整數數組 nums ,其中元素已經按 升序 排列,請你將其轉換爲一棵 高度平衡 二叉搜索樹。

高度平衡 二叉樹是一棵知足「每一個節點的左右兩個子樹的高度差的絕對值不超過 1 」的二叉樹。

 

示例 1:

image.png

輸入:nums = [-10,-3,0,5,9]
輸出:[0,-3,9,-10,null,5]
解釋:[0,-10,5,null,-3,null,9] 也將被視爲正確答案:
複製代碼

示例 2:

image.png

輸入:nums = [1,3]
輸出:[3,1]
解釋:[1,3] 和 [3,1] 都是高度平衡二叉搜索樹。
 

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 嚴格遞增 順序排列
複製代碼

思路:

  • 構建一顆樹包括:構建root、構建 root.left 和 root.right
  • 題目要求"高度平衡" — 構建 root 時候,選擇數組的中間元素做爲 root 節點值,便可保持平衡。
  • 遞歸函數能夠傳遞數組,也能夠傳遞指針,選擇傳遞指針的時候: l r 分別表明參與構建BST的數組的首尾索引。
var sortedArrayToBST = function(nums) {
    return toBST(nums, 0, nums.length - 1)
};
const toBST = function(nums, l, r){
    if( l > r){
        return null;
    }
    const mid = l + r >> 1;
    const root = new TreeNode(nums[mid]);
    root.left = toBST(nums, l, mid - 1);
    root.right = toBST(nums, mid + 1, r);

    return root;
}
複製代碼

棧是一種先進後出的數據結構,因此涉及到你須要先進後出這個想法後,就可使用棧。

其次我以爲棧跟遞歸很類似,遞歸是否是先壓棧,而後先進來的先出去,就跟函數調用棧同樣。

20. 有效的括號

這是一道很典型的用棧解決的問題, 給定一個只包括 '(',')','{','}','[',']' 的字符串 s ,判斷字符串是否有效。

有效字符串需知足:

左括號必須用相同類型的右括號閉合。 左括號必須以正確的順序閉合。  

示例 1:

輸入:s = "()"
輸出:true
示例 2:

輸入:s = "()[]{}"
輸出:true
示例 3:

輸入:s = "(]"
輸出:false
示例 4:

輸入:s = "([)]"
輸出:false
複製代碼

思路: 這道題有一規律:

  1. 右括號前面,必須是相對應的左括號,才能抵消!
  2. 右括號前面,不是對應的左括號,那麼該字符串,必定不是有效的括號!

也就是說左括號咱們直接放入棧中便可,發現是右括號就要對比是否跟棧頂元素相匹配,不匹配就返回false

var isValid = function(s) {
    const map = { '{': '}', '(': ')', '[': ']' };
    const stack = [];
    for(let i of s){
        if(map[i]){
            stack.push(i);
        } else {
            if(map[stack[stack.length - 1]] === i){
                stack.pop()
            }else{
                return false;
            }
        }
    }
    return stack.length === 0;
};
複製代碼

15五、 最小棧

先看題目:

設計一個支持 push ,pop ,top 操做,並能在常數時間內檢索到最小元素的棧。

  • push(x) —— 將元素 x 推入棧中。
  • pop() —— 刪除棧頂的元素。
  • top() —— 獲取棧頂元素。
  • getMin() —— 檢索棧中的最小元素。

 

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

提示:

pop、top 和 getMin 操做老是在 非空棧 上調用。
複製代碼

咱們先不寫getMin方法,知足其餘方法實現就很是簡單,咱們來看一下:

var MinStack = function() {
    this.stack = [];
};

MinStack.prototype.push = function(x) {
    this.stack.push(x);
};

MinStack.prototype.pop = function() {
    this.stack.pop();
};

MinStack.prototype.top = function() {
    return this.stack[this.stack.length - 1];
};
複製代碼

如何保證每次取最小呢,咱們舉一個例子: 截屏2021-07-25 下午5.46.53.png

如上圖,咱們須要一個輔助棧來記錄最小值,

  • 開始咱們向stack push -2
  • 此時輔助棧minStack,由於此時stack最小的是-2,也push -2
  • stack push 0
  • 此時輔助站minStack 會用 0 跟 -2對比,-2更小,minstack會push -2
  • stack push -3
  • 此時輔助站minStack 會用 -3 跟 -2對比,-3更小,minstack會push -3

因此咱們取最小的時候,總能在minStack中取到最小值,因此解法就出來了:

var MinStack = function() {
    this.stack = [];
    // 輔助棧
    this.minStack = [];
};

MinStack.prototype.push = function(x) {
    this.stack.push(x);
    // 若是是第一次或者當前x比最小棧裏的最小值還小才push x
    if(this.minStack.length === 0 || x < this.minStack[this.minStack.length - 1]){
        this.minStack.push(x)
    } else {
         this.minStack.push( this.minStack[this.minStack.length - 1])
    }
};

MinStack.prototype.pop = function() {
    this.stack.pop();
    this.minStack.pop();
};

MinStack.prototype.top = function() {
    return this.stack[this.stack.length - 1];
};

MinStack.prototype.getMin = function() {
    return this.minStack[this.stack.length - 1];
};
複製代碼

動態規劃

動態規劃,必定要知道動態轉移方程,有了這個,就至關於解題的鑰匙,咱們從題目中體會一下

53. 最大子序和

題目以下:

給定一個整數數組 nums ,找到一個具備最大和的連續子數組(子數組最少包含一個元素),返回其最大和。  

示例 1:

輸入:nums = [-2,1,-3,4,-1,2,1,-5,4]
輸出:6
解釋:連續子數組 [4,-1,2,1] 的和最大,爲 6 。
示例 2:

輸入:nums = [1]
輸出:1
示例 3:

輸入:nums = [0]
輸出:0
複製代碼

思路:

  • 這道題能夠用動態規劃來解決,關鍵是找動態轉移方程
  • 咱們動態轉移方程中,dp表示每個nums下標的最大自序和,因此dp[i]的意思爲:包括下標i以前的最大連續子序列和爲dp[i]。

肯定轉義方程的公示:

dp[i]只有兩個方向能夠推出來:

  • 一、若是dp[i - 1] < 0,也就是當前遍歷到nums的i,以前的最大子序和是負數,那麼咱們就不必繼續加它了,由於dp[i] = dp[i - 1] + nums[i] 會比nums[i]更小,因此此時還不如dp[i] = nums[i],就是目前遍歷到i的最大子序和呢
  • 二、同理dp[i - 1] > 0,說明nums[i]值得去加dp[i - 1],此時迴避nums[i]更大

這樣代碼就出來了,其實更多的就是求dp,遍歷nums每個下標都會產生最大子序和,咱們記錄下來便可

var maxSubArray = function(nums) {
  let res = nums[0];
  const dp = [nums[0]];
  for(let i=1;i < nums.length;i++){
      if(dp[i-1]>0){
        dp[i]=nums[i]+dp[i-1]
      }else{
       dp[i]=nums[i]
      }
      
    res=Math.max(dp[i],res)
  }
    return res
};
複製代碼

70. 爬樓梯

先看題目:

假設你正在爬樓梯。須要 n 階你才能到達樓頂。

每次你能夠爬 1 或 2 個臺階。你有多少種不一樣的方法能夠爬到樓頂呢?

注意:給定 n 是一個正整數。

示例 1:

輸入: 2
輸出: 2
解釋: 有兩種方法能夠爬到樓頂。
1.  1 階 + 12.  2 階
示例 2:

輸入: 3
輸出: 3
解釋: 有三種方法能夠爬到樓頂。
1.  1 階 + 1 階 + 12.  1 階 + 23.  2 階 + 1複製代碼

涉及到動態規劃,必定要知道動態轉移方程,有了這個,就至關於解題的鑰匙,

這道題咱們假設dp[10]表示爬到是你爬到10階就到達樓頂的方法數,

那麼,dp[10] 是否是就是你爬到8階,而後再走2步就到了,還有你走到9階,再走1步就到了,

因此 dp[10] 是否是等於 dp[9]+dp[8]

延伸一下 dp[n] 是否是等於 dp[n - 1] + dp[n - 2]

代碼以下:

var climbStairs = function(n) {
    const dp = {};
    dp[1] = 1;
    dp[2] = 2;
    for(let i = 3; i <= n; i++){
        dp[i] = dp[i-1] + dp[i-2]
    }
    return dp[n]
};
複製代碼

數學問題

如下更多的是涉及數學問題,這些解法很是重要,由於在中級題裏面會常常用到,好比咱們立刻講到的加一這個題, 中級的兩數相加都是一個模板。

66. 加一

題目以下:

給定一個由 整數 組成的 非空 數組所表示的非負整數,在該數的基礎上加一。

最高位數字存放在數組的首位, 數組中每一個元素只存儲單個數字。

你能夠假設除了整數 0 以外,這個整數不會以零開頭。

示例 1:

輸入:digits = [1,2,3]
輸出:[1,2,4]
解釋:輸入數組表示數字 123。
示例 2:

輸入:digits = [4,3,2,1]
輸出:[4,3,2,2]
解釋:輸入數組表示數字 4321。
示例 3:

輸入:digits = [0]
輸出:[1]
複製代碼

這個題的關鍵有兩點:

  • 須要有一個進位的變量carry記錄到底進位是幾
  • 還須要一個每次迭代都重置和的變量sum來幫咱們算是否進位,以及進位後的數字

記住這個題,這是兩數字相加的套路,此次是+1,其實就是兩數相加的題(騰訊面試遇到過兩數相加)

var plusOne = function(digits) {
  let carry = 1; // 進位(由於咱們肯定+1,初始化進位就是1)
  for(let i = digits.length - 1; i >= 0; i--){
      let sum = 0; // 這個變量是用來每次循環計算進位和digits[i]的值的
      sum = digits[i] + carry; 
      digits[i] = sum % 10; // 模運算取個位數
      carry = (sum / 10) | 0; // 除以10是取百位數,而且|0表示捨棄小數位
  }
  if(digits[0] === 0) digits.unshift(carry);
  return digits
};
複製代碼

69 x的平方根

題目以下: 實現 int sqrt(int x) 函數。

計算並返回 x 的平方根,其中 x 是非負整數。

因爲返回類型是整數,結果只保留整數的部分,小數部分將被捨去。

示例 1:

輸入: 4
輸出: 2
複製代碼

示例 2:

輸入: 8
輸出: 2
說明: 8 的平方根是 2.82842..., 
     因爲返回類型是整數,小數部分將被捨去。
複製代碼

這道題是典型的二分法解題,因此咱們須要熟悉二分法的通用模板,咱們出一個題:

在 [1, 2, 3, 4, 5, 6] 中找到 4,若存在則返回下標,不存在返回-1

const arr = [1, 2, 3, 4, 5, 6];
function getIndex1(arr, key) {
  let low = 0;
  const high = arr.length - 1;
  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    if (key === arr[mid]) {
      return mid;
    }
    if (key > arr[mid]) {
      low = mid + 1;
    } else {
      height = mid - 1;
    }
  }
  return -1;
}
console.log(getIndex1(arr, 5)); // 4
複製代碼

因此這道題的意思就是,咱們找一個數平方跟x最相近的數,二分法的用法中也有找相近數的功能

因此代碼以下:

var mySqrt = function(x) {
    let [l , r] = [0, x];
    let ans = -1;
    while(l <= r) {
        const mid = (l + r) >> 1;
        if(mid * mid > x){
            r = mid - 1
        } else if(mid * mid < x){
            ans = mid; // 防止越界
            l = mid + 1;
        } else {
            ans = mid;
            return ans;
        }
    }
    return ans;
};
};
複製代碼

171. Excel表序列號

這個題比較重要,也比較基礎,簡而言之就是進制轉換,必須緊緊掌握

題目以下:

給你一個整數 columnNumber ,返回它在 Excel 表中相對應的列名稱。

例如:

A -> 1
B -> 2
C -> 3
...
Z -> 26
AA -> 27
AB -> 28 
...
複製代碼
示例 1:

輸入:columnNumber = 1
輸出:"A"
示例 2:

輸入:columnNumber = 28
輸出:"AB"
示例 3:

輸入:columnNumber = 701
輸出:"ZY"
示例 4:

輸入:columnNumber = 2147483647
輸出:"FXSHRXW"
複製代碼

說白了,這就是一道26進制的問題,之前咱們知道10進制轉2進制就是不停的除2,把餘數加起來,26進制也是同樣,不停的除26

思路:

  • 初始化結果 ans = 0,遍歷時將每一個字母與 A 作減法,由於 A 表示 1,因此減法後須要每一個數加 1,計算其表明的數值 num = 字母 - ‘A’ + 1
  • 由於有 26 個字母,因此至關於 26 進制,每 26 個數則向前進一位
  • 因此每遍歷一位則ans = ans * 26 + num
  • 以 ZY 爲例,Z 的值爲 26,Y 的值爲 25,則結果爲 26 * 26 + 25=701
var titleToNumber = function(columnTitle) {
    let ans = 0;
    for(let i = 0; i < columnTitle.length; i++){
        ans = ans * 26 + (columnTitle[i].charCodeAt() - 'A'.charCodeAt() + 1)
    }
    return ans;
};
複製代碼

172. 階乘中的零

題目:

給定一個整數 n,返回 n! 結果尾數中零的數量。

示例 1:

輸入: 3
輸出: 0
解釋: 3! = 6, 尾數中沒有零。
示例 2:

輸入: 5
輸出: 1
解釋: 5! = 120, 尾數中有 1 個零.
複製代碼

這道題很簡單,有多少個5就有多少個0,爲何這麼說呢,咱們分析一下題目

好比說 5!,

  • 也就是 5 * 4 * 3 * 2 * 1 = 120,咱們發現只有1個0,怎麼產生的呢,主要形成者就是 2 * 5 構造了一個0

  • 再看看10!

10! = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 其中,除了10 = 2 * 5和自己有一對2 * 5,因此有兩個0,這樣這道題的規律就出來了,咱們再精進一步

image.png

如上圖,每四個數字都會出現一個或者多個2的因子,可是隻有每 5 個數字才能找到一個或多個5的因子。因此整體上看來,2的因子是遠遠多於5的因子的,因此咱們只須要找5的倍數就能夠了。

咱們再進一步,按照上面的說法,咱們須要計算好比10的階乘有多少個0,要把10的階乘算出來,其實咱們只須要算10有幾個5就行了,爲何呢

咱們發現只有5的倍數的階乘,纔會產生5, 因此咱們須要看看階層數有多少個5,代碼以下:

var trailingZeroes = function (n) {
  let r = 0;
  while (n > 1) {
    n = Math.floor(n / 5);
    r += n;
  }
  return r;
};
複製代碼

190.顛倒二進制位

題目以下:

顛倒給定的 32 位無符號整數的二進制位。

示例 1:

輸入: 00000010100101000001111010011100
輸出: 00111001011110000010100101000000
解釋: 輸入的二進制串 00000010100101000001111010011100 表示無符號整數 43261596,
     所以返回 964176192,其二進制表示形式爲 00111001011110000010100101000000複製代碼

示例 2:

輸入:11111111111111111111111111111101
輸出:10111111111111111111111111111111
解釋:輸入的二進制串 11111111111111111111111111111101 表示無符號整數 4294967293,
     所以返回 3221225471 其二進制表示形式爲 10111111111111111111111111111111複製代碼

這類題,就是翻轉字符串,咱們能夠把其轉爲字符串,再轉成數組,再reverse一下,這裏咱們選用數學的方式去解答,不用這種轉字符串的方式。

解答這道題以前,咱們須要瞭解的前置知識:

  1. 與預算 &
1 & 1 // 1的2進制最後一位是1,獲得1
2 & 0 // 2的2進制最後一位是0,獲得0
3 & 1 // 3的2進制最後一位是1,獲得1
4 & 0 // 4的2進制最後一位是0,獲得0
複製代碼

因此咱們知道了怎麼取10進制最後1位的2進制是幾。

  1. JavaScript 使用 32 位按位運算數(意思是咱們的按位運算都會轉成32位,你的數字不能超過32位,會出問題)
  • JavaScript 將數字存儲爲 64 位浮點數,但全部按位運算都以 32 位二進制數執行。

  • 在執行位運算以前,JavaScript 將數字轉換爲 32 位有符號整數。

  • 執行按位操做後,結果將轉換回 64 位 JavaScript 數。

  1. '<< 1' 運算

這個運算實際上就是把10進制乘以2,這個乘2在2進制上表現出右邊填了一個0,咱們距舉例來講,

  • 2的2進制是 10,2 << 1 獲得4, 4的2進制是100,因此比10多了個0
  • 3的2進制是 11,3 << 1 獲得6。 6的2進制是110,因此比11多了個0

以上就是規律

思路:循環取最後一位拼接起來便可

var reverseBits = function (n) {
  let result = 0
  for (let i = 0; i < 32; i++) {
    result = (result << 1) + (n & 1)
    n = n >> 1
  }
  // 爲何要 >>> 0 呢,一位javascript沒有無符號整數,全是有符號的
  // 不>>>0的話,得出來的值是負數,可是無符號整數是沒有符號的
  // javascript 有符號轉化爲無符號的方法就是>>>0
  return result >>> 0
}
複製代碼

268. 丟失的數字

題目以下:

給定一個包含 [0, n] 中 n 個數的數組 nums ,找出 [0, n] 這個範圍內沒有出如今數組中的那個數。

進階:

你可否實現線性時間複雜度、僅使用額外常數空間的算法解決此問題?  

示例 1:

輸入:nums = [3,0,1]
輸出:2
解釋:n = 3,由於有 3 個數字,因此全部的數字都在範圍 [0,3] 內。2 是丟失的數字,由於它沒有出如今 nums 中。
示例 2:

輸入:nums = [0,1]
輸出:2
解釋:n = 2,由於有 2 個數字,因此全部的數字都在範圍 [0,2] 內。2 是丟失的數字,由於它沒有出如今 nums 中。
複製代碼

這題很簡單,就是用0-n的總和減去數組總和

  • 0 - n 的總和用等差數列:(首數+尾數)* 項數 / 2 來求
var missingNumber = function(nums) {
    const len = nums.length
 
   let sum = ((1 + len) * len) / 2
 
   for (let i = 0; i < len; i++) {
     sum -= nums[i]
   }
 
   return sum
 }
複製代碼

3的冪

題目以下:

給定一個整數,寫一個函數來判斷它是不是 3 的冪次方。若是是,返回 true ;不然,返回 false 。

整數 n 是 3 的冪次方需知足:存在整數 x 使得 n == 3的x次方  

示例 1:

輸入:n = 27
輸出:true
示例 2:

輸入:n = 0
輸出:false
示例 3:

輸入:n = 9
輸出:true
複製代碼

思路

  • 咱們拿27來講:27 = 3 * 3 * 3,因此27是3的冪次方
  • 咱們拿29來講: 29 = 3 * 3 * 3點幾

也就是說,若是是3的冪次方,一直除以3,除到最後就等於1好比27/3/3/3等於1 若是不是3的冪次方,除到最後就是3點幾/3 等於1點幾

代碼就出來了判斷是否是等於1便可

var isPowerOfThree = function(n) {
    while(n >= 3){
        n /= 3;
    }
    return n === 1;
};
複製代碼

412. Fizz Buzz

這個題沒啥好說的,就按照題目說的寫代碼就行,先看題目:

寫一個程序,輸出從 1 到 n 數字的字符串表示。

  1. 若是 n 是3的倍數,輸出「Fizz」;

  2. 若是 n 是5的倍數,輸出「Buzz」;

  3. 若是 n 同時是3和5的倍數,輸出 「FizzBuzz」。

示例:

n = 15,

返回:
[
    "1",
    "2",
    "Fizz",
    "4",
    "Buzz",
    "Fizz",
    "7",
    "8",
    "Fizz",
    "Buzz",
    "11",
    "Fizz",
    "13",
    "14",
    "FizzBuzz"
]
複製代碼
var fizzBuzz = function (n) {
    const list = [];
    for (let i = 1; i <= n; i++) {
      const is3Times = i % 3 === 0; // 是不是3的倍數
      const is5Times = i % 5 === 0; // 是不是5的倍數
      const is15Times = is3Times && is5Times; // 是不是15的倍數
      if (is15Times) {
        list.push('FizzBuzz');
        continue;
      }
      if (is3Times) {
        list.push('Fizz');
        continue;
      }
      if (is5Times) {
        list.push('Buzz');
        continue;
      }
      list.push(`${i}`);
    }
    return list;
  };
複製代碼
    1. 整數反轉

這個題跟以前的excel序號題差很少,咱們先看題目:

屏幕快照 2021-07-27 上午10.55.40.png

思路以下: 這道題能夠將數字轉字符串而後翻轉,咱們不用這種方法,用更純正的數學方法,速度和效率更好。

假設咱們有一個數字12345,下圖展現了翻轉的過程

image.png

var reverse = function(x) {
    let ret = 0;
    while(x){
        ret = ret * 10 + x % 10;
        if(ret > Math.pow(2, 31) - 1 || ret < Math.pow(-2, 31)) return 0;
        x = (x / 10) | 0
    }
    return ret
};
複製代碼

環問題

這類問題的特色就是,你要循環尋找,到底怎麼循環尋找,看題便知。

141. 環形鏈表

題目以下:

給定一個鏈表,判斷鏈表中是否有環。

若是鏈表中有某個節點,能夠經過連續跟蹤 next 指針再次到達,則鏈表中存在環。 爲了表示給定鏈表中的環,咱們使用整數 pos 來表示鏈表尾鏈接到鏈表中的位置(索引從 0 開始)。 若是 pos 是 -1,則在該鏈表中沒有環。注意:pos 不做爲參數進行傳遞,僅僅是爲了標識鏈表的實際狀況。

若是鏈表中存在環,則返回 true 。 不然,返回 false 。

示例 1:

image.png

輸入: head = [3,2,0,-4], pos = 1
輸出: true
解釋: 鏈表中有一個環,其尾部鏈接到第二個節點。
複製代碼

示例 2: image.png

輸入: head = [1,2], pos = 0
輸出: true
解釋: 鏈表中有一個環,其尾部鏈接到第一個節點。
複製代碼

咱們採用標記法:

給遍歷過的節點打記號,若是遍歷過程當中遇到有記號的說明已環

var hasCycle = function(head) {
    let traversingNode = head;
    while(traversingNode){
        if(traversingNode.isVistitd) return true
        traversingNode.isVistitd = true
        traversingNode = traversingNode.next
    }
    return false;
};
複製代碼

160. 相交鏈表

題目以下:

給你兩個單鏈表的頭節點 headA 和 headB ,請你找出並返回兩個單鏈表相交的起始節點。若是兩個鏈表沒有交點,返回 null 。

圖示兩個鏈表在節點 c1 開始相交:

image.png

題目數據 保證 整個鏈式結構中不存在環。

注意,函數返回結果後,鏈表必須 保持其原始結構 。

示例 1:

image.png

輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
輸出:Intersected at '8'
解釋:相交節點的值爲 8 (注意,若是兩個鏈表相交則不能爲 0)。
從各自的表頭開始算起,鏈表 A 爲 [4,1,8,4,5],鏈表 B 爲 [5,0,1,8,4,5]。
在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。
複製代碼

示例 2:

image.png

輸入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
輸出:Intersected at '2'
解釋:相交節點的值爲 2 (注意,若是兩個鏈表相交則不能爲 0)。
從各自的表頭開始算起,鏈表 A 爲 [0,9,1,2,4],鏈表 B 爲 [3,2,4]。
在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。
複製代碼

稍後更新本文章

202. 快樂數

題目以下: 編寫一個算法來判斷一個數 n 是否是快樂數。

「快樂數」定義爲:

  • 對於一個正整數,每一次將該數替換爲它每一個位置上的數字的平方和。
  • 而後重複這個過程直到這個數變爲 1,也多是 無限循環 但始終變不到 1。
  • 若是 能夠變爲  1,那麼這個數就是快樂數。
  • 若是 n 是快樂數就返回 true ;不是,則返回 false 。

image.png

快樂數怎麼分析呢?

咱們來看一個表,就會得出結論,一個數按照快樂數定義的方式分別每一個數字平方,會有兩種狀況

    1. 獲得1
    1. 無限循環

無限循環參照下圖

image.png

有人會說會不會一直變大,答案是不會: 咱們看下面列表,

  • 能夠看到若是你是13位,你的下一次快樂數算法會變爲4位1053,
  • 若是你是9999, 4位,下一個快樂數是324
位數 位數對應最大值 下一個快樂數
1 9 81
2 99 162
3 999 243
4 9999 324
13 9999999999999 1053

因此代碼只要判斷這兩種就好了,代碼以下:

// 封裝獲取快樂數的方法
function getNext(n){
    n = String(n);
    let sum = 0;
    for(let num of n){
        sum = sum + Math.pow(+num, 2);
    }
    return sum;
}
var isHappy = function(n) {
    // 哈希表來看是否循環
    const map = {};
    while( n !== 1 ){
        map[n] = true;
        n = getNext(n)
        if(map[n]) return false
    }
    return true
};
複製代碼

後面會寫中級算法的題,請你們務必把這些基礎算法題掌握好,基礎不牢地動山搖,後面中級題不少都是在這些基礎題的基礎上的。

相關文章
相關標籤/搜索