還欠自己一整套LeetCode 演算法題

beth_miao發表於2018-07-12

一直惦記著要刷題,但是因為上班忙,沒時間...

總而言之,各種理由一再推延...

// 建立一個陣列列表來處理排序和搜尋的資料結構
function ArrayList() {
    let arr = [];   
    
    this.insert = function(item) {  // 將資料插入到陣列中
        arr.push(item);
    };
    
    this.show = function() {    // 展示陣列的結構
        return arr.join(' < ');
    };

    // 氣泡排序
    this.bubbleSort = function () {
        let len = arr.length;
        for (let i = 0; i < len; i++) { 
            // 這裡之所以再-i,是因為外層迴圈已經跑完一輪
            // 內迴圈就沒有必要再比較一回了
            for (let j = 0; j < len - 1 - i; j++) {  
                if (arr[j] > arr[j + 1]) {  // 當前項和下一項做比較,如果大於的話就按照下面交換位置
                    // ES6利用解構賦值的方式輕鬆實現j和j+1值的交換
                    [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
                }
            }
        }
    };

    // 選擇排序
    this.selectSort = function () {
        let len = arr.length,
            min;
        for (let i = 0; i < len - 1; i++) {
            min = i;    // 我們取第一個值當標杆
            for (let j = i; j < len; j++) { // 內部迴圈從i開始到陣列結束
                if (arr[min] > arr[j]) {    // 比標杆的值還小,就替換新值
                    min = j;
                }
            }
            if (i !== min) {    // 上面經過一頓比較替換,如果標杆的值和之前取的第一個值不同了,就交換位置
                [arr[i], arr[min]] = [arr[min], arr[i]];
            }
        }
    };
    // 插入排序
    this.insertSort = function () {
        let len = arr.length,
            index, tmp;
        // 這裡預設第一項已經排序了,直接從第二項開始
        for (let i = 1; i < len; i++) {
            index = i;        // 用來記錄一個索引 
            tmp = arr[i];     // 儲存一個臨時變數,方便之後插入位置
            // 索引必須是大於0,並且陣列前一項的值如果大於臨時變數的值
            // 就將前一項的值賦給當期項,並且index--
            while (index > 0 && arr[index - 1] > tmp) { 
                arr[index] = arr[index - 1];   
                index--;  
            }
            arr[index] = tmp; // 最後在一頓替換後插入到了正確的位置上
        }
    };

    // 歸併排序
    this.mergeSort = function () {
        arr = mergeRecurve(arr);    // 由於需要不停的拆分直到陣列只有一項,所以使用遞迴來做
    };
    // 遞迴
    function mergeRecurve(arr) {
        let len = arr.length;
        // 遞迴的停止條件,如果陣列只有一項,就直接返回了
        // 這也是我們遞迴的目的,直到陣列只有一項
        if (len === 1) return arr;      
        let mid = Math.floor(len / 2);
        let left = arr.slice(0, mid);
        let right = arr.slice(mid, len);    // 到這裡把陣列一分為二

        // 為了不斷對原陣列拆分,對left和right陣列繼續遞迴,並作為引數傳給merge函式
        // merge函式負責合併和排序小陣列為大陣列
        return merge(mergeRecurve(left), mergeRecurve(right));  
    }
    function merge(left, right) {   // 接收兩個陣列,最後合併到一起返回一個大陣列
        let res = [],
            lLen = left.length,
            rLen = right.length,
            l = 0,
            r = 0;

        while (l < lLen && r < rLen) {
            // 如果left陣列的項比right陣列的項小的話,就將left這裡小的項新增到大陣列裡
            if (left[l] < right[r]) {   
                res.push(left[l++]);    // 並繼續下一項比較
            } else {
                res.push(right[r++]);   // 否則將right裡小的項先新增到大陣列中
            }
        }
        // 將left和right陣列中剩餘的項也都新增到大陣列中
        while (l < lLen) {
            res.push(left[l++]);
        }
        while (r < rLen) {
            res.push(right[r++]);
        }

        return res;  // 返回排好序的大陣列
    }
     // 快速排序
     this.quickSort = function () {
        quick(arr, 0, arr.length - 1);  // 遞迴
    }
    function quick(arr, left, right) {
        let index;
        if (arr.length > 1) {
            index = partition(arr, left, right);  // 劃分

            if (left < index - 1) {
                quick(arr, left, index - 1)
            }
            if (index < right) {
                quick(arr, index, right);
            }
        }
    }
    // 劃分函式
    function partition(arr, left, right) {
        console.log(arr, left, right)
        let point = arr[Math.floor((left+right)/2)],
            i = left,
            j = right;  // 雙指標
        
            while (i <= j) {
                while (arr[i] < point) {
                    i++;
                }
                while (arr[j] > point) {
                    j--;
                }
                if (i<=j) {
                    [arr[i], arr[j]] = [arr[j], arr[i]];  // 交換位置
                    i++;
                    j--;
                }
            }
            return i;
    }
}

// 測試用例,此測試用例在之後的演算法中皆可使用
let arr = [2, 3, 1, 4, 6, 5];
let createList = function(arr) {
    let list = new ArrayList();
    for (let i = 0; i < arr.length; i++) {
        list.insert(arr[i]);
    }
    return list;
};
let item = createList(arr);
console.log(item.show()); // 排序前 5 < 4 < 3 < 2 < 1
// item.bubbleSort();
// item.selectSort();
// item.insertSort();
// item.mergeSort();
item.quickSort();
console.log(item.show()); // 排序後 1 < 2 < 3 < 4 < 5

複製程式碼
// 給定幾種面額的硬幣和一個總額,使用最少的硬幣湊成這個總額。
let coinChange = function (coins, amount) {
    let max = amount + 1;
    let dp = new Array(amount + 1);
    dp.fill(max);
    dp[0] = 0;
    for (let i = 1; i < max; i++) {
        for (let j = 0; j < coins.length; j++) {
            if (coins[j] <= i) {
                dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)
            }
        }
    }
    return dp[amount] > amount ? -1 : dp[amount];
};
coinChange([1,3,4,5], 20)
複製程式碼

