算法面试题目

221 阅读17分钟

面试题目

这篇文章会陆续更新,很多题目不是最优解,笔者能力很有限,只是笔者在自己的能力范围内写出的题解,仅作参考,如有更优解欢迎在评论区分享,欢迎批评指正。

荷兰国旗问题(三指针) :就如荷兰国旗所示:我们需要将一个无序的数组,按照给定的某一个界定值target将数组分成三部分就好比荷兰的三色旗,分为小于等于大于三部分

题解:定义三个指针,不创建额外的空间,就在原数组的基础之上将数组划分为三个区域,指针的移动和数组中元素的交换,什么时候需要移动,什么时候不需要都要注意

let arr = [2, 3, 6, 8, 6, 7];
/**
 * @param {*} arr 数组
 * @param {*} target 界定值
 */
function flagColorQues(arr, target) {
  let p1 = 0; //左区域指针
  let p2 = arr.length - 1; //右区域指针
  let i = 0; //遍历指针
  while (i <= p2 && p1 <= p2 && p1 <= i) {
    // 这个while循环条件也是要注意,当他们相等的情况下是有一个元素的,如果条件错误写出的结果也是错的
    if (arr[i] == target) {
      i++;
    } else if (arr[i] < target) {
      var temp = arr[i];
      arr[i++] = arr[p1];
      arr[p1++] = temp;
    } else if (arr[i] > target) {
      // 注意这个遍历指针不用++
      var temp = arr[i];
      arr[i] = arr[p2];
      arr[p2--] = temp;
    }
  }
}
flagColorQues(arr, 6);
console.log(arr);

leetcode.208-实现 Trie (前缀树)

前缀树(Prefix Tree),也叫字典树(Trie Tree),是一种树形数据结构,用于高效地存储和检索字符串集合。它的基本思想是将每个字符串拆分成一个个字符,然后将这些字符按照顺序插入到一棵树中。树的根节点表示空字符串,每个节点代表一个字符,从根节点到叶子节点的路径表示一个字符串。对于每个节点,它的子节点按照字符的顺序排列,从左到右依次代表该节点代表的字符串后面添加一个字符所得到的所有字符串。

字典树的主要优点是可以快速地查找某个字符串是否存在于字符串集合中,以及查找所有以某个前缀开头的字符串。这使得它在很多字符串处理问题中都有广泛的应用,例如字符串匹配、文本搜索、自动补全、拼写检查等。

题解:

插入操作: 拿到根节点,每个节点都有它的 chilDNodes 数组(当然,你也可以用哈希表存储) , 我们通过把传入的字符串拆分成一个个字符, 并且将他们的 ASCII 值与"a"的 ASCII 值相减得到下标 , 将当前节点的该 childNode 数组的该下标新建一个新的节点并且把他的 paseby 值++ , 在字符串完全遍历之后来到最后一个节点,把他的 ends 值++

搜索操作: 一直遍历字符串中的字符,如果遇到当前节点的后续节点为null的情况就直接返回false,这说明这个字符还没有添加进字典树 否则就一直往下走,走到最后一个节点,看他的ends值,它的 ends 值代表以当前字符结尾,也就是所求

startsWith 操作:(和搜索操作就返回值不相同) 仍然是拿到根节点,在这个字典树上遍历字符串,遇到当前节点的后续节点为null的情况直接返回false,否则就往下走,走到最后一个节点处返回他的paseby值

删除操作: 在字典树上从跟节点开始让他的paseby--,随后遍历字符串,每到一个节点,把他的paseby--,最后一个节点的ends--

class TrieNode {
  constructor() {
    this.paseby = 0;
    this.ends = 0;
    this.childNodes = new Array(26); //每个节点的下级节点
  }
}

var Trie = function () {
  this.root = new TrieNode();
  return null;
};

/**
 * @param {string} word
 * @return {void}
 */
