LeetCode刷题记录-前端

299 阅读6分钟

1.剑指offer

第一题 :

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序, 每一列都按照从上到下递增的顺序排序。

请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

// 思想分析:从四个对角上面开始比较,这里用的是从左下角的值开始比较,
// target大于当前值,就向右移动j++;target小于当前值,就向上移动i--
arr = [
    [1, 2, 8, 9],
    [2, 4, 9, 12],
    [4, 7, 10, 13],
    [6, 8, 11, 15]
  ];
target = 5;

  function Find(target, arr) {
    // i 行;j 列            
    for (let i = arr.length - 1, j = 0; i >= 0 && j < arr[0].length;) {
      if (arr[i][j] == target) {
        return 'true'
      }
      if (arr[i][j] > target) {
        i--;
      } else {
        j++;
      }
    }
    return 'false'
  }
  console.log(this.Find(target, arr));

第二题:请实现一个函数,将一个字符串中的每个空格替换成“%20”。

例如,当字符串为We Are Happy. 则经过替换之后的字符串为We%20Are%20Happy。

 str = "he llo  world";
// 方法一:split、join  在js中字符串自带函数     
  function replaceSpace(str) {
    let arr = str.split(' ');
    return arr.join("%20")
  } 
// 方法二:堆栈     新建一个数组,遇到空的时候压替换值入栈   
  function replaceSpace(str) {
    let result = [];
    for (let i = 0; i < str.length; i++) {
      if (str[i] == " ") {
        result.push("%20");
      } else {
        result.push(str[i]);
      }
    }
    return result.join('').toString();
  }
  console.log(replaceSpace(str));

链表

第三题:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

// 注解:这里要注意,这里所说的从尾到头,只需要存链表的值;而不是翻转链表

//这是定义一个链表的方法
function ListNode(x) {
    this.val = x;
    this.next = null;
}
//思想:将链表的值unshift从头插入另一个数组里面
function printListFromTailToHead(head) {
    var ArrayList = [],
      pNode = head;
    while (pNode != null) {
      ArrayList.unshift(pNode.val);
      pNode = pNode.next;
    }
    return ArrayList;
  }

2.LeetCode

 第19题:给定一个链表,删除链表的倒数第 n个节点,并且返回链表的头结点。

示例: 给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:给定的 n 保证是有效的。

 //Definition for singly - linked list.
  function ListNode(val) {
    this.val = val;
    this.next = null;
  }

var removeNthFromEnd = function (head, n) {
    var fake = new ListNode(0);
    fake.next = head;
    var length = 0;
    var current = head;
    //得到长度
    while (current != null) {
      length++;
      current = current.next;
    }
    // length-n后正好时要删除的那个            
    length -= n;
    var current = fake;
    // length-n后正好时要删除的那个的前一个           
    while (length > 0) {
      length--;
      current = current.next
    }
    current.next = current.next.next;
    return fake.next
};

力扣 206反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

 //Definition for singly - linked list.
  function ListNode(val) {
    this.val = val;
    this.next = null;
  }

1.自己写的算法:效率比较低15%,108ms/35.6MB

思想是先用数组存储反向链表的值,然后再从链表头到尾重新赋值

  var reverseList = function (head) {
    let pNode = head;
    let arr = [];
    while (pNode != null) {
      arr.unshift(pNode.val); //[5,4,3,2,1]               
      pNode = pNode.next;
    }
    let first = head;
    for (let i = 0; i < arr.length; i++) {
      head.val = arr[i];
      head = head.next;
    }
    head = null;
    return first
  };

2.官方方法一:迭代 86.3% 80ms/34.7MB

假设存在链表 1 → 2 → 3 → Ø,我们想要把它改成 Ø ← 1 ← 2 ← 3。

在遍历列表时,将当前节点的 next 指针改为指向前一个元素。

由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。

在更改引用之前,还需要另一个指针来存储下一个节点。

不要忘记在最后返回新的头引用!

var reverseList = function (head) {
    let prev = null;
    let current = head;
    while (current != null) {
      let next = current.next;
      current.next = prev;
      prev = current;
      current = next;
    }
    return prev
  }