// 求出從矩陣左上角走到右下角,且只能向右向下移動,一共有多少種可能性。
let uniquePaths = function (m, n) {
    const pos = new Array(m)
    for (let i = 0; i < m; i++) {
        pos[i] = new Array(n)
    }
    for (let i = 0; i < n; i++) {
        pos[0][i] = 1
    }
    for (let i = 0; i < m; i++) {
        pos[i][0] = 1
    }
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            pos[i][j] = pos[i - 1][j] + pos[i][j - 1]
        }
    }
    return pos[m - 1][n - 1]
};
複製程式碼
// 獲取給定陣列連續元素累加最大值
var maxSubArray = function (nums) {
    let count = nums[0], maxCount = nums[0]
    for (let i = 1; i < nums.length; i++) {
        count = Math.max(count + nums[i], nums[i])
        maxCount = Math.max(maxCount, count)    
    }
    return maxCount
};
複製程式碼
// 查詢橫向和縱向都遞增的二維矩陣中的某個值
var searchMatrix = function (matrix, target) {
    if (matrix.length == 0) return false
    let row = 0, col = matrix[0].length - 1
    while (true) {
        if (matrix[row][col] > target && col > 0) {
            col--
        } else if (matrix[row][col] < target && row < matrix.length - 1) {
            row++
        } else if (matrix[row][col] == target) {
            return true
        } else {
            break
        }
    }
    return false
};
複製程式碼
// 找到陣列中最左邊和最右邊的某個數字所在位置
var searchRange = function (nums, target) {
    let targetIndex = binarySearch(nums, target, 0, nums.length - 1)
    if (targetIndex == -1) return [-1, -1]
    let l = targetIndex, r = targetIndex
    while(l > 0 && nums[l - 1] == target){
        l--
    }
    while(r < nums.length - 1 && nums[r + 1] == target){
        r++
    }
    return [l, r]
};

