发布于 2026-01-06 1 阅读
0

24小时编程面试准备挑战赛第一天(2020年1月4日)

24 小时编程面试准备挑战

第一天(2020年1月4日)

我不太相信用60分钟的编程挑战来衡量开发者的技能。大多数情况下,这些问题的设计都使得如果你掌握了算法,15分钟就能解决。我记不清自己何时在如此短的时间内解决过任何棘手的实际问题。任何复杂的问题都需要时间来解决。

然而,我周一,也就是1月6日,有两场编程面试。所以,与其抱怨面试过程,我决定从明天,也就是2020年1月4日早上8点开始,一直到1月5日晚上8点,连续24小时学习数据结构和算法。

我会定期更新我的进度,发布笔记和代码。以下是我计划探讨的主题:

  • 数组和字符串
  • 链表
  • 栈和队列
  • 树木
  • 图表
  • 位操作
  • 递归
  • 动态规划
  • 搜索和排序

我计划阅读Gayle Laakmann McDowell 的《破解编程面试》和Michael McMillan 的《JavaScript 数据结构与算法:将经典计算方法引入 Web》 。

我会阅读其他博客文章,尽可能多地解决编程问题,并与开发者社区分享。

请分享一下你所知道的好的面试准备资源。我希望这篇文章和讨论能成为一个非常有用的参考平台,帮助那些时间紧迫、准备编程面试的人。

第一天(2020年1月4日)

替代文字

我早上 10 点开始了 24 小时编程面试准备挑战。一杯香浓的咖啡已经准备好,我即将攻克编程面试难题。以下是我今天要学习的主题。

  • 数组
  • 细绳
  • 链表
  • 堆栈
  • 队列
  • 树木
  • 图表
  • 搜索

我会认真学习这些主题,记录所有有用的资源,解决每个主题下 5 到 10 道题,并每两小时向社区汇报一次进度。我相信社区可以成为我很好的监督伙伴。

请分享一下您关于如何快速掌握数据结构的最佳想法。非常感谢您的任何建议。

下次更新时间为中午12点。

下午 12:00 更新

  • 我读了这本书的第一章Cracking the Coding Interview
  • 以下是我解决过的编程问题:

编码

数组和字符串

1. 唯一性判断:实现一个算法来判断一个字符串是否包含所有唯一字符。如果不能使用其他数据结构该怎么办?

function uniqueCharacters(str) {
  const ASCII_CHARS = 256;

  if(str.length > ASCII_CHARS) {
    return false;
  }

  const chars = Array(ASCII_CHARS).fill(false);

  for(let char of str){
    const index = char.charCodeAt(0);
    if (chars[index] === true) {
      return false;
    }
    chars[index] = true;
  }

  return true;
}

console.log(uniqueCharacters('abcd10jk')); // true
console.log(uniqueCharacters('hutg9mnd!nk9')); // false

2. 检查排列:给定两个字符串,编写一个方法来判断其中一个字符串是否是另一个字符串的排列。

function isPermutation(str1, str2) {
  if (str1.length !== str2.length) {
    return false;
  }

  const map = new Map();

  for(const char of str1) {
    if(map.has(char)) {
      map.set(char, map.get(char) + 1);
    } else {
      map.set(char, 1);
    }
  }

  for(const char of str2) {
      if(map.has(char)) {
        const value = map.get(char);
        if(value === 0) {
          return false;
        } else {
          map.set(char, map.get(char) - 1);
        }
      }
  }

  for(const value of map.values()) {
    if(value !== 0) {
      return false;
    }
  }
  return true;

}

console.log(isPermutation("god", "dog")); // true

3. URLify:编写一个方法,将字符串中的所有空格替换为'%20'。

function replaceSpaces(str, trueLength) {
  let output = "";
  let chars = 0;

  for(let char of str) {
    if(char !== " ") {
      chars++;
    }
  }

  let spaces = trueLength - chars;

  for(let char of str) {
    if(char === " " && spaces > 0) {
      output += "%20";
      spaces--;
    } else if(char !== " ") {
      output += char;
    }
  }

  while(spaces > 0) {
    output += "%20";
    spaces--;
  }

  return output;
}

console.log(replaceSpaces("Mr 3ohn Smith", 13)); // "Mr%203ohn%20Smith"

3. 回文排列:给定一个字符串,编写一个函数来检查它是否是某个回文的排列。

function isPermutationOfPalindrome(str) {
  let charCount = 0;
  let map = new Map();

  for(let char of str) {
    if (char === " ") {
      continue;
    }

    if(map.has(char)) {
      map.delete(char);
    } else {
      map.set(char, true);
    }

    charCount++;
  }

  if(charCount % 2 === 0) {
    return map.size === 0;
  } else {
    return map.size === 1;
  }
}

