算法-数组

25 阅读8分钟
2. 合并两个有序数组

相当于nums1长度变大;因为是有序的两个数组 谁大谁往后放;比较完最小长度 如果nums2还有剩余插到中间的空里

var merge = function(nums1, m, nums2, n) {
    let k = m + n -1
    m = m-1
    n = n-1
    while (m>=0 && n>=0){
        if(nums2[n]>nums1[m]){
            nums1[k] = nums2[n]
            n--;
        }else{
            nums1[k] = nums1[m];
            m--
        }
        k--;
    }
    while (n>=0){
        nums1[k] = nums2[n]
        k--;
        n--;
    }

    return nums1;
};
3. 两数之和

巧用map存储已经遍历的值

var twoSum = function(nums, target) {
    let tempMap = new Map()
    for(let i=0;i<nums.length;i++){
        if(tempMap.has(target - nums[i])){
            return [tempMap.get(target - nums[i]), i]
        }else{
            tempMap.set(nums[i],i)
        }
    }
    return false;
};
13. 三数之和
var threeSum = function(nums) {
  	 // 先排序
    nums.sort((a, b) => a - b);
    const result = [];
   //  nums.length - 2
    for (let i = 0; i < nums.length - 2; i++) {
        // 重复的跳过
        if (i > 0 && nums[i] == nums[i - 1]) continue;
        let left = i + 1;
        let right = nums.length - 1;
        while (left < right) {
            const sum = nums[i] + nums[left] + nums[right];
            if (sum === 0) {
              	// 找到符合值存储  然后左右都跳过重复的  继续下一次循环
                result.push([nums[i], nums[left], nums[right]]);
                while (nums[left] === nums[left + 1]) left++;
                while (nums[right - 1] === nums[right]) right--;
                left++;
                right--;
            } else if (sum < 0) {
              	// 小于目标值左+
                left++;
            } else {
               // 大于目标值右-
                right--;
            }
        }
    }
    return result;
};
5. 最大子数组和

两个值分别存储累加值和最大值;累加后和当前值比 如果不大于累加值 则使用当前值

var maxSubArray = function(nums) {
    let currentMax = nums[0]//存储累加值
    let globalMax = nums[0]// 存储最大值
    for(let i=1;i<nums.length;i++){
      	// 累加和 + 当前值 如果大则取当前值的和 否则用当前值
        currentMax = Math.max(currentMax+nums[i], nums[i])
       // 累加值与最大值比较
        globalMax = Math.max(currentMax, globalMax)
    }
    return globalMax;
};
9. 全排列

全排列没有重复项

var permute = function(nums) {
  const result = []
  // 参数: 当前已经排列的数组 
  function backTrack(currents){
  // 如果当前排列的数组长度已经和要排列的数组长度一致 则算是一种排列方式
      if(currents.length === nums.length){
          result.push([...currents]);
          return
      }
  // 1.遍历要排列的数组
      for(let num of nums){
      // 2.如果已经在当前排列数组内部则进行下一个
          if(currents.includes(num)){
              continue;
          }
      // 3.把不在当前数组内的数 添加到当前排列数组里  
      // 4.继续调这个排列方法;再继续挨个数试
          backTrack([...currents,num])
      }
  }
  backTrack([])
  return result;
};
41. 组合总和 === target
var combinationSum = function(candidates, target) {
    const result = []
    candidates.sort((a, b) => a - b); // 首先对数组进行排序
    function backTrack(remaining, path, start){
        if(remaining==target){
            // 推入副本
            result.push([...path]);
            return
        }
        for(let i=start; i<candidates.length;i++){
            if (remaining+candidates[i] > target) {
                // 如果当前值大于剩余值,不需要继续探索
                break;
            }
          
            // 继续探索
            backTrack(remaining+candidates[i],[...path,candidates[i]],i)
         
        }
    }
    backTrack(0,[], 0)
    return result;
};
71.n个骰子的点数
// 回溯
var dicesProbability = function(n) {
    let rt = {} // 存各种点数
    let cont = 0 // 存总次数
    function  dfs(oNum) {
      	// 如果个数够了 证明是一次随机 记录当前点数 并且次数++
        if(oNum.len == n){
          	// 重复的直接+1
            if(rt[oNum.rt]){
                rt[oNum.rt] += 1
            } else{
                rt[oNum.rt] = 1
            }
            cont++;
            return 
        }
        for(let i = 1; i < 7; i++){
          // 循环6个点数 每个骰子都 六种可能
          dfs({
                len:oNum.len + 1,
                rt:oNum.rt + i 
            })
        }
    }
    dfs({len:0, rt:0})
    let rtArr = Object.values(rt)
    return rtArr.map(num=>{
        return num/cont
    })
};
16. 数组中的第K个最大元素
var findKthLargest = function(nums, k) {
    // 第k大个元素 就是从小排序的倒数第几个元素  nums.length - k
    return quickSelect(nums, 0, nums.length - 1, nums.length - k);
};