function binarySearch(arr, val, lo, hi) {
    if (hi < lo) return -1
    let mid = lo + parseInt((hi - lo) / 2)

    if (val < arr[mid]) {
        return binarySearch(arr, val, lo, mid - 1)
    } else if (val > arr[mid]) {
        return binarySearch(arr, val, mid + 1, hi)
    } else {
        return mid
    }
}

複製程式碼
// 找到給定字串中某段最長的迴文
var longestPalindrome = function (s) {
    let maxLength = 0, left = 0, right = 0
    for (let i = 0; i < s.length; i++) {
        let singleCharLength = getPalLenByCenterChar(s, i, i)
        let doubleCharLength = getPalLenByCenterChar(s, i, i + 1)
        let max = Math.max(singleCharLength, doubleCharLength)
        if (max > maxLength) {
            maxLength = max
            left = i - parseInt((max - 1) / 2)
            right = i + parseInt(max / 2)
        }
    }
    return s.slice(left, right + 1)
};

function getPalLenByCenterChar(s, left, right) {
    // 中間值為兩個字元,確保兩個字元相等
    if (s[left] != s[right]){
        return right - left
    }
    while (left > 0 && right < s.length - 1) {
        left--
        right++
        if (s[left] != s[right]){
            return right - left - 1
        }
    }
    return right - left + 1
}
複製程式碼
// 通過給定單詞在二維字母陣列中查詢是否能使用鄰近字母組成這個單詞
let hasWord = false

var findWords = function (board, words) {
    var ans = []
    for (let word of words) {
        for (let j = 0; j < board.length; j++) {
            for (let i = 0; i < board[0].length; i++) {
                if (board[j][i] == word[0]) {
                    hasWord = false
                    DFS(word, board, 0, j, i, "")
                    if (hasWord) {
                        if (!ans.includes(word))
                            ans.push(word)
                    }
                }
            }
        }
    }
    return ans
};

function DFS(word, board, index, j, i, subStr) {
    if (word[index] == board[j][i]) {
        subStr += board[j][i]
        board[j][i] = "*"
        if (j < board.length - 1)
            DFS(word, board, index + 1, j + 1, i, subStr)
        if (j > 0)
            DFS(word, board, index + 1, j - 1, i, subStr)
        if (i < board[0].length - 1)
            DFS(word, board, index + 1, j, i + 1, subStr)
        if (i > 0)
            DFS(word, board, index + 1, j, i - 1, subStr)
        board[j][i] = word[index]
    }
    if (index >= word.length || subStr == word) {
        hasWord = true
    }
}
複製程式碼
// 獲取二維矩陣中最大相鄰遞增陣列長度
const dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]]

var longestIncreasingPath = function (matrix) {
    if (matrix.length == 0) return 0
    const m = matrix.length, n = matrix[0].length
    let max = 1

    let cache = new Array(m)
    for (let i = 0; i < m; i++){
        let child = new Array(n)
        child.fill(0)
        cache[i] = child
    }

    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            let len = dfs(matrix, i, j, m, n, cache)
            max = Math.max(max, len)
        }
    }
    return max
}

function dfs(matrix, i, j, m, n, cache){
    if (cache[i][j] != 0) return cache[i][j]
    let max = 1
    for (let dir of dirs){
        let x = i + dir[0], y = j + dir[1]
        if(x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] <= matrix[i][j]) continue;
        let len = 1 + dfs(matrix, x, y, m, n, cache)
        max = Math.max(max, len)
    }
    cache[i][j] = max
    return max
}
複製程式碼
// 連結串列的排序
var sortList = function (head) {
    if (head == null || head.next == null) return head

    let prev = null, slow = head, fast = head
    while (fast != null && fast.next != null) {
        prev = slow
        slow = slow.next
        fast = fast.next.next
    }

    prev.next = null;

    let l1 = sortList(head)
    let l2 = sortList(slow)

    return merge(l1, l2)
};