Trie.prototype.insert = function (word) {
  if (word == null) return;
  let wordStr = word.split("");
  let node = this.root; //定义一个节点从根节点出发
  node.paseby++;
  for (let i = 0; i < wordStr.length; i++) {
    let index = wordStr[i].charCodeAt() - "a".charCodeAt(); //ASCII`值相减确认位置
    if (!node.childNodes[index]) {
      // 不存在就添加
      node.childNodes[index] = new TrieNode();
    }
    // 有就往下走
    node = node.childNodes[index]; //节点往下走
    node.paseby++;
  }
  node.ends++; //这整个字符串走完end++
  return null;
};

/**
 * @param {string} word
 * @return {boolean}
 */
// 判断一个字符串加入了几次
Trie.prototype.search = function (word) {
  if (word == null) return;
  let wordStr = word.split("");
  let node = this.root; //从根节点开始
  for (let i = 0; i < wordStr.length; i++) {
    let index = wordStr[i].charCodeAt() - "a".charCodeAt();
    if (!node.childNodes[index]) {
      // 如果不存在直接返回
      return false;
    } else {
      // 存在就往下走
      node = node.childNodes[index];
    }
  }
  return node.ends ? true : false; //此时的node在最后一个节点,它的ends同时也是这个单词出现的次数
};

/**
 * @param {string} prefix
 * @return {boolean}
 */
Trie.prototype.startsWith = function (prefix) {
  if (prefix == null) return;
  let wordStr = prefix.split("");
  let node = this.root; //从根节点开始
  for (let i = 0; i < wordStr.length; i++) {
    let index = wordStr[i].charCodeAt() - "a".charCodeAt();
    if (!node.childNodes[index]) {
      // 如果不存在直接返回
      return false;
    } else {
      // 存在就往下走
      node = node.childNodes[index];
    }
  }
  return node.paseby; //此时的node在最后一个节点,返回它的paseby
};

/**
 * @param {string} data
 * @return {boolean}
 */
// 沿途的paseby值减减,最后一个ends也值减减
Trie.prototype.delete = function (data) {
  if (this.search(data)) {
    // 先确定是否有这个字符串
    let node = this.root;
    let wordStr = data.split("");
    node.paseby--; //根节点先减减
    for (let i = 0; i < wordStr.length; i++) {
      let index = wordStr[i].charCodeAt() - "a".charCodeAt();
      if (--node.childNodes[index].paseby == 0) {
        node.childNodes[index] = null; //如果当前节点的paseby值减减过后为0,直接返回
        return;
      }
      node = node.childNodes[index]; //否则往下走
    }
    node.ends--; //最后一个节点ends减减
  } else {
    return; //不存在直接返回
  }
};
/**
 * Your Trie object will be instantiated and called as such:
 * var obj = new Trie()
 * obj.insert(word)
 * var param_2 = obj.search(word)
 * var param_3 = obj.startsWith(prefix)
 */
var obj = new Trie();

console.log(
  obj.insert("apple"),
  obj.search("apples"), //ends
  obj.startsWith("app"), //paseby
  obj.delete("apple")
);
console.log(obj.search("apple"));

给定一个数组 A(0,1...n-1),请构建一个数组 (0,1.. n-1),其中 的值是数组A 中除了下标以外的元素的和,然后再乘以5。

Example:
  input: [1456,162]
  output:[34533070345320340]
  将结果传递给下一步骤

题解:用到了reduce方法,值得注意的是这个方法有两个参数

第一个参数是一个回调函数,这个回调函数又能够接收四个参数(累加器、当前元素的值、当前元素索引、遍历的数组)

第二个参数可以这样理解,在函数第一次遍历的时候需要把上一次执行的结果与当前元素相加,那么第一次调用函数时,不存在上一次执行的结果,所以这个参数在不传递的情况下默认是数组的第一个值