// 要排序的数组 最小 最大 第几个
function quickSelect(nums, low, high, k) {
    const pivot = nums[Math.floor((low + high) / 2)];

    let left = low;
    let right = high;

    while (left <= right) {
      	// 左侧的比中间值小的
        while (nums[left] < pivot) left++;
       // 右侧的比中间值大的
        while (nums[right] > pivot) right--;
        // 如果还没相遇 两个互换
        if (left <= right) {
            [nums[left], nums[right]] = [nums[right], nums[left]];
            left++;
            right--;
        }
    }
		// 如果目标在 right侧的左边;去最低值和right的区间找
    if (k <= right) return quickSelect(nums, low, right, k);
    // 如果目标在 left侧的右边;去left和最高值的区间找
    if (k >= left) return quickSelect(nums, left, high, k);

    return nums[k];
}
21. 长度最小的子数组 === target

双指针;初始化最小值为Infinity; 先加到大于等于target值 再缩短长度,再继续加 记录最短长度

// 先加到target值 再缩短长度,再继续加 记录最短长度
var minSubArrayLen = function(target, nums) {
    let left = 0;
    let result =0;
    let minLen = Infinity
    for(let i=0;i<nums.length;i++){
        result +=nums[i];
      	// 和 大于等于 target 开始缩短
        while (result>=target){
            minLen = Math.min(minLen, i-left+1)
            result -= nums[left]
            left++
        }
    }
    return minLen === Infinity ? 0 : minLen;
};
80.和为s的连续正数序列
var findContinuousSequence = function(target) {
    const max = target - 1, // 可选的最大正数范围 [1, max]
    queue = [], // 单调递增队列
    res = []    // 结果集
    let sum = 0
    for (let v = 1; v <= max; v++) {
        // 一次将范围值入队
        sum += v, queue.push(v)
        // 当大于期望值target 时 出队且更新sum
        while (sum > target) sum -= queue.shift()
        // 当满足条件 存入结果集
        if (sum === target && queue.length >= 2) res.push([...queue])
    }
    return res
};

72.0~n-1中缺失的数字
var missingNumber = function(nums) {
    let left = 0;
    let right = nums.length-1;
    let mid = 0
    while (left<=right) {
        mid = Math.floor((left+right)/2)
        if(mid==nums[mid]){ // 如果对应上 缺失的肯定在右边;左指针移动
            left = mid + 1
        }else{
            right = mid -1 // 如果对应不上 缺失的肯定在左边;右指针移动
        }
    }
    return left  // left是满足条件的最小索引
};
let nums = [0,1,2,3,4,5,6,7,9]

37. 寻找重复数
var findDuplicate = function(nums) {
    let low = 1;
    let high = nums.length - 1;

    while (low < high) {
        const mid = Math.floor((low + high) / 2);
        let count = 0;

        // 计算数组中有多少个数小于等于 mid
        nums.forEach((num) => {
            if (num <= mid) {
                count++;
            }
        });

        // 根据抽屉原理缩小搜索范围  如果count大 证明重复数在左边
        if (count > mid) {
            high = mid;
        } else {
            low = mid + 1;
        }
    }

    // low 和 high 相遇的地方就是重复的数字
    return low;
};
29. 将数组分成和相等的三个部分

给你一个整数数组 arr,只有可以将其划分为三个和相等的 非空 部分时才返回 true,否则返回 false

先看总数能不能被3整除; 然后得到每份多大值;循环的时候 得到target 计数 计数满足后 返回true 否则返回false

var canThreePartsEqualSum = function(arr) {
    let totalSum = arr.reduce((a,b)=>a+b)
    // 首先看总和能不能被3整除
    if(totalSum%3!==0) return  false;
    // 每一块的值
    let partSum = totalSum/3
    let sum = 0, parts = 0;
    for(let num of arr){
        sum +=num;
        if(sum === partSum){
            parts++;
            sum =0;
        }
        // 如果计算两部分  后一部分基本可以确定的 但是如果是 [-1,1,-1,1]这种会有问题 也可以当作一个优化项
        if(parts === 3)return true;
    }
    return false;
};
31. 最长连续递增序列

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