function merge(l1, l2) {
    let l = new ListNode(0), p = l;

    while (l1 != null && l2 != null) {
        if (l1.val < l2.val) {
            p.next = l1;
            l1 = l1.next;
        } else {
            p.next = l2;
            l2 = l2.next;
        }
        p = p.next;
    }

    if (l1 != null)
        p.next = l1;

    if (l2 != null)
        p.next = l2;

    return l.next;
}
複製程式碼
// 連結串列的倒序
var reverseList = function(head) {
    let ans = null,cur = head
    while (cur != null) {
        let nextTmp = cur.next
        cur.next = ans
        ans = cur
        cur = nextTmp
    }
    return ans
};
複製程式碼
// 求陣列中第K大的值
var findKthLargest = function (nums, k) {
    for (let i = 0; i <= k; i++) {
        let max = i
        for (let j = i; j < nums.length; j++) {
            if (nums[j] > nums[max]) max = j
        }
        swap(nums, i, max)
    }
    return nums[k - 1]
};

function swap(arr, a, b) {
    let tmp = arr[a]
    arr[a] = arr[b]
    arr[b] = tmp
}
// 對有重複值的陣列 [2,0,2,1,1,0] 排序
var sortColors = function (nums) {
    sort(nums, 0, nums.length - 1)
};

function sort(arr, lo, hi) {
    if (hi <= lo) return
    let lt = lo, i = lo + 1, gt = hi;
    let v = arr[lo]
    while (i <= gt) {
        if (arr[i] < v) swap(arr, lt++, i++)
        else if (arr[i] > v) swap(arr, i, gt--)
        else i++
    }
    sort(arr, lo, lt - 1)
    sort(arr, gt + 1, hi)
}

function swap(arr, a, b) {
    let x = arr[a]
    arr[a] = arr[b]
    arr[b] = x
}
複製程式碼
// 計算 x 的 n 次方
var myPow = function (x, n) {
    if (n == 0) return 1
    if (n < 0) {
        n = -n
        x = 1 / x
    }
    return (n % 2 == 0) ? myPow(x * x, parseInt(n / 2)) : x * myPow(x * x, parseInt(n / 2));
};
複製程式碼
// 求 x 的平方根
var mySqrt = function (x) {
    let l = 0, r = x
    while (true) {
        let mid = parseInt(l + (r - l) / 2)
        if (mid * mid > x) {
            r = mid - 1
        } else if (mid * mid < x) {
            if ((mid + 1) * (mid + 1) > x) {
                return mid
            }
            l = mid + 1
        } else {
            return mid
        }
    }
};
複製程式碼
// 將一個32位數字的二進位制進行倒序
var reverseBits = function(n) {
    var t = n.toString(2).split("");
    while(t.length < 32) t.unshift("0"); // 插入足夠的 0
    return parseInt(t.reverse().join(""), 2);
};

複製程式碼
// Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1.
var firstUniqChar = function (s) {
  for (let i = 0; i < s.length; i++) {
    let item = s[i];
    if (s.lastIndexOf(item) === s.indexOf(item)){
      return i;
    }
  }
  return -1;
};

console.log(firstUniqChar('leetcode'));
複製程式碼
// Given two arrays, write a function to compute their intersection.
var intersect = function (nums1, nums2) {
  let map1 = {},
    map2 = {},
    result = [];
  nums1.forEach((num) => {
    if (map1[num] === undefined) {
      map1[num] = 1
    } else {
      map1[num]++
    }
  })
  nums2.forEach((num) => {
    if (map2[num] === undefined) {
      map2[num] = 1
    } else {
      map2[num]++
    }
  })
  Object.keys(map1).forEach((item, index) => {
    while (map1[item] && map2[item]) {
      result.push(+item)
      map1[item]--
      map2[item]--
    }
  })
  return result
};