function targetArr(arr) {
  let resArr = new Array(arr.length);
  for (let i = 0; i < resArr.length; i++) {
    resArr[i] =
      arr.reduce((accumulator, currentValue, current) => {
        if (current == i)
          return accumulator; //注意是返回累加器,如果是返回0那么累加器就为0了
        else return accumulator + currentValue;
      }, 0) * 5;
  }
  return resArr;
}

console.log(targetArr([1, 4, 56, 1, 6, 2]));

leetcode7.正数反转 32 位的有符号整数x ,返回将 x中的数字部分反转后的结果。如果反转后整数超过 32 位的有符号整数的范围 [−2**312**31 − 1] ,就返回 0。假设环境不允许存储 64 位整数(有符号或无符号)。

Example:
   input: x = 123
   output: 321
Example:
    input:x = -123
    output:-321
Example:
    input:x = 120
    output:21
Example:
    input:x = 0
    output:0

题解:首先我们先去绝对值,然后要利用数组的reverse方法把数组倒序,之后转换成字符串,再利用正则表达式去除,之后就没什么难度了。

/**
 * @param {number} x
 * @return {number}
*/
var reverse = function (x) {
  var xAbs = Math.abs(x); //获取数字绝对值
  let result = xAbs
    .toString()
    .split("")
    .reverse()
    .toString()
    .replace(/\,/g, "")
    .replace(/^0+/, "");//正则表达式
  // 判断是否加正负号
  if (result > 2 ** 31 - 1) {
    result = 0;
  }
  if (x < 0) {
    result = "-" + result;
  }
  return result;
};

最小和问题(归并思想,分而治之):一个无序的数组,把数组中每一项元素的左边比他小的数求和,得到整个数组的最小和

Example:
   input: arr = [1, 3, 4, 2, 5]
   output: 1+4+1+10 = 16

题解:暴力循环的话就没什么好说的了,这里是看的左神的解法,利用的是归并的思想,将求最小和问题转换成获取到右侧比左侧大的问题,就是比如[1, 3, 4, 2, 5]通过中间mid差分为[1,3,4]和[2,5],1比2小,那么就有2个1,3比2大,比5小就有一个3,当然需要无限拆分直到需要归并的开头和结尾相等返回0

let arr = [1, 3, 4, 2, 5];

// 求最小和
function toSum(arr, begin, end) {
  if (begin >= end) {
    return 0;
  }
  let mid = (end + begin) >> 1; //找中点,分治
  return (
    toSum(arr, begin, mid) +
    toSum(arr, mid + 1, end) +
    merge(arr, begin, end, mid)
  ); //合并

  function merge(arr, begin, end, mid) {
    let helpArr = new Array(end - begin + 1); //开辟额外的空间
    let l = begin; //左指针
    let r = mid + 1; //右指针
    let res = 0; //和
    let i = 0; //指针
    while (l <= mid && r <= end) {
      // 两个指针都没有越界
      res += arr[l] < arr[r] ? end - r + 1 * arr[l] : 0;
      helpArr[i++] = arr[l] < arr[r] ? arr[l++] : arr[r++];
    }
    while (l <= mid) {
      helpArr[i++] = arr[l++];
    }
    while (r <= end) {
      helpArr[i++] = arr[r++];
    }
    for (let i = 0; i < helpArr.length; i++) {
      arr[begin + i] = helpArr[i];
    }
    return res;
  }
}

console.log(toSum(arr, 0, arr.length - 1));

判断一个链表是否有环,如果有环就返回交点,否则返回null