3.官方方法二:递归

递归版本稍微复杂一些,其关键在于反向工作。

假设列表的其余部分已经被反转,现在我该如何反转它前面的部分?

假设列表为: n1->… -> n{k-1} -> n{k} -> n{k+1} -> … -> n{m} -> Ø

若从节点 n{k+1}到 n{m}已经被反转,而我们正处于 n{k}。

n{1}-> … -> n{k-1} -> n{k} -> n{k+1} <-… <-n{m}

我们希望 n{k+1} 的下一个节点指向 n{k} 。

所以,n{k}.next.next = n{k}。

要小心的是 n{1}的下一个必须指向 Ø 。

如果你忽略了这一点,你的链表中可能会产生循环。

如果使用大小为 2 的链表测试代码,则可能会捕获此错误。

 var reverseList = function (head) {
    if (head == null || head.next == null) return head;
    var p = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return p;
  }

力扣21题 将两个有序链表合并为一个新的有序链表并返回。

新链表是通过拼接给定的两个链表的所有节点组成的。

示例:输入:1->2->4, 1->3->4输出:1->1->2->3->4->4

1.自己写的 136ms/35.4MB 10% 也是官方的迭代方法

 var mergeTwoLists = function (l1, l2) {
    // 得到head            
    var head = new ListNode(-1);
    var first = head;
    while (l1 != null && l2 != null) {
      if (l1.val <= l2.val) {
        first.next = l1;
        l1 = l1.next;
      } else {
        first.next = l2;
        l2 = l2.next;
      }
      first = first.next;
    }
    first.next = l1 == null ? l2 : l1;
    return head.next
  };

2.方法:递归

想法:我们可以如下递归地定义在两个链表里的 merge 操作(忽略边界情况,比如空链表等):

{ list1[0] + merge(list1[1:], list2) & list1[0] < list2[0]

{ list2[0] + merge(list1, list2[1:]) & otherwise

也就是说,两个链表头部较小的一个与剩下元素的 merge 操作结果合并。

算法:我们直接将以上递归过程建模,首先考虑边界情况。

特殊的,如果 l1 或者 l2 一开始就是 null ,那么没有任何操作需要合并,所以我们只需要返回非空链表。

否则,我们要判断 l1 和 l2 哪一个的头元素更小,然后递归地决定下一个添加到结果里的值。

如果两个链表都是空的,那么过程终止,所以递归过程最终一定会终止。

var mergeTwoLists = function (l1, l2) {
    if (l1 == null) {
      return l2;
    } else if (l2 == null) {
      return l1;
    } else if (l1.val < l2.val) {
      l1.next = mergeTwoLists(l1.next, l2);
      return l1;
    } else {
      l2.next = mergeTwoLists(l1, l2.next);
      return l2;
    }
  }

笔试题:连字符字符串与驼峰字符串的相互转换

连字符转驼峰

第一种方法:使用正则匹配函数

let str = 'str-arr-test';
str = str.replace(/(\w*)-(\w*)/g, function ($1, $2, $3) {
    //console.log($2, $3);
    return $2 + $3[0].toUpperCase() + $3.slice(1);
});
console.log(str);//strArrTest

第二种方法:使用 js 中 split 和 reduce 方法

let str = 'str-arr-test';
str = str.split('-').reduce((acc, val, idx) => idx === 0 ?
    acc + val : acc + val[0].toLocaleUpperCase() + val.slice(1), '');
console.log(str);//strArrTest

驼峰转连字符

第一种方法:正则

let str = 'strArrTest';
str = str.replace(/([A-Z])/g, function ($1) {
    return '-' + $1.toLocaleLowerCase();
});
console.log(str);//str-arr-test

第二种方法: 使用正则非捕获组来切割数组,然后使用 reduce 遍历所有元素

let str = 'strArrTest';
str = str.split(/(?=[A-Z])/g).reduce((acc, val, idx) => idx === 0 ?
    acc + '-' + val : acc + '-' + val[0].toLocaleLowerCase() + val.slice(1));
console.log(str);//str-arr-test