console.log(intersect([1, 3, 4], [3, 4]));
複製程式碼
// Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var moveZeroes = function (nums) {
  let zeros = [];
  for (let i = 0; i < nums.length;) {
    if (nums[i] === 0) {
      nums.splice(i, 1)
      zeros.push(0)
      continue
    }
    i++ 
  }
  nums.splice(nums.length, 0, ...zeros)
};


moveZeroes([0, 0, 1])
複製程式碼
// Write an algorithm to determine if a number is "happy".
/**
 * @param {number} n
 * @return {boolean}
 */
var isHappy = function (n) {
  let number = n
  let map = {}
  while (!map[number]) {
    let arr = (number + '').split('')
    if (number == 1) return true
    map[number] = true
    number = 0;
    arr.forEach((num) => {
      number += num * num
    })
  }
  return false
};

console.log(isHappy(19));
複製程式碼
// LeetCode #53. Maximum Subarray
const maxSubArray=(A)=> {
  let start = 0;
  let end = 0;
  let n = A.length;
  let dp = new Array(n);//dp[i] means the maximum subarray ending with A[i];
  dp[0] = A[0];
  let max = dp[0];
  for(let i = 1; i < n; i++){
      if(dp[i - 1] > 0){
        end=i;
      }else{
        start=i;
        end=i;
      }
      dp[i] = A[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0);
      max = Math.max(max, dp[i]);
  }
  let data={
    max,
    start,
    end,
  }
  return data;
}
maxSubArray([1,-2,5,2,-3,2])

function maxSubArray(A) {
  var maxSoFar=A[0], maxEndingHere=A[0];
  for (var i=1;i<A.length;++i){
    maxEndingHere= Math.max(maxEndingHere+A[i],A[i]);
    maxSoFar=Math.max(maxSoFar, maxEndingHere);	
  }
  return maxSoFar;
}

複製程式碼
// 寫一個函式求陣列的最大值和最小值
function highAndLow(numbers){ 
    numbers = numbers.split('')
    return `${Math.max(...numbers)} ${Math.min(...numbers)}`
 }
複製程式碼
// 寫一個函式判斷字串中x的數量和o的數量是否相等(忽略大小寫)
function XO(str) {
  str = str.toLowerCase().split('')
  return str.filter(x => x === 'x').length === str.filter(x => x === 'o').length
}

function XO(str) {
    return (str.match(/x/ig) || []).length === (str.match(/o/ig) || []).length;
}
複製程式碼
// 寫一個函式判斷一個數字是不是某個整數的平方。
function isSquare(n) {
  return Math.sqrt(n) % 1 === 0
}