题解

  • 方法一:Set 是否重复根据Set的特性,我们遍历这个链表,每次遍历节点就把这个节点放到>set中,然后继续遍历节点,如果这个链表没有环,那么set中就不会有重复,否则就直接返回这个重>复的节点
  • 方法二:快慢指针这个方法,怎么说你知道这个方法那就这样写就对了,你不知道就白瞎了(因为这>个你要问我怎么证明我也不知道,但是就是这么写的);
  • 定义两个指针,快指针p1慢指针p2,
  • 快指针一次走两步,慢指针一次走一步
  • 因为如果是一个有环的链表,那么快慢指针会在这个环上循环,并且一定会相遇,当二者相遇时,快指针回到头节点的位置,慢指针还在那个位置;(如果没有环,那么一定是快指针先遇到 null,直接返回 null 就 ok 了)
  • 随后快慢指针一起一次一步开始移动,当二者再次相遇时,相遇的节点就是所求的交点
function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}

var a = new ListNode(1);
var b = new ListNode(2);
var c = new ListNode(3);
var d = new ListNode(4);

a.next = b;
b.next = c;
c.next = d;

// 用set判断一个链表是否有环
// 判断一个链表是不是闭环
function isCircle(head) {
  // 一个单链表有环,那么我们遍历整个链表一定说死循环的,
  // 不存在node.next == null的情况;
  // 我们循环到一个节点,把他放入set,如果循环中遇到set有这个节点就说明有环
  // 能一直遍历到null就说明无环
  let set = new Set();
  let bool = blink(head);
  function blink(root) {
    if (root == null) return;
    if (set.has(root)) {
      return root;
    }
    set.add(root);
    return blink(root.next);
  }

  return bool ? true : false;
}

console.log("链表是否有环:", isCircle(a));

console.log("--------------------------------");

// 快慢指针法
// 判断一个链表是否有环,有环的话返回相交的节点,没有的话返回null
function getPublicNode(head) {
  if (head == null || head.next == null || head.next.next == null) return null;
  let p1 = head; //快指针
  let p2 = head; //慢指针
  // 先让他们各走一步,错开
  p1 = p1.next.next;
  p2 = p2.next;
  while (p1 != p2) {
    // 没有相遇继续走
    if (p1.next == null || p1.next.next == null) {
      return null; //快指针肯定先走完整个链表,如果有环不可能有null
    }
    p1 = p1.next.next;
    p2 = p2.next;
  }
  // 跳出循环,已经相遇
  p1 = head; //快指针回到头部
  // 随后快慢指针各走一步,二者交的点就是环的交点
  while (p1 != p2) {
    p1 = p1.next;
    p2 = p2.next;
  }
  // 跳出循环,二者又相遇,相遇即所求
  return p2;
}
console.log("相交节点:", getPublicNode(a));

leetcode.234-回文链表

Example:
   input:head = [1221]
   output: true
Example:
   input:head = [1,2]
   output: false

题解 : 遍历链表把每个节点的值放到一个栈中保存,然后我们再次遍历这个链表,每次遍历的同时出栈一个元素,出栈的元素和当前遍历的节点值相比较,不等返回false,反之为true。

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {boolean}
 */
var isPalindrome = function (head) {
  let stackValue = []; //一个栈
  getNodeVal(head); //节点全部入栈

  // 然后就是出栈然后比较pop返回出栈的数值
  let res = compare(head, stackValue);

  function compare(head, stack) {
    if (head == null || stack.length == 0) return;
    while (head) {
      let val = head.val;
      if (val != stack.pop()) {
        return false;
      } else {
        head = head.next;
      }
    }
    return true;
  }

  function getNodeVal(root) {
    if (root == null) {
      return;
    }
    stackValue.push(root.val);
    getNodeVal(root.next);
    return stackValue;
  }
     return res;
};

剑指 Offer 52. 两个链表的第一个公共节点

题解

分别遍历两个链表,如果这两个链表有交点, 那么遍历的最后,他们所保存的节点一定是相等的都是最后一个节;在遍历的过程中记录链表的长度,随后找到链表之间的差值,我们让长链表多走差值个节点,之后让两个节点同步开始移动,那么这两个节点相交的交点即为所求(有点困惑的,画个图体会体会,为什么先移动差值步?)

