常考数据结构和算法--JS篇

本文总结基础的数据结构算法题的JS实现:
看需求:👇
手撕前端基础题,请左拐:

leetcode高频算法题的JS/Python实现,请右拐:

前端面试基础(Vue知识整理),请立正:


博主正努力学习,抓紧更新中,冲鸭~

数据结构与算法JS实现



前言

别问,问就是一起卷。(还嫌不够卷吗,/(ㄒoㄒ)/~~)


一、排序算法

1.1 冒泡

//  冒泡排序
const bubbleSort = (arr)=>{
  if (arr.length <=1)  return
    for(let i=0;i<arr.length;i++) {
      let hasChange = false;
      for (let j=0;j<arr.length-i-1;j++) {
        if (arr[j] > arr[j+1]) {
          const temp = arr[j]
          arr[j] = arr[j+1]
          arr[j+1] = temp
          hasChange = true
        }
      }
    //   当hasChange为false时,所有元素已到位
      if(!hasChange) break
    }
    console.log(arr);
};

1.2 插入

const insertionSort = (arr) => {
    if (arr.length <= 1) return
    for (let i = 1; i < arr.length; i++) {
        const temp = arr[i]
        let j = i - 1
        // 若arr[i]前有大于arr[i]的值的化,向后移位,腾出空间,直到一个<=arr[i]的值
        for (j; j >= 0; j--) {
            if (arr[j] > temp) {
                arr[j + 1] = arr[j]
            } else {
                break
            }
        }
        arr[j + 1] = temp
    }
    console.log(arr)
}

1.3 选择

// 选择排序
const selectionSort = (arr) => {
    if (arr.length <= 1) return
    // 需要注意这里的边界, 因为需要在内层进行 i+1后的循环,所以外层需要 数组长度-1
    for (let i = 0; i < arr.length - 1; i++) {
        let minIndex = i
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j // 找到整个数组的最小值
            }
        }
        const temp = arr[i]
        arr[i] = arr[minIndex]
        arr[minIndex] = temp
    }
    console.log(arr)
}

const test = [4, 5, 6, 3, 2, 1]
bubbleSort(test)
const testSort = [4, 1, 6, 3, 2, 1]
insertionSort(testSort)
const testSelect = [4, 8, 6, 3, 2, 1, 0, 12]
selectionSort(testSelect)

1.4 快排(⭐)

