JavaScript數據結構與算法-Array-(leetcode原題)

博客原文地址: https://finget.github.io/2019...

只出現一次的數字i

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

說明:

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

示例 1:

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

輸入: [4,1,2,1,2]
輸出: 4

主要運用的就是異或運算和交換定律。javascript

例如:1 ^ 1 = 02 ^ 2 = 00 ^ 1 = 11 ^ 1 ^ 2 ^ 3 ^ 2 ^ 4 ^ 3 = 4前端

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    // 這個方法能夠找出存在奇數次的數字,不必定只有一次
    for(let i = 1;i<nums.length;i++) {
        nums[0] = nums[0] ^ nums[i]
    }
    return nums[0]
};

只出現一次的數字ii

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

說明:

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

示例 1:

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

輸入: [0,1,0,1,0,1,99]
輸出: 99
/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    // 這個方法也能夠作上面的題,i+=2,能夠以此類推下去
    nums.sort();
    for (let i = 0; i < nums.length; i+=3) {
        if (nums[i] !== nums[i + 1]) {
          return nums[i];
          break;
        }
    }
};

兩個數組的交集i

給定兩個數組,編寫一個函數來計算它們的交集。

示例 1:

輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2]
示例 2:

輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [9,4]
說明:

輸出結果中的每一個元素必定是惟一的。 *
咱們能夠不考慮輸出結果的順序。

思路:這個題比較簡單,用filter遍歷,用indexOf判斷nums1中的數字是否存在於nums2中,這可能會有重複出現的狀況,再用Set 去重就好了。java

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    return Array.from(new Set(nums1.filter(item => nums2.indexOf(item)>-1)))
};

兩個數組的交集ii

給定兩個數組,編寫一個函數來計算它們的交集。

示例 1:

輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2,2]
示例 2:

輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [4,9]
說明:

輸出結果中每一個元素出現的次數,應與元素在兩個數組中出現的次數一致。
咱們能夠不考慮輸出結果的順序。
進階:

若是給定的數組已經排好序呢?你將如何優化你的算法?
若是 nums1 的大小比 nums2 小不少,哪一種方法更優?
若是 nums2 的元素存儲在磁盤上,磁盤內存是有限的,而且你不能一次加載全部的元素到內存中,你該怎麼辦?

思路:這個題和上面那個題,最大的區別是,數組中有重複的數字,也得返回,。並且還的考慮一下,數組的長度對遍歷的優化。個人解法是判斷數組的長度,遍歷長度短的數組,由於兩個數組的交集不可能超出最短的數組,而後用indexOf判斷是不是交集,再刪除長數組中重複的這一項,進行下一次循環,由於indexOf只能找出第一個出現的位置,會出錯。例如:[2,2][1,2,1],若是不刪,返回結果是[2,2],正確結果是[2]git

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersect = function(nums1, nums2) {
    let res = []
    function fnc(min, max) {
        let index = -1
        for (let i = 0; i < min.length; i++) {
            if (max.indexOf(min[i]) > -1) {
                res.push(min[i])
                max.splice(max.indexOf(min[i]),1)
            }
        }
    }
    if (nums1.length > nums2.length) {
        fnc(nums2, nums1)
    } else {
        fnc(nums1, nums2)
    }
    return res
};

加一

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

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

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

示例 1:

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

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

思路: 我一開始想的是,轉成數字直接+1,結果發現若是數字超出最大數字就會出錯。那就只能從數組最後一位開始加了,遇到9就得向前進一位加一。這裏用的是遞歸,用了一個res臨時變量來存0,而後將原數組最後一位刪了。若是數組長度爲1,要麼=10 => return [1,0,...res],要麼<10 => [...arr,...res]github

/**
 * @param {number[]} digits
 * @return {number[]}
 */
var plusOne = function(digits) {
    let res = []
    function fnc (arr) {
        let len = arr.length - 1
        if (arr[len] + 1 == 10) {
            if (len==0) {
                return [1,0,...res]
            }
            res.unshift(0)
            arr.pop()
            // 這裏須要return 遞歸調用,否則會獲得undefined
            return fnc(arr)
        } else {
            digits[len]+=1
            return [...arr,...res]
        }
    }
    return fnc(digits)
};