如果遍历的最后保存的值结果不等,说明他们没有交点

function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}

var a1 = new ListNode(1);
var a2 = new ListNode(1);

var b1 = new ListNode(2);
var b2 = new ListNode(2);
var b3 = new ListNode(2);

var c = new ListNode(3);
var d = new ListNode(4);

// 链表1
a1.next = a2;
a2.next = c;
c.next = d;

// 链表2
b1.next = b2;
b2.next = b3;

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
var getIntersectionNode = function (headA, headB) {
  if (headA == null || headB == null) return null;
  // 拿到两个链表的头
  let p1 = headA;
  let p2 = headB;
  // 思路是,先得到两个链表的长度,找到两个中长的那个链表
  // 随后长链表先走差值个节点,然后两个节点同步运动,二者相遇的就是所求交点
  let lengthDiff = 0;

  // 获取链表1长度
  while (p1.next != null) {
    lengthDiff++;
    p1 = p1.next; //更新判断
  }
  // 获取链表1、2之间长度的差值
  while (p2.next != null) {
    lengthDiff--;
    p2 = p2.next;
  }
  if (p2 != p1) {
    // 如果有交点,那么最后p1、p2保存的应该是尾节点
    return null;
  }
  p1 = lengthDiff > 0 ? headA : headB; //找到更长的链表
  p2 = p1 == headA ? headB : headA; //找到短的链表
  lengthDiff = Math.abs(lengthDiff);
  // 长链表移动差值个单位
  while (lengthDiff > 0) {
    lengthDiff--;
    p1 = p1.next;
  }

  // 随后两个链表的头节点一起移动
  while (p1 != null && p2 != null) {
    if (p1 == p2) {
      return p1;
    } else {
      p1 = p1.next;
      p2 = p2.next;
    }
  }
};

console.log(getIntersectionNode(a1, b1));

二叉树的层次遍历

题解: 利用队列先进先出的特性将二叉树从左到右入队列,

首先将根节点入队列,那么这个队列中目前只有一个根节点,遍历当前队列,先进先出,每次出队列时,把出队列的节点的值保存进一个Array类型的临时变量

判断这个节点有无孩子节点,先左孩子,后右孩子,如果有就加入队列

然后我们继续下次循环,while内对当前队列中的节点,循环遍历每次拿出时加入到Array类型的临时变量中,并且判断它的孩子节点,保存到下一次需要遍历的队列中;以此往复。


function TreeNode(val) {
  this.val = val;
  this.left = null;
  this.right = null;
}

let a1 = new TreeNode(3);
let b1 = new TreeNode(9);
let c1 = new TreeNode(20);
let d1 = new TreeNode(15);
let e1 = new TreeNode(7);

a1.left = b1;
a1.right = c1;
c1.left = d1;
c1.right = e1;

/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrder = function (root) {
  if (!root) return [];
  let res = [];
  let queue = [];
  queue.push(root); //头节点首先要放进去
  while (queue.length > 0) {
    let levelNode = []; //保存每一层的节点
    let length = queue.length; //好好想想为什么要在这里获取,我一开始就没有这样,调试一下
    for (var i = 0; i < length; i++) {
      let node = queue.shift(); //先进先出
      levelNode.push(node.val);
      if (node.left) {
        queue.push(node.left);
      }
      if (node.right) {
        queue.push(node.right);
      }
    }
    res.push(levelNode);
  }
  return res;
};

console.log(levelOrder(a1));

题解: 判断一颗树是不是完全二叉树 层序遍历这棵二叉树,每次出队列的时候弹出这一层的所有节点 不是完全二叉树的两种情况:

  1. 如果遇到了一个节点它的孩子节点不健全(没有同时拥有两个孩子节点,任何一个孩子不存在,或者说两个孩子都不存在),那么之后遇到的节点都应该是叶子节点,如果在这种情况下遇到了有孩子节点的节点,那么直接返回false

  2. 如果存在一个节点有右孩子但是没有左孩子,那么这棵树一定不是完全二叉树