function isSquare(n) {
  return Number.isInteger(Math.sqrt(n)
}

function isSquare(n){
  const s = Math.sqrt(n)
  return s === (s | 0)
  // return s === ( ~~s )
}
複製程式碼
// 寫一個函式,將字串除了最後的四位,其他都變成#
function maskify(cc) {
  return cc.slice(0, -4).replace(/./g, '#') + cc.slice(-4)
}
複製程式碼
// 下面三角形的數列:

// ```
//              1
//           3     5
//        7     9    11
//    13    15    17    19
// 21    23    25    27    29
// ...
// ```

// 寫一個函式,給定行的序號,然後求和:

// ```
// rowSumOddNumbers(1) // 1
// rowSumOddNumbers(2) // 3+5=8
// rowSumOddNumbers(3) // 7+9+11=27
// rowSumOddNumbers(42) // 74088
function rowSumOddNumbers(n) {
  return n*n*n
}
複製程式碼
// 將數字的每一位求平方,然後組合成新的數字(注意:請返回一個數字)
function squareDigits(num){
  return +num.toString().split('').map(i => i*i).join('')
}
複製程式碼
// 寫一個函式`solution`,求比一個數字n小的所有3和5的整數倍數和。
function solution(number){
  if(number < 0) return 0
  return [...Array(number).keys()]
    .filter(n => n % 3 === 0 || n % 5 === 0)
    .reduce((a, b) => a + b,0)
}

function solution(n) {
  const n3  = Math.floor((n-1)/3)
  const n5  = Math.floor((n-1)/5)
  const n15 = Math.floor((n-1)/15)
  return (n3+1)*(n3*3)/2 + (n5+1)*(n5*5)/2 - (n15+1)*(n15*15)/2
}
複製程式碼
// 寫一個二分查詢函式`bsearch`,一個已排序的陣列,如果找到目標值,返回目標值在陣列中的序號,如果沒有找到目標值,返回目標值應該被插入的位置。

function bsearch(A, x){
  let l = 0,
      r = A.length - 1,
      guess
      
  while(l<=r) {
    guess = Math.floor( (l + r) / 2 )
    if(A[guess] === x) return guess
    if(A[guess] > x) {
      if(guess === 0 || A[guess - 1] < x) {
        return guess
      }
      r = guess - 1
    } else {
      if(guess === A.length - 1 || A[guess + 1] > x) {
        return guess + 1
      }
      l = guess + 1
    }
  }
}
複製程式碼
// 在一個陣列中大部分都是奇數(或偶數),只有1個可能是偶數(或奇數),寫一個函式`special`找到這個不一樣的值。
function special(A){
  var evens = A.filter(a=>a%2==0)
  var odds = A.filter(a=>a%2!==0)
  return evens.length==1? evens[0] : odds[0]
}
複製程式碼
// 寫一個函式`reverse`反轉一個陣列A 不使用Array.reverse 不要建立新陣列
function reverse(A){
  for(let i = 0; i < (A.length / 2); i++){
    const t = A[i]
    A[i] = A[A.length - i - 1]
    A[A.length - i - 1] = t
  }
}
複製程式碼
// 定義陣列的旋轉操作`rotate(A, amount)`,讓數尾部amount個元素移動到頭部。
function reverse(A, start, end){
  for(let i = 0; i < (end - start + 1) / 2; i++) {
    const t = A[start + i]
    A[start + i] = A[end - i]
    A[end-i] = t
  }
}

function rotate(A, amount){
  reverse(A, 0, A.length - 1)
  reverse(A, 0, amount - 1)
  reverse(A, amount, A.length - 1)
}
複製程式碼
// 實現一個遞迴版本的reverse函式,反轉一個陣列。
function reverse(A){
  return A.length  ? 
    reverse( A.slice(1) ).concat(A[0]) : A
}

function reverse(A){
  const [f, ...tail] = A
  return [...(tail.length ? reverse(tail) : []), f]
}

// 尾遞迴
function reverse(A, i = 0){
  if(i < A.length / 2) {
    const t = A[i]
    A[i] = A[A.length - i - 1]
    A[A.length - i - 1] = t
    return reverse(A, i+1)
  }
}
複製程式碼
// 寫一個函式`flat`展平一個陣列
function flat(arr){
  return [].concat(...arr.map(x => Array.isArray(x) ? flat(x) : x))
}

複製程式碼
// 素數是(不包括1)只能被自己1整除的數字,比如2、3、5、7、11、13……都是素數,寫一個函式`is_prime`驗證一個數字是否是素數。
const is_prime = num => {
    for(let i = 2, s = Math.sqrt(num); i <= s; i++)
        if(num % i === 0) return false; 
    return num !== 1;
}
複製程式碼
// 柯里化函式curry是這樣一個函式,它將一個接受多引數的函式,轉換成為接收連續單引數的高階函式(可以被連續呼叫)。
const curry = func => {
  const g = (...allArgs) => allArgs.length >= func.length ?
    func(...allArgs) : (...args) => g(...allArgs, ...args)
  return g
}

複製程式碼

That's all 如上, 待續;

相關文章