電話號碼

給定一個僅包含數字 2-9 的字符串,返回全部它能表示的字母組合。
給出數字到字母的映射以下(與電話按鍵相同)。注意 1 不對應任何字母。

示例:
輸入:"23"
輸出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
說明:
儘管上面的答案是按字典序排列的,可是你能夠任意選擇答案輸出的順序。

此處輸入圖片的描述

這道題的思路就是遞歸,由於輸入的字符串長度不肯定,因此就兩個兩個的組合,好比輸入234,他們對應的字符串映射成['abc','def','ghi'],就先組合 abcdef => [["ad","ae","af","bd","be","bf","cd","ce","cf"],'ghi'] 再遞歸。算法

export default (str) => {
  // 創建電話號碼鍵盤映射
  let map = ['', 1, 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']
  // 字符串轉成數組
  let num = str.split('')
  let code = []
  // code 是存儲 str 對應的 映射 字符串的數組
  num.forEach(item => {
    if (map[item]) {
      code.push(map[item])
    }
  })
  // 遞歸函數
  let fnc = arr => {
    let tmp = []
    for (let i = 0; i < arr[0].length; i++) {
      for (let j = 0; j < arr[1].length; j++) {
        tmp.push(`${arr[0][i]}${arr[1][j]}`)
      }
    }
    // 替換數組前兩項,相當重要
    arr.splice(0, 2, tmp)
    if (arr.length > 1) {
      fnc(arr)
    } else {
      return tmp
    }
    // 最後會返回一個二維數組,而咱們須要的就是第一個
    return arr[0]
  }
  return fnc(code)
}

卡牌分組

給定一副牌,每張牌上都寫着一個整數。

此時,你須要選定一個數字 X,使咱們能夠將整副牌按下述規則分紅 1 組或更多組:

每組都有 X 張牌。
組內全部的牌上都寫着相同的整數。
僅當你可選的 X >= 2 時返回 true。

示例 1:

輸入:[1,2,3,4,4,3,2,1]
輸出:true
解釋:可行的分組是 [1,1],[2,2],[3,3],[4,4]
示例 2:

輸入:[1,1,1,2,2,2,3,3]
輸出:false
解釋:沒有知足要求的分組。
示例 3:

輸入:[1]
輸出:false
解釋:沒有知足要求的分組。
示例 4:

輸入:[1,1]
輸出:true
解釋:可行的分組是 [1,1]
示例 5:

輸入:[1,1,2,2,2,2]
輸出:true
解釋:可行的分組是 [1,1],[2,2],[2,2]

提示:

1 <= deck.length <= 10000
0 <= deck[i] < 10000

思路:這個題比較難,主要是最大公約數。segmentfault

最大公約數:幾個整數中公有的約數,叫作這幾個數的公約數;其中最大的一個,叫作這幾個數的最大公約數。例如:十二、16的公約數有一、二、4,其中最大的一個是4,4是12與16的最大公約數,通常記爲(12,16)=4。十二、1五、18的最大公約數是3,記爲(12,15,18)=3。
// 此方法主要用到這樣一個定理:a和b的公約數==b和a%b的公約數==a%b和b%(a%b)的公約數…………; 另外要知道.a和0的公約數==a;

function Mgn(num1,num2){  
    return num2!=0 ? Mgn(num2,num1%num2):num1; 
}
按位非運算符「~」
先看看w3c的定義:

位運算 NOT 由否認號(~)表示,它是 ECMAScript 中爲數很少的與二進制算術有關的運算符之一。

位運算 NOT 是三步的處理過程:

把運算數轉換成 32 位數字

把二進制數轉換成它的二進制反碼(0->1, 1->0)

把二進制數轉換成浮點數

簡單的理解,對任一數值 x 進行按位非操做的結果爲 -(x + 1)

console.log('~null: ', ~null);       // => -1
console.log('~undefined: ', ~undefined);  // => -1
console.log('~0: ', ~0);          // => -1
console.log('~{}: ', ~{});         // => -1
console.log('~[]: ', ~[]);         // => -1
console.log('~(1/0): ', ~(1/0));      // => -1
console.log('~false: ', ~false);      // => -1
console.log('~true: ', ~true);       // => -2
console.log('~1.2543: ', ~1.2543);     // => -2
console.log('~4.9: ', ~4.9);       // => -5
console.log('~(-2.999): ', ~(-2.999));   // => 1

那麼, ~~x就爲 -(-(x+1) + 1)

console.log('~~null: ', ~~null);       // => 0
console.log('~~undefined: ', ~~undefined);  // => 0
console.log('~~0: ', ~~0);          // => 0
console.log('~~{}: ', ~~{});         // => 0
console.log('~~[]: ', ~~[]);         // => 0
console.log('~~(1/0): ', ~~(1/0));      // => 0
console.log('~~false: ', ~~false);      // => 0
console.log('~~true: ', ~~true);       // => 1
console.log('~~1.2543: ', ~~1.2543);     // => 1
console.log('~~4.9: ', ~~4.9);       // => 4
console.log('~~(-2.999): ', ~~(-2.999));   // => -2
/**
 * @param {number[]} deck
 * @return {boolean}
 */
var hasGroupsSizeX = function(deck) {
    
    let map = {}
    for(let item of deck) {
        map[item] = ~~map[item] + 1
    }
    // map = {0:2,1:2,3:4} 這就是各個數出現的次數,而後去它們的最大公約數
    const min = Math.min(...Object.values(map))

    if(min < 2) return false
  
    for (let index of Array(min).fill().keys()) {
        if(index === 0) continue
        // 取最大公約數
        if(Object.values(map).every(item => item % (index + 1) === 0)) {
            return true
        }
    }
  
    return false
};
// 這是leetcode的最優解法
/**
 * @param {number[]} deck
 * @return {boolean}
 */

const gcd = (...arr) => {
  // 取最大公約數
  let _gcd = (x, y) => (!y ? x : gcd(y, x % y))
  return [...arr].reduce((a, b) => _gcd(a, b))
}
var hasGroupsSizeX = function (deck) {
  
  let obj = {}
  deck.forEach(v => { obj[v] ? obj[v]++ : obj[v] = 1 })
  let arr = Object.values(obj)
  return gcd(...arr) !== 1
};

找出字符串中出現次數最多的字符

根據上面的題得出了這個解法數組

function maxStr(str) {
  let map = {}
  for(let v of str) {
    map[v] = ~~map[v] + 1
  }
  // 將object的value 變成一個數組
  let max = Math.max(...Object.values(map))
  for (let key in map) {
    if (map[key] == max){
      return key
    }
  }
}

種花問題

假設你有一個很長的花壇,一部分地塊種植了花,另外一部分卻沒有。但是,花卉不能種植在相鄰的地塊上,它們會爭奪水源,二者都會死去。

給定一個花壇(表示爲一個數組包含0和1,其中0表示沒種植花,1表示種植了花),和一個數 n 。可否在不打破種植規則的狀況下種入 n 朵花?能則返回True,不能則返回False。

示例 1:

輸入: flowerbed = [1,0,0,0,1], n = 1
輸出: True
示例 2:

輸入: flowerbed = [1,0,0,0,1], n = 2
輸出: False
注意:

數組內已種好的花不會違反種植規則。
輸入的數組長度範圍爲 [1, 20000]。
n 是非負整數,且不會超過輸入數組的大小。

思路:[0,0,0] 先後都是0,就能夠插入一個,而後數組下標加2,再判斷。函數

暴力求解
/**
 * @param {number[]} flowerbed
 * @param {number} n
 * @return {boolean}
 */
var canPlaceFlowers = function(flowerbed, n) {
    let blank = 0
    if (flowerbed.length == 1&&flowerbed[0]==0) {
        return 1 >= n
    }
    for(let i = 0;i<flowerbed.length;i++) {
        if(!flowerbed[i]&&!flowerbed[i-1]&&!flowerbed[i+1]) {
            blank++;
            // 其實這裏i是日後跳兩位,可是這裏只加一,由於for裏面還有i++
            i++;
            // 及時跳槽循環,減小時間
            if (blank >= n) {
              break;
            }
        }
    }
    return blank >= n
};

最後

建立了一個前端學習交流羣,感興趣的朋友,一塊兒來嗨呀!
學習

相關文章
相關標籤/搜索