leetcode.958-二叉树的完全性检查(判断一棵树是不是完全二叉树)

题解: 判断一颗树是不是完全二叉树 层序遍历这棵二叉树,每次出队列的时候弹出这一层的所有节点 不是完全二叉树的两种情况:

  1. 如果遇到了一个节点它的孩子节点不健全(没有同时拥有两个孩子节点,任何一个孩子不存在,或者说两个孩子都不存在),那么之后遇到的节点都应该是叶子节点,如果在这种情况下遇到了有孩子节点的节点,那么直接返回false

  2. 如果存在一个节点有右孩子但是没有左孩子,那么这棵树一定不是完全二叉树

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isCompleteTree = function (root) {
  if (root == null) return;
  // 层序遍历,肯定要用到队列
  let queue = [];
  let hasNoChildNode = false; //有没有遇到左右孩子不健全的节点,如果遇到了它的后面必须是叶子节点
  queue.push(root);
  while (queue.length > 0) {
    let length = queue.length;
    for (let i = 0; i < length; i++) {
      root = queue.shift(); //弹出队列

      if (
        // 左孩子为空,但是右孩子不为空
        (root.left == null && root.right != null) ||
        // 遇到了不健全的节点情况下,之后的节点仍然存在孩子节点
        (hasNoChildNode && (root.left || root.right))
      ) {
        return false;
      }
      if (root.left) {
        queue.push(root.left);
      }
      if (root.right) {
        queue.push(root.right);
      }
      // 这个判断条件不能放在上面最开始,好好思考
      if (root.left == null || root.right == null) {
        hasNoChildNode = true; //遇到了
      }
    }
  }
  return true;
};

console.log(isCompleteTree(a)); //false

leetcode.98-验证二叉搜索树

方法一:

利用数组,中序遍历二叉树,判断数组中的节点值是否升序排序

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
function isSearchTree(root) {
  let riseList = []; //就是判断这个节点组成的数组是否是升序排序
  getNode(root);

  for (let i = 0; i < riseList.length - 1; i++) {
    if (riseList[i] < riseList[i + 1]) {
      continue;
    } else {
      return false;
    }
  }
  return true;
  // 中序遍历获取树的节点
  function getNode(root) {
    if (root == null) return;
    getNode(root.left);
    riseList.push(root.val);
    getNode(root.right);
  }
}

方法二:

递归 , 判断节点大小

let preVlaue = Number.MIN_SAFE_INTEGER;
function isValidBST(root) {
  if (root == null) return true;
  // 判断当前节点的左子树是否是二叉搜索树
  let leftBool = isValidBST(root.left);
  if (!leftBool) {
    // 左子树不是直接返回
    return false;
  }
  // 左子树需要和当前节点比较
  if (root.value <= preVlaue) {
    // 这个遍历一定是下一个节点大于preValue
    return false; //当前节点的值不应该小
  } else {
    preVlaue = root.value; //用preVlaue记录遍历过的点
  }
  return isValidBST(root.right); //走到这里说明左子树是二叉搜索树,那么整棵树是不是就看右子树是不是
}

剑指 Offer 68 -二叉搜索树的最近公共祖先

题解: > 方法一

把这个整个二叉树,它的节点与其父节点形成一个映射关系Map关系 ( Node -> fatherNode )

随后,我们获取一个节点Node的父节点就可以Map.get(Node)更加简单的获取到当前节点的父节点,那么更新节点也是把当前节点变成它的父节点网上找,直到找到根节点;就这样我们把其中一个节点的祖先节点就全部找到了,用Set保存起来

利用节点 1 的祖先Set,我们遍历节点 2,同样的方式找他和他的父节点,看当前节点是否存在于节点 1 的Set

方法二:递归,看代码,没法讲