重点是连续。如果不连续了 累加器=1 连续了就一致+;最大值每次比较

var findLengthOfLCIS = function(arr) {


    if (arr.length === 0) return 0;

    let maxLen = 1;
    let currentLen = 1;

    for (let i = 1; i < arr.length; i++) {
        if (arr[i] > arr[i - 1]) {
            currentLen++;
            maxLen = Math.max(maxLen, currentLen);
        } else {
            currentLen = 1;
        }
    }
    return maxLen;
};
32. 最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

初始化一个数组 dp 与输入数组 nums 长度相同,每个元素的值为 1。

第一层遍历所有;每个遍历它前面的值 只要有比当前值小的 看这个点的dep值+1 与当前的dep比较 哪个大取哪个

如果需要得到最长的子序列。可以把dep里存数组。比较的时候用长度P「初始值可以用map填进去当前数组」


var lengthOfLIS = function(nums) {
    const dep = Array(nums.length).fill(1)
    let result = 0
    for(let i=0;i<nums.length;i++){
        for(let j=0;j<i;j++){
            if(nums[i]>nums[j]){
                // 可以不连续 上升就可以
                dep[i] = Math.max(dep[i],dep[j]+1)
            }
        }
        result = Math.max(result,dep[i])
    }
    return result;
};
93.最长重复子数组

给两个整数数组 AB ,返回两个数组中公共的、长度最长的子数组的长度。

示例:

输入: A: [1,2,3,2,1] B: [3,2,1,4,7] 输出:3 解释: 长度最长的公共子数组是 [3, 2, 1] 。

function findLength(nums1, nums2) {
    let m = nums1.length, n = nums2.length;
  //用一个二维数组 记录前面的相等次数。取最大值
    let dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
    let maxLength = 0;

    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (nums1[i - 1] === nums2[j - 1]) {
                // 主要看前一个。当前值相等 前一个的值+1为现在的值
                dp[i][j] = dp[i - 1][j - 1] + 1;
                maxLength = Math.max(maxLength, dp[i][j]);
            } else {
                dp[i][j] = 0;
            }
        }
    }

    return maxLength;
}

34. 打乱数组
/**
 * @param {number[]} nums
 */
var Solution = function(nums) {
    this.original = nums.slice();  // 保存原始数组的副本
    this.array = nums;
};

/**
 * Resets the array to its original configuration and return it.
 * @return {number[]}
 */
Solution.prototype.reset = function() {
    this.array = this.original.slice(); // 恢复原始数组的副本
    return this.array;
};

/**
 * Returns a random shuffling of the array.
 * @return {number[]}
 */
Solution.prototype.shuffle = function() {
    for (let i = 0; i < this.array.length; i++) {
        let rand = Math.floor(Math.random() * (i + 1)); // 随机选择一个小于或等于 i 的索引
        // 交换当前元素和随机选择的元素
        [this.array[i], this.array[rand]] = [this.array[rand], this.array[i]];
    }
    return this.array;
};
76.调整数组顺序使奇数位于偶数前面
var exchange = function(nums) {
    let rt = []
    for(let i=0,len=nums.length;i<len;i++){
        if(nums[i]%2 ==0){
            rt.push(nums[i])
        }else{
            rt.unshift(nums[i])
        }
    }
    return rt;
};
let rt = exchange([1,2,3,4])
console.log(rt)

85.删除有序数组中的重复项
var removeDuplicates = function(nums) {
  if (nums.length == 0) return 0;
  let i = 0;
  for (let j = 1; j < nums.length; j++) {
    	// 如果下一个和当前的相等就直接跳过  不想等时候 把不相等的移动到 前面来覆盖掉想等的值
      if (nums[j] != nums[i]) {
          i++;
        	// 不相等时候直接下一个赋值到当前值
          nums[i] = nums[j];
      }
  }
  return i + 1;
};

// 使用了splice截取数组
var removeDuplicates = function(nums) {
    let len = nums.length
    for(let i=0;i<len;i++){
        if(nums[i] == nums[i-1]){
            nums.splice(i,1)
            len--
            i--
        }
    }
    return len
};
86.两个数组的交集

题:给定两个数组 nums1nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序

  let rtArr =[]
  while (arr1.length) {
      let item = arr1.pop()  
      let index = arr2.indexOf (item)
      if (index != -1&&!rtArr.includes(item)) {
          // 确保重复的不被漏掉
          arr2.splice(index, 1)
          rtArr.push(item)
      }
  }
  return rtArr