// 选择一个元素作为"基准"
// 小于"基准"的元素,都移到"基准"的左边;大于"基准"的元素,都移到"基准"的右边。
// 对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
function quickSort(arr) {
    // 交换元素
    function swap(arr, a, b) {
        var temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

// 分区函数
    const partition = (arr,pivot,left,right)=>{
        const pivotVal = arr[pivot];
        let startIndex = left;
        for(let i=left;i<right;i++) {
            if(arr[i]<pivotVal) {
                swap(arr,i,startIndex);
                startIndex++
            }
        }
        swap(arr,startIndex,pivot);
        return startIndex
    };

//快排主函数
    const sort = (arr,left,right)=>{
        if(left < right) {
            let pivot = right;
            let partitionIndex = partition(arr,pivot,left,right);
            sort(arr,left,partitionIndex-1<left?left:partitionIndex-1);
            sort(arr,partitionIndex+1>right?right:partitionIndex+1,right)
        }
    };

    sort(arr,0,arr.length-1);
    return arr;
}

console.log(quickSort([6,5,7,2,4,2,4,2,6,1,1])); // [1,1,2,2,2,4,4,5,6,6]

1.5 归并

/**
 * @Description: 归并排序的核心思想是分治,把一个复杂问题拆分成若干个子问题来求解。
 * 归并排序的算法思想是:把数组从中间划分为两个子数组,
 * 一直递归地把子数组划分成更小的数组,直到子数组里面只有一个元素的时候开始排序。
 * 排序的方法就是按照大小顺序合并两个元素。
 * 接着依次按照递归的顺序返回,不断合并排好序的数组,直到把整个数组排好序。
 * @author Li,Weixin
 * @date 2021/9/7
 */
const mergeArr = (left, right) => {
    let temp = [];
    let leftIndex = 0;
    let rightIndex = 0;
    // 判断2个数组中元素的大小,一次插入数组
    while (left.length > leftIndex && right.length > rightIndex) {
        if (left[leftIndex] <= right[rightIndex]) {
            temp.push(left[leftIndex]);
            leftIndex++
        } else {
            temp.push(right[rightIndex]);
            rightIndex++
        }
    }
    return temp.concat(left.slice(leftIndex)).concat(right.splice(rightIndex))
};

const mergeSort = (arr) => {
    // 当任意数组分解到只有一个时返回
    if (arr.length <= 1) return arr;
    const middle = Math.floor(arr.length / 2);
    const left = arr.slice(0, middle);
    const right = arr.slice(middle);
    //递归 分解 合并
    return mergeArr(mergeSort(left), mergeSort(right))
};

const testArr = [];
let i = 0;
while (i < 100) {
    testArr.push(Math.floor(Math.random() * 1000));
    i++
}
const res = mergeSort(testArr);
console.log(res)

1.6 基数

二、二分查找(⭐)

Binary Search :一种非常高效的查找算法,时间复杂度O(logn),解释如下:

在这里插入图描述

// 二分查找得非递归实现
function binary_search(nums,target) {
    let left = 0;
    let right = nums.length - 1;
    while (left <= right) {  //循环退出条件
        mid = left + ((right - left) >> 1); //位运算求中位数
        if (target === nums[mid]) {
            return mid
        } else if (target < nums[mid]) {
            right = mid - 1
        } else {
            left = mid + 1
        }
    }
    return -1
}

//二分查找得递归实现
function binary_research(arr,left,right,key) {
    if (left > right) {
        return -1
    }
    let mid = left + ((right-left)>>1);
    if(arr[mid] === key){
        return mid;
    }else if (arr[mid] > key){
        return binary_research(arr,left,mid-1,key)
    }else{
        return binary_research(arr,mid+1,right,key)
    }
}

二分查找的局限性:

  • 数组必须有序
  • 数据量太小,顺序遍历即可;数据量太大也不好,二分查找底层依赖数组这周数据结构,需要连续的内存空间。

2.1 求解平方根

三、链表相关

3.1 反转单链表

//非递归实现
var reverseList = function(head) {
    //let [prev, curr] = [null, head];
    let prev=null;
    let curr=head;
    while (curr) {
        let tmp = curr.next;    // 1. 临时存储当前指针后续内容
        curr.next = prev;       // 2. 反转链表
        prev = curr;            // 3. 接收反转结果
        curr = tmp;             // 4. 接回临时存储的后续内容
    }
    return prev;
};

3.2 未排序链表去重

//移除未排序链表中的重复节点。保留最开始出现的节点。(两层循环)
//输入:[1, 2, 3, 3, 2, 1]
//输出:[1, 2, 3]
const removeDuplicateNodes = (head) => {
    let p = head;
    while (p) {
        let q = p;
        while (q.next) {
            if (q.next.val === p.val) {
                q.next = q.next.next;
            } else {
                q = q.next;
            }
        }
        p = p.next;
    }
    return head
}	

3.3 排序列表去重

const deleteDuplicates = (head) => {
    let current = head; //把首节点指针赋给current
    while (current && current.next) { //当前节点及下一节点不为空
        if (current.val === current.next.val) {
            current.next = current.next.next;
        }else{
            current = current.next
        }
    }
    return head //返回首节点
}

3.4 单链表删除节点

//给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
//返回删除后的链表的头节点。

const deleteNode = (head, val) => {
    let pre = new ListNode(0); // 额外添加头节点 哨兵节点 考虑要删除的节点在第一个
    pre.next = head;
    let node = pre;
    while (node.next) {
        if (node.next.val === val) {
            node.next = node.next.next;
            break;
        }
        node = node.next;
    }
    return pre.next;
}

3.5 链表partition

// 思路:一拆为二然后合并
function partition(head, x) {
    let dummy1 = new ListNode(-1); //辅助节点
    let dummy2 = new ListNode(-1);
    let p1 = dummy1;
    let p2 = dummy2;
    let p = head;
    while (p != null) {
        if (p.val < x) {
            p1 = p1.next;
        } else {
            p2.next = p;
            p2 = p2.next;
        }
        p = p.next;
    }
    if (dummy1.next == null) {
        return head;
    } else {
        p1.next = dummy2.next;
        p2.next = null; //以null结尾
        return dummy1.next;
    }
}

3.6 寻找链表倒数第K个节点

3.7 删除列表倒数第N个节点

3.8 判断是否为回文链表

const isPalindrome = (head) => {
    let nums = [];
    while (head) {
        nums.push(head.val);
        head = head.next;
    }
    while (nums.length > 1) { //注意大于1,考虑基数个
        if (nums.pop() !== nums.shift()) return false;
    }
    return true;
}

3.9 判断链表是否有环

const hasCycle = (head) => {
    if (!head || !head.next) return false;
    let slow = head;
    let fast = head.next;
    while (slow !== fast) {
        if(!fast || !fast.next) return false;
        fast = fast.next.next;
        slow = slow.next;
    }
    return true
}

3.10 环形链表第一个入环节点

3.11 两个链表的第一个公共节点

3.12 合并两个排序列表(K个?)

3.13 奇偶链表

四、二叉树相关

4.1 前序/中序/后序遍历

  • 先序遍历
var preorderTraversal = function(root) {
  const res = []
  function traversal (root) {
    if (root !== null) {
      res.push(root.val) // 访问根节点的值
      traversal(root.left) // 递归遍历左子树
      traversal(root.right) // 递归遍历右子树
    }
  }
  traversal(root)
  return res
}
  • 中序遍历
var inorderTraversal = function(root) {
  const res = []
  function traversal (root) {
    if (root !== null) {
      traversal(root.left)
      res.push(root.val)
      traversal(root.right)
    }
  }
  traversal(root)
  return res
}
  • 后序遍历
var postorderTraversal = function(root) {
  const res = []
  function traversal (root) {
    if (root !== null) {
      traversal(root.left)
      traversal(root.right)
      res.push(root.val)
    }
  }
  traversal(root)
  return res
}

4.2 反转二叉树

var invertTree = function(root) {
  function traversal (root) {
    if (root === null) {
      return null
    } else {
      [root.left, root.right] = [traversal(root.right), traversal(root.left)]
      return root
    }
  }
  return  traversal(root)
}

4.3 DFS/BFS

var levelOrder = function(root) {
  const res = []
  function traversal (root, depth) {
    if (root !== null) {
      if (!res[depth]) {
        res[depth] = []
      }
      traversal(root.left, depth + 1)
      res[depth].push(root.val)
      traversal(root.right, depth + 1)
    }
  }
  traversal(root, 0)
  return res
}

4.4 二叉树深度

function TreeDepth(pRoot) {
//树的深度=左子树的深度和右子树深度中最大者+1
	if (pRoot === null) return 0;
	var leftDep = TreeDepth(pRoot.left);
	var rightDep = TreeDepth(pRoot.right);
	return Math.max(leftDep, rightDep) + 1;
}

4.5 二叉树的最小深度

// 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
var minDepth = function (root) {
    if (root == null) {
        return 0;
    }
    if (root.left == null && root.right == null) {
        return 1;
    }
    let ans = Infinity;
    if (root.left != null) {
        ans = Math.min(minDepth(root.left), ans);
    }
    if (root.right != null) {
        ans = Math.min(minDepth(root.right), ans);
    }
    return ans + 1;
};
  • 最大深度
var maxDepth = function (root) {
  let res = 0
  function traversal (root, depth) {
    if (root !== null) {
      if (depth > res) {
        res = depth
      }
      if (root.left) {
        traversal(root.left, depth + 1)
      }
      if (root.right) {
        traversal(root.right, depth + 1)
      }
    }
  }
  traversal(root, 1)
  return res
}

4.6 判断二叉树是否为平衡二叉树

// 判断是否是平衡二叉树
function IsBalanced(pRoot) { //方法1:节点重复遍历了,影响效率了
    if (pRoot == null) return true;
    let leftLen = TreeDepth(pRoot.left);
    let rightLen = TreeDepth(pRoot.right);
    return Math.abs(rightLen - leftLen) <= 1 && IsBalanced(pRoot.left) && IsBalanced(pRoot.right);
//左右子树均平衡,且左右子树高度差不超过1
}
function TreeDepth(pRoot) {
    if (pRoot == null) return 0;
    let leftLen = TreeDepth(pRoot.left);
    let rightLen = TreeDepth(pRoot.right);
    return Math.max(leftLen, rightLen) + 1;
}
// 方法2:
//改进办法就是在求高度的同时判断是否平衡,如果不平衡就返回-1,否则返回树的高度。
//并且当左子树高度为-1时,就没必要去求右子树的高度了,可以直接一路返回到最上层了
function IsBalanced(pRoot) {
    return TreeDepth(pRoot) !== -1;
}
function TreeDepth(pRoot) {
    if (pRoot === null) return 0;
    const leftLen = TreeDepth(pRoot.left);
    if (leftLen === -1) return -1;//当左子树高度为-1时,就没必要去求rightLen,可以直接一路返回到最上层了
    const rightLen = TreeDepth(pRoot.right);
    if (rightLen === -1) return -1;
    return Math.abs(leftLen - rightLen) > 1 ? -1 : Math.max(leftLen, rightLen) + 1;
}

4.7 树找两(叶子)节点最长距离(相隔最长路径)

4.8 二叉树右视图(左)

4.9 二叉树路径总和

// 二叉树的路径总和
// 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。存在返回true 否则false
var hasPathSum = function (root, sum) {
// 根节点为空
    if (root === null) return false;
// 叶节点 同时 sum 参数等于叶节点值
    if (root.left === null && root.right === null) return root.val === sum;
// 总和减去当前值,并递归
    sum = sum - root.val
    return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);
};

