还欠自己一整套LeetCode 算法题

本文深入探讨了多种经典数据结构的实现与应用,包括数组、链表、树等,并介绍了冒泡排序、选择排序等常见排序算法的原理及实现方式。此外,还涉及了一些实用的算法问题解决方案。

一直惦记着要刷题,但是因为上班忙,没时间...

总而言之,各种理由一再推延...

// 创建一个数组列表来处理排序和搜索的数据结构
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 如上, 待续;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值