/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function (root, p, q) {
  // 用map记录每个节点以及他所对应的父节点
  // 然后找到p节点到根节点的这条链,随后看q节点网上能不能遇到p节点的链
  let fatherMap = new Map();
  //建立映射关系
  setFatherMap(root, fatherMap);
  let pNodeSet = new Set(); //存放p的祖先节点
  fatherMap.set(root, root); //根节点的祖先是他本身
  while (
    p != fatherMap.get(root)
    //不等于根节点就继续找
  ) {
    pNodeSet.add(p); //加入它的链条
    p = fatherMap.get(p); //更新成它的父节点
  }
  pNodeSet.add(root); //头节点没有加进去

  // 然后在q的链上找p的链
  while (q != null) {
    if (pNodeSet.has(q)) {
      return q; //存在直接返回它,不用找他的父节点
    } else {
      q = fatherMap.get(q);
    }
  }
  return null;

  function setFatherMap(root, map) {
    if (root == null) return;
    map.set(root.left, root);
    map.set(root.right, root);
    setFatherMap(root.left, map);
    d;
    setFatherMap(root.right, map);
  }
};

console.log(lowestCommonAncestor(a, d, h));
var lowestCommonAncestor = function (root, p, q) {
  if (root == null || root == p || root == q) {
    return root;
  }
  let left = lowestCommonAncestor(root.left, p, q);
  let right = lowestCommonAncestor(root.right, p, q);
  if (left != null && right != null) {
    return root;
  }
  return left != null ? left : right;
};

对整数数组把前4个元素入栈(如果不足 4个就把所有元素入栈),然后把1个元素出栈(如果不足1个就把所有元素出栈).将栈里剩余的元素,以栈顶为开始以栈底为结束作为一个数组返回。

Example:
  input: [12,3,4,5,6,7,8,910,11,12, 13,1415]
  output:[321]
  将结果传递给下一步骤

题解:就是栈的先进后出,最后一步考虑到这个特性就直接把数组颠倒了,这并不符合题目的期望。