4.10 二叉树的所有路径

const binaryTreePaths = (root)=>{
    //设置result来存储结果
    const result = [];
    const recursion = (root,path=[])=> {
        //设置终止条件,当前节点为空就不再往下走
        if (!root) return;
        //将对应的值放入path中
        path.push(root.val);
        //遍历左右子树
        recursion(root.left, path);
        recursion(root.right, path);

        //    如果当前节点的左右子树均为空,则到底了
        if (!root.left && !root.right) {
            result.push(path.join('->'))
        }
        // 回退这一步的操作,我们已经走完这条路了
        path.pop();
    };
    recursion(root,[]);
    return result
};

const root={
  val:1,
  left: {
      val:2,
      left:null,
      right: {val:5,left:null,right:null},
  },
  right: {val:3,left:null,right:null}  
};

console.log(binaryTreePaths(root));

五、动态规划

5.1 斐波那契数列

// 递归法
function Fibonacci(n) {
    if(n<2){
        return n;
    }else{
        return Fibonacci(n - 1) + Fibonacci(n-2);
    }
}
function Fibonacci(n, a, b) {//尾递归 不爆栈
    if (n <= 1) {
        return a;
    }
    return Fibonacci(n - 1, b, a + b)
}

5.2 最长公共子序列LCS

5.3 最长上升子序列