console.log(
  isPermutationOfPalindrome("tacoa cat") === true,
  isPermutationOfPalindrome("atco cat") === true,
  isPermutationOfPalindrome(" rac ecar rara ") === true
);

4. 字符串压缩:实现一个使用重复字符计数执行基本字符串压缩的方法。

function compress (str) {
  const map = new Map();
  let result = '';

  for(const char of str) {
    if(map.has(char)) {
      map.set(char, map.get(char) + 1);
    } else {
      map.set(char, 1);
    }
  }

  for(let [key, value] of map) {
    result += key + value;
  }

  return str.lenght >= result.lenght ? str : result;
}

console.log(compress('aabcccccaaa')); // "a5b1c5"

5. 旋转矩阵:给定一个用 NxN 矩阵表示的图像,其中图像中的每个像素为 4 字节,编写一个方法将图像旋转 90 度。

function rotateMatrix(arr) {
    let n = arr.length - 1;

    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n - i; j++) {
            let temp = arr[i][j];

            arr[i][j] = arr[n - j][n - i]; // top row
            arr[n - j][n - i] = temp; // right column
        }
    }

    return arr;
}


console.log(rotateMatrix([
  [1,2,3],
  [4,5,6],
  [7,8,9]
])); 

/* 
[
    [9, 6, 3], 
    [8, 5, 2], 
    [7, 4, 1]
] 
*/