function stackArr(arr) {
  let stack = [];
  for (let i = 0; i < 4; i++) {
    if (arr[i] == undefined) {
      break;
    }
    stack.push(arr[i]); //入栈
  }
  stack.pop(); //出栈
  return stack.reverse();
}
console.log(stackArr([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));

对一个整教数组,将其按照从小到大排序。设目标值为 32,在数组中找到目标值,并返回最小的索引。如果目标值不存在于数组,则返回它应该插入的位置。

Example:
   input: [4,3215]
   output:4

题解:在数组排序过后,再查找数组中元素应该插入的位置就简单了很多,找到比target大的值arr[i]直接返回它的索引,不用继续往下找了。反之还需要考察直到找到比target大的值,或者把整个数组遍历完。

function sortSearch(arr, target) {
  arr.sort((a, b) => a - b); //排序
  if (arr.includes(target)) {
    return arr.indexOf(target);
  } else if (arr.indexOf(target) == -1) {
    // 不存在
    let resIndex = 0;
    for (var i = 0; i < arr.length; i++) {
      if (target < arr[i]) {
        // 很直接的返回了
        return i;
      } else if (target > arr[i]) {
        // 还需要继续找
        resIndex = i + 1;
      }
    }
    return resIndex;
  }
console.log(sortSearch([4, 3, 2, 15], 32));

输入一个整数n(如果n<0,则n取0,如果n>1000,则n取1000),求1~5*n,这5*n个整数的十进制表示中1出现的次数。例如12的十进制表示中1出现的次数:1~12这些整数中包含1的数字有1、10、11和12,1一共出现了5次.

Example:
   input: 12
   output:16

题解:这里遍历每个数,然后把获取到的number转换成string再拆分成arr,在累加数组中1的个数

function getNumberOne(num) {
  if (num <= 0) {
    num = 0;
  } else if (num > 1000) {
    num = 1000;
  }
  let result = 0;
  for (let i = 0; i < 5 * num; i++) {
    var item = i.toString().split(""); //把每一项拆成数组
    for (let j = 0; j < item.length; j++) {
      if (item[j] == 1) {
        result++;
      }
    }
  }
  return result;
}

console.log(getNumberOne(12)); //16

对整数数组把前 8个元素入栈(如果不足 8个就把所有元素入栈),然后把 5个元素出栈(如果不足5个就把所有元素出栈),将栈里剩余的元素,以栈顶为开始以栈底为结束作为一个数组返回.

Example:
   input: [12,3,456,78,910,1112131415]
   output: [32,1]

题解:就是最基础的栈,入栈出栈,不得不说js实现操作是真的简单

function aboutsatck(arr) {
  let stackArr = [];
  let newArr = [];
  if (arr.length < 8) {
    // 数组元素小于8的情况
    for (let i = 0; i < arr.length; i++) {
      stackArr.push(arr[i]); //入栈
    }
    if (stackArr.length < 5) {
      // 当前栈不足五个,全部出栈
      for (var i = 0; i < stackArr.length; i++) {
        stackArr.pop();
      }
      return newArr;
    } else {
      // 当前栈数量大于5,但是小于8
      for (let n = 0; n < 5; n++) {
        stackArr.pop(); //出栈
      }
      for (let i = stackArr.length - 1; i >= 0; i--) {
        newArr.push(stackArr[i]);
      }
      return newArr;
    }
  } else {
    // 数组元素大于8的情况
    for (let j = 0; j < 8; j++) {
      stackArr.push(arr[j]); //入栈
    }
    for (let n = 0; n < 5; n++) {
      stackArr.pop(); //出栈
    }
  }
  for (let i = stackArr.length - 1; i >= 0; i--) {
    newArr.push(stackArr[i]);
  }
  return newArr;
}

console.log(aboutsatck([1, 2, 3, 4, 5, 6])); //1
console.log(aboutsatck([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])); //[ 3, 2, 1 ]
console.log(aboutsatck([1, 2, 3, 4, 5])); //[]

对一个整数数组,使得数组中的每个元素只出现 2次,将新数组从小到大排列后返回

Example:
   input: [1, 23]
   output: [112233]

题解:这里是先直接利用Set去除数组中的重复项,然后,就是两个相同数组合并再排序

function doubleNumberArr(arr) {
  let res = new Set(arr); //先确保每个元素出现一次
  return Array(...res)
    .concat(Array(...res))
    .sort((a, b) => a - b);
}
console.log(doubleNumberArr([1, 2, 3])); //[ 1, 1, 2, 2, 3, 3 ]

对一个整数数组,将数组中的元素替换成它们排序(队大到小排序) 后的率号,规定序号从 6 开始编号(包括 6)并依次递增(元素相等则序号相等),返回这个数组

Example:
   input: [4231]
   output: [6879]

题解:这个题目需要先将数组排序,然后从大到小排序后,又要根据数组元素找到它的排序,至于排序从6开始,这个可以忽略,就是要返回数组从大到小的排序的编号,因为相同的元素具有相同的编号,所以我选择先把数组去重,然后我们需要获取元素再排序过后的数组下标用map存储,那么我们就可以循环原数组,然后从map中取下标再加6即可。

function sortIndex(arr) {
  var sigle = new Set(arr);
  let sigleArr = new Array(...sigle);
  sigleArr.sort((a, b) => b - a);
  let obj = sigleArr.map((item, index) => [item, index]);
  let elementAndIndex = new Map(obj);
  let relust = [];
  arr.forEach((element) => relust.push(6 + elementAndIndex.get(element)));
  return relust;
}
console.log(sortIndex([4, 2, 4, 3, 1])); //[ 6, 8, 6, 7, 9 ]
console.log([4, 2, 3, 1]); //[ 6, 8, 7, 9 ]