5.4 连续子数组(字串)的最大和

5.5 硬币找零问题

var coinChange = function (coins, amount) {
    let dp = new Array(amount + 1).fill(Infinity);
    dp[0] = 0;
    for (let i = 1; i <= amount; i++) {
        for (let coin of coins) {
            if (i - coin >= 0) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }
    return dp[amount] === Infinity ? -1 : dp[amount];
}

5.6 0-1背包问题

// * @param {*} capacity 允许负重大小
// * @param {*} wt 重量数组
// * @param {*} val 价值数组
// * @param {*} n 几件物品 val数组的长度
function knapSack (capacity, wt, val, n) {
    var i, w, a, b, dp = [];
    for (let i = 0; i <= n; i++) {
        dp[i] = [];
    }
    for (let j = 0; j <= n; j++) { //dp[i][w]表示:对于前i个物品,当前背包容量为w时,这种情况下可以装下的最大价值
        for (let w = 0; w <= capacity; w++) { //从0开始计数
            if (j == 0 || w == 0) {
                dp[j][w] = 0;
            } else if (wt[j-1] <= w) {
                a = val[j - 1] + dp[j - 1][w - wt[j - 1]]; // 第j个放 wt[j - 1]表示第j个物品的重量
                b = dp[j - 1][w]; // 第j个不放
                dp[j][w] = (a > b) ? a : b; // max(a,b)
            } else {
                dp[j][w] = dp[j-1][w];
            }
        }
    }
    return dp[n][capacity];
}

5.7 爬楼梯问题

六、字符串

6.1 贪心:具有给定数值的最小字符串

6.2 判断括号字符串是够有效

6.3 字符串逆序(★★★)

//JavaScript 中可以使用 split 结合 数组自带的 reverse,先转成数组,再将数组拼接成字符串。
var str = 'abc';
var reverseStr = str.split('').reverse().join('');

//经过此操作 str 还是不变
//方法二:不使用API
var x = 701120;
var digit;
var ret = 0;
var count = [];
while (x > 0) {
    digit = x % 10;
    count.push(digit);
    ret = ret*10+digit;
    x = parseInt(x / 10);
} 
console.log(count.join(""));//'021107' 字符串类型
console.log(ret);//21107 Number 类型

七、全排列1与2(Leetcode)

八、数组/队列/堆栈 常见题

8.1 合并二维有序数组成一维有序数组

function merge(left, right) {
    let result = []
    while (left.length > 0 && right.length > 0) {
        if (left[0] < right[0]) {
            /*shift()方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。*/
            result.push(left.shift())
        } else {
            result.push(right.shift())
        }
    }
    return result.concat(left).concat(right)
}
function mergeSort(arr) {
    if (arr.length === 1) {
        return arr
    }
    while(arr.length > 1){
        let arrayItem1 = arr.shift();
        let arrayItem2 = arr.shift();
        let mergeArr = merge(arrayItem1, arrayItem2);
        arr.push(mergeArr);
    }
    return arr[0]
}

let arr1 = [[1,2,3],[4,5,6],[7,8,9],[1,2,3],[4,5,6]];
let arr2 = [[1,4,6],[7,8,10],[2,6,9],[3,7,13],[1,5,12]];
console.log(mergeSort(arr1))
console.log(mergeSort(arr2))

8.2 返回数组中第K个最大元素

8.3 返回数组中和为sum的个数

8.4 实现一个字典树

8.5 数组乱序(★★★)

// 方法1:arr.sort((a,b) => Math.random() - 0.5);
arr.sort(() => Math.random() - 0.5);// 使用sort
// 方法2:时间复杂度 O(n^2)
function randomSortArray(arr) {
    let backArr = [];
    while (arr.length) {
        let index = parseInt(Math.random() * arr.length);
        backArr.push(arr[index]);
        arr.splice(index, 1);
    }
    return backArr;
}
// 方法3:时间复杂度 O(n)
function randomSortArray2(arr) {
    let lenNum = arr.length - 1;
    let tempData;
    for (let i = 0; i < lenNum; i++) {
        let index = parseInt(Math.random() * (lenNum + 1 - i));
        tempData = a[index];
        a[index] = a[lenNum - i]// 随机选一个放在最后
        a[lenNum - i] = tempData;
    }
    return arr;
}

8.6 字符串逆序

// 经此操作 str还是不变
// 方法二:不使用API

let x = 501120;

let digit;

let ret = 0;
let count = [];
while (x > 0) {
    digit = x%10;
    count.push(digit);
    ret = ret*10+digit;
    x = parseInt(x/10);
}
console.log(count.join('')); //字符串类型
console.log(typeof ret) //Number类型

字符串中出现最多的字母的次数

var str = "abcaaaaacdef";
//console.log(str.length);
var obj = {};
for(let i = 0; i < str.length; i++){
    if( !obj[str.charAt(i)]){
        obj[str.charAt(i)] = 1;
    }else{
        obj[str.charAt(i)]++;
    }
}
//存出现次数最多的值和次数
var number = '';
var num = 0;
console.log("obj为:",obj); //Object {a: 6, b: 1, c: 2, d: 1, e: 1…}
//i代表每一项
for(var i in obj){
    //console.log("i为:",i);
    //obj[i] 为每一项的值
    // console.log("obj",obj[i]);
    if(obj[i]>num){
        num = obj[i];
        number = i;
    }
}
console.log("最终出现最多的字母是:"+ number + ",次数为:"+ num);//最终出现最多的字母是:a,次数为:6
---------------------------------------------------------------
var str = 'qwertyuilo.,mnbvcsarrrrrrrrtyuiop;l,mhgfdqrtyuio;.cvxsrtyiuo';
var json = {};
//遍历str拆解其中的每一个字符将其某个字符的值及出现的个数拿出来作为json的kv
for (var i = 0; i < str.length; i++) {
    //判断json中是否有当前str的值
    if (!json[str.charAt(i)]) {
        //如果不存在 就将当前值添加到json中去
        json[str.charAt(i)] = 1;
    } else {
        //else的话就让数组中已有的当前值的index值++;
        json[str.charAt(i)]++;
    }
}
//存储出现次数最多的值和次数
var number = '';
var num=0;
//遍历json  使用打擂算法统计需要的值
for (var i in json) {
    //如果当前项大于下一项
    if (json[i]>num) {
        //就让当前值更改为出现最多次数的值
        num = json[i];
        number = i;
    }
}
//最终打印出现最多的值以及出现的次数
console.log('出现最多的值是'+number+'出现次数为'+num)

两个棧实现队列

//用两个栈来实现一个队列,完成队列的Push和Pop操作。
var stackPush = [];
var stackPop = [];
function push(node) {
// write code here
    stackPush.push(node);
}
function pop() {
    if (stackPop.length===0 && stackPush.length===0) {
        console.log("Queue is empty!");
    } else if (stackPop.length===0) {
        while (!stackPush.length===0) {
            stackPop.push(stackPush.pop());
        }
    }
    return stackPop.pop();
}

合并有序数组

var merge = function(nums1, m, nums2, n) {
    // 先将nums2合并至nums1中
    for(let i = 0; i<n; i++) {
        nums1[m] = nums2[i];
        m++;
    }
    // 再对nums1进行合并
    nums1.sort((a, b)=>a-b);
    return nums1;

};

缺点:没有利用到两个数组各自有序的前提
时间复杂度:O(m+n)log((m+n))
空间复杂度:O(log(m+n))


  • 双指针法:设有两个指针,分别指向两个数组的头部,由于最后需要返回的是nums1,故需要另设一个数组nums存放nums1初始的值。
    两个指针分别指向nums和nums2的头部。
    指针指向值小的那个先进入数组nums1,同时对应指针后移一位
    当其中一个数组已经遍历完成后,另一个数组剩下的值直接添加进数组nums1中
    在复制nums1的副本时,需要对nums1进行深复制,浅复制可能会导致数值在中途发生改变。
var merge = function(nums1, m, nums2, n) {
    
    let nums = [m];     // 将nums1的值复制一份保存在nums中
    for(let i = 0; i<m; i++) {
        nums[i] = nums1[i];
    }
    let indicator1 = 0;     // 指针指向nums1的头部
    let indicator2 = 0;     // 指针指向nums2的头部
    let index = 0;
    while(index<(m+n)) {
        if(indicator1<m && indicator2<n) {
            // 当nums和nums2两个数组都没遍历完时,数字较小的先进入数组nums1
            nums1[index++] = (nums[indicator1]<nums2[indicator2]) ? nums[indicator1++] : nums2[indicator2++];
        } else {
            // 否则其中一个数组已经遍历完成而另一个数组未遍历完成,则让未遍历完的数组中的值进入数组nums1
            nums1[index++] = (indicator1<m) ? nums[indicator1++] : nums2[indicator2++];
        }
    }
    return nums1;
};
  • 双指针、从后到前
    设有两个指针,分别指向nums1和nums2的尾部;
    指针指向值大的那个先进入数组nums1,同时对应指针前移一位;
    当nums2数组已经遍历完成,无论nums1的指针是否遍历结束,此时nums1已有序排列,赋值即可结束。
var merge = function(nums1, m, nums2, n) {
    
    let indicator1 = m-1;   // 指针指向nums1的末尾
    let indicator2 = n-1;   // 指针指向nums2的末尾
    let len = m+n-1;
    while(indicator2 >= 0) {
        // 数字较大的一个先入数组
        nums1[len--] = (nums1[indicator1]>nums2[indicator2]) ? nums1[indicator1--] : nums2[indicator2--];
    }
    return nums1;

};

两数之和

// 用 hashMap 存储遍历过的元素和对应的索引。
// 每遍历一个元素,看看 hashMap 中是否存在满足要求的目标数字。
// 所有事情在一次遍历中完成(用了空间换取时间)。

const twoSum = (nums, target) => {
  const prevNums = {};                    // 存储出现过的数字,和对应的索引               

  for (let i = 0; i < nums.length; i++) {       // 遍历元素   
    const curNum = nums[i];                     // 当前元素   
    const targetNum = target - curNum;          // 满足要求的目标元素   
    const targetNumIndex = prevNums[targetNum]; // 在prevNums中获取目标元素的索引
    if (targetNumIndex !== undefined) {         // 如果存在,直接返回 [目标元素的索引,当前索引]
      return [targetNumIndex, i];
    } else {                                    // 如果不存在,说明之前没出现过目标元素
      prevNums[curNum] = i;                     // 存入当前的元素和对应的索引
    }
  }
}

-----------------------------
 //暴力枚举法
const twoSum = function(nums, target) {
    for(let i=0;i<nums.length;i++){
        for(let j=i+1;j<nums.length;j++){
            if(nums[i] + nums[j] == target){
                return [i, j];
            }
        }
    }
};

参考资料

  1. 面试宝典
  2. 秋招面经
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值