6. 字符串旋转;假设你有一个 isSubstring 方法,用于检查一个单词是否是另一个单词的子字符串。给定两个字符串 s1 和 s2,编写代码,仅使用一次 isSubstring 调用来检查 s2 是否是 s1 的旋转(例如,“waterbottle” 是“P'erbottlewat” 的旋转)。

const isSubstring = (str1, str2) => str1.includes(str2);

function isRotation(str1, str2) {
  if(!str1 || !str2) {
    return false;
  }

  if(str1.length !== str2.length) {
    return false;
  }

  return isSubstring(str1 + str2, str2);
}

console.log(isRotation("waterbottle", "erbottlewat")); // true
console.log(isRotation("aacdf", "acda")); // false

7. 查找给定字符串或数组中的第一个唯一字符

function findFirstUniqueCharacter(str) {
  if(!str) return;

  const map = new Map();

  for(let char of str) {
      if(map.has(char)) {
        map.set(char, map.get(char) + 1);
      } else {
        map.set(char, 1);
      }
  }

  for(let [key, value] of map) {
    if(value === 1) {
      return key;
    }
  }
  return "None";
}

console.log(findFirstUniqueCharacter('foobar')); // f
console.log(findFirstUniqueCharacter('aabbccdef')); // d
console.log(findFirstUniqueCharacter('aabbcc')); // 'No Unique Character Found'

下午3:30更新

链表

链表Node

class Node {
  constructor(value, next = null) {
    this.value = value;
    this.next = next;
  }
}

链表print函数

function print(node) {
  while(node !== null) {
      console.log('->' + node.value);
      node = node.next;
  }
}

1. 删除重复项:编写代码从无序链表中删除重复项。

function removeDups(node) {
  if(node === null) return null;

  let nodes = new Map();
  let current = node.next;
  let prev = node;
  nodes.set(node.value, 1);

  while(current !== null) {
    if(nodes.get(current.value) === 1) {
      prev.next = current.next;
    } else {
      nodes.set(current.value, 1);
      prev = current;
    }
    current = current.next;
  }
  return node;
}

let node1 = new Node(1);
let node2 = new Node(2);
let node3 = new Node(3);
let node4 = new Node(2);
let node5 = new Node(1);

node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = null;

print(removeDups(node1));

/* 
"->1"
"->2"
"->3"
*/

2. 返回第 K 个元素到最后一个元素:实现一个算法来查找单链表的第 k 个元素到最后一个元素。

function KthToLast(root, n) {
  if(!root) return null;
  let current = root;
  let follower = root;

  for(let i = 0; i < n; i++) {
    current = current.next;
  }

  while(current.next !== null) {
    current = current.next;
    follower = follower.next;
  }

  return follower;
}


let node1 = new Node(1);
let node2 = new Node(2);
let node3 = new Node(3);
let node4 = new Node(4);
let node5 = new Node(5);

node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = null;

console.log(KthToLast(node1, 2).value);  // 3

3. 删除链表中间部分

function removeDups(head) {
  if(head === null) return null;

  let fast = head;
  let slow = head;
  let prev = null;

  while(fast !== null && fast.next !== null) {
    fast = fast.next.next;
    prev = slow;
    slow = slow.next;
  }

  prev.next = slow.next;

  return head;
}

let nodeA = new Node('a');
let nodeB = new Node('b');
let nodeC = new Node('c');
let nodeD = new Node('d');
let nodeE = new Node('e');
let nodeF = new Node('f');

nodeA.next = nodeB;
nodeB.next = nodeC;
nodeC.next = nodeD;
nodeD.next = nodeE;
nodeE.next = nodeF;
nodeF.next = null;

print(removeDups(nodeA)); 
/*
"->a"
"->b"
"->c"
"->e"
"->f"
*/

4. 根据给定值对链表进行分区,如果我们不关心链表元素的构成,stable

function partition(head, x) {
  let tail = head;
  let current = head;

  while(current !== null) {
    let next = current.next;
    if(current.value < x) {
      current.next = head;
      head = current;
    } else {
      tail.next = current;
      tail = current;
    }
    current = next;
  }
  tail.next = null;

  return head;
}

let nodeA = new Node(3);
let nodeB = new Node(5);
let nodeC = new Node(8);
let nodeD = new Node(2);
let nodeE = new Node(10);
let nodeF = new Node(2);
let nodeH = new Node(1);

nodeA.next = nodeB;
nodeB.next = nodeC;
nodeC.next = nodeD;
nodeD.next = nodeE;
nodeE.next = nodeF;
nodeF.next = nodeH;
nodeH.next = null;

print(partition(nodeA, 5));
/*
"->1"
"->2"
"->2"
"->3"
"->5"
"->8"
"->10"
*/

5. 给定一个链表,其中每个节点有两个指针,一个指向下一个节点,一个指向链表中的随机节点,克隆该链表。

function clone(node) {
  if(node === null) return node;

  let map = new Map();
  let copy = new Node(node.value);
  let current = node;
  let newHead = copy;

  map.set(current, copy);

  current = current.next;
  while(current !== null) {
    let temp = new Node(current.value);
    map.set(current, temp);
    copy.next = temp;
    copy = temp;
    current = current.next;
  }

  current = node;
  copy = newHead;

  while(current !== null) {
    if(current.randome !== null) {
      copy.random = map.get(current.random);
    } else {
      copy.random = null;
    }

    current = current.next;
    copy = copy.next;
  }

  return newHead;
}

6. 给定一个链表,判断它是否包含环。

function hasCycle(node) {
  if(node === null) return false;
  let slow = node;
  let fast = node.next;

  while(fast !== null && fast.next !== null) {
    if(fast === slow) return true;
    slow = slow.next;
    fast = fast.next.next;
  }

  return false;
}

let node1 = new Node(1);
let node2 = new Node(2);
let node3 = new Node(3);
let node4 = new Node(4);
let node5 = new Node(5);

node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node3;

console.log(hasCycle(node1)); // true

7. 如何一次性找到链表的中间元素?

function findMiddle(node) {
    if(node === null) return null;
    let fast = node.next;
    let slow = node;

    while(fast !== null && fast.next !== null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    console.log(slow.value);
}

let node1 = new Node(5);
let node2 = new Node(6);
let node3 = new Node(7);
let node4 = new Node(1);
let node5 = new Node(2);

node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = null;

findMiddle(node1); // 7

8. 将两个数相加

// Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
// Output: 7 -> 0 -> 8
// Explanation: 342 + 465 = 807.

const addTwoNumbers = function(l1, l2) {
  let head = new Node(0);
  let carry = 0;
  let current = head;

  while(l1 !== null || l2 !== null ) {
    const x = l1 !== null ? l1.value : 0;
    const y = l2 !== null ? l2.value : 0;

    const sum = carry + x + y;
    carry = Math.floor(sum / 10);

    current.next = new Node(sum % 10);
    current = current.next;

    if(l1 != null) l1 = l1.next;
    if(l2 != null) l2 = l2.next;
  }

  if (carry > 0) {
    current.next = new ListNode(carry);
  }
  return head.next;
};

const node13 = new Node(3);
const node12 = new Node(4);
const node11 = new Node(2);
node11.next = node12;
node12.next = node13;
const l1 = node11; 

const node23 = new Node(4);
const node22 = new Node(6);
const node21 = new Node(5);
node22.next = node23;
node21.next = node22;
const l2 = node21;

print(addTwoNumbers(l1, l2));
/*
"->7"
"->0"
"->8"
*/

晚上7:45更新

堆栈

1. 给定一个栈,使用不超过一个额外的栈对栈中的元素进行排序。

Array.prototype.peek = function() {
  return this[this.length -1];
}

Array.prototype.isEmpty = function() {
  return this.length <= 0;
}

function sortStack(stack) {
  if(!stack || stack.length ===0) return stack;

  let newStack = [];
  newStack.push(stack.pop());

  while(!stack.isEmpty()) {
    const temp = stack.pop();
    while(!newStack.isEmpty() && temp > newStack.peek()) {
      stack.push(newStack.pop());
    }
    newStack.push(temp);
  }
  return newStack;
}

console.log(sortStack([5, 1, 3, 2, 4])); // [5, 4, 3, 2, 1]

2. 评估 Javascript 中的逆波兰表示法

function reversePolishNotation(seq) {
  if(seq.length <= 2) {
    return;
  }

  const operands = ['+', '-', '*', '/'];
  const stack = [];
  let i = 0;

  stack.push(seq[i]);
  i++;

  while(i <= seq.length) {
    let item = seq[i];
    let index = operands.indexOf(item);

    if(index < 0) {
      stack.push(item);
    } else {
      const a = parseInt(stack.pop(), 10);
      const b = parseInt(stack.pop(), 10);

      if(index === 0) {
        stack.push(a + b);
      } 
      if(index === 1) {
        stack.push(a - b);
      }
      if(index === 2) {
        stack.push(a * b);
      }
      if(index === 3) {
        stack.push(b/a);
      }
    }
    i++;
  }

  return parseInt(stack[0], 0);
}

console.log(reversePolishNotation(["2", "1", "+", "3", "*"])) // 9
console.log(reversePolishNotation(["4", "13", "5", "/", "+"])) // 6
console.log(reversePolishNotation(["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"])) // 22
console.log(reversePolishNotation(["2", "1"])) // undefined

3. 使用 JavaScript 实现栈

function Stack() {
  this.top = null;
}

Stack.prototype.push = function (val) {
  let node = {
    data : val,
    next : null
  };

  node.next = this.top;
  this.top = node;
};

Stack.prototype.pop = function () {
  if(this.top === null) {
    return null;
  } else {
    const top = this.top;
    this.top = this.top.next;
    return top.data;
  }
};

Stack.prototype.peek = function() {
  if(this.top === null) {
    return null;
  } else {
    return this.top.data;
  }
}

var S1 = new Stack();
S1.push(5);
S1.push(6);
S1.push(7);
S1.push(8);

console.log(S1.pop()); // 8
console.log(S1.pop()); // 7

4. 使用两个栈的队列(JavaScript)

function Queue() {
  this.pushStack = new Stack();
  this.popStack = new Stack();
}

Queue.prototype.enqueue = function(value) {
  this.pushStack.push(value);
}

Queue.prototype.dequeue = function() {
  let popStack = this.popStack;
  let pushStack = this.pushStack;

  if(popStack.peek()) {
    const deq = popStack.pop();
    return deq;
  }

  while(pushStack.peek()) {
    popStack.push(pushStack.pop());
  }
}

const q1 = new Queue();
q1.enqueue(3);
q1.enqueue(4);
q1.enqueue(5);
q1.enqueue(6);
q1.enqueue(7);
q1.dequeue();
q1.dequeue();
q1.dequeue();

console.log(q1);

5.

function stepsToSolveTowerOfHanoi(height, from, to, via) {
  if (height >= 1) {
    // Move a tower of height - 1 to buffer peg using destination peg
    stepsToSolveTowerOfHanoi(height - 1, from, via, to);
    // Move the remaining disk to the destination peg.
    console.log(`Move disk ${height} from Tower ${from} to Tower ${to}`);
    // Move the tower of `height-1` from the `buffer peg` to the `destination peg` using the `source peg`.        
    stepsToSolveTowerOfHanoi(height - 1, via, to, from);
  }

  return;
}

stepsToSolveTowerOfHanoi(3, "A", "C", "B");
/*
"Move disk 1 from Tower A to Tower C"
"Move disk 2 from Tower A to Tower B"
"Move disk 1 from Tower C to Tower B"
"Move disk 3 from Tower A to Tower C"
"Move disk 1 from Tower B to Tower A"
"Move disk 2 from Tower B to Tower C"
"Move disk 1 from Tower A to Tower C"
*/

6. 判断一个字符串是否由有效的括号组成。

function isMatchingBrackets(str) {
  if(str.length <= 1) {
    return false;
  }

  let stack = [];
  const map = {
    '{': '}',
    '[': ']',
    '(': ')'
  };

  for(let char of str) {
    if(char === '(' || char === '[' || char === '{') {
      stack.push(char);
    } else {
      const last = stack.pop();
      if (char !== map[last]) {
        return false;
      }
    }
  }
  return stack.length === 0;
}

console.log(isMatchingBrackets("(){}")); // true
console.log(isMatchingBrackets("[{()()}({[]})]({}[({})])((((((()[])){}))[]{{{({({({{{{{{}}}}}})})})}}}))[][][]")); // true
console.log(isMatchingBrackets("({(()))}}"));  // false
文章来源:https://dev.to/zahidulislam/24-hour-coding-interview-prep-challenge-29ad