https://xin-tan.com/passages/2019-06-23-algorithm-offer/

二维数组里的查询

https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/

  1. /**
  2. * @param {number[][]} matrix
  3. * @param {number} target
  4. * @return {boolean}
  5. */
  6. var findNumberIn2DArray = function(matrix, target) {
  7. if(matrix === null || matrix.length < 1 || matrix[0].length < 1) {
  8. return false
  9. }
  10. //起点在右上角
  11. let row = 0, col = matrix[0].length-1
  12. //判断当前数组元素和target,如果当前大于target,往左走;小与target,往下走
  13. while (row < matrix.length && col >= 0) {
  14. if (matrix[row][col] === target) {
  15. return true
  16. } else if (matrix[row][col] > target) {
  17. col -= 1
  18. } else if (matrix[row][col] < target) {
  19. row += 1
  20. }
  21. }
  22. //走出边界了还没找到,说明不存在,返回false
  23. return false
  24. };

逆序打印链表

考察点:用栈

  1. var reversePrint = function(head) {
  2. let arr = []
  3. while(head) {
  4. arr.unshift(head.val)
  5. head = head.next
  6. }
  7. return arr
  8. };

翻转链表

1-2-3-4-null
pre -> null current->head next
剑指offer - 图1
注意移动pre 和 current
退出条件是current != null
退出时current === null
所以应该返回prev, prev才是链表头结点

  1. var reverseList = function(head) {
  2. let prev = null, current = head, next = head
  3. while(current) {
  4. next = current.next
  5. current.next = prev
  6. prev = current
  7. current = next
  8. }
  9. return prev
  10. };

复制链表

  1. /**
  2. * // Definition for a Node.
  3. * function Node(val, next, random) {
  4. * this.val = val;
  5. * this.next = next;
  6. * this.random = random;
  7. * };
  8. */
  9. /**
  10. * @param {Node} head
  11. * @return {Node}
  12. */
  13. var copyRandomList = function(head) {
  14. // 判断如果是空链表
  15. if (!head) return null
  16. let node = new Node(),temp = node,map = new Map()
  17. let curr = head
  18. // 第一次循环,赋值和存map
  19. while (curr) {
  20. temp.val = curr.val
  21. temp.next = curr.next ? new Node() : null
  22. map.set(curr, temp)
  23. curr = curr.next
  24. temp = temp.next
  25. }
  26. // 临时节点重新指向头节点
  27. curr = head
  28. temp = node
  29. // 走第二次,处理random
  30. while (curr) {
  31. temp.random = map.get(curr.random)
  32. temp = temp.next
  33. curr = curr.next
  34. }
  35. return node
  36. };

需要注意的是:中间需要回到头结点,所以在最开始创建temp时还用了另外一个变量node,同时保存地址
找random里这一句是灵魂

  1. temp.random = map.get(curr.random)

备注 2020年6月27日递归(1)_00.png备注 2020年6月27日递归(1)_01.png

判断一个链表是否有环

需要注意的是 slow 和 head的起始点不一样

  1. var hasCycle = function(head) {
  2. let slow = head.next
  3. let fast = head
  4. while(fast !== null && fast.next !== null ) {
  5. if (fast.val === slow.val) {
  6. return true
  7. }
  8. fast = fast.next.next
  9. slow = slow.next
  10. }
  11. return false
  12. };

也可以用set

  1. var hasCycle = function(head) {
  2. let nodeSet = new Set()
  3. while(head){
  4. if (nodeSet.has(head)) {
  5. return true
  6. }
  7. nodeSet.add(head)
  8. head = head.next
  9. }
  10. return false
  11. };

删除链表里的全部重复元素

1-1-2-2-3-4 得到3-4
思路:
参考题解:
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/solution/san-chong-jie-fa-duo-tu-zhan-shi-82-shan-chu-pai-x/
这里我们使用双指针的方式,定义a,b两个指针。
考虑到一些边界条件,比如1->1->1->2这种情况,需要把开头的几个1给去掉,我们增加一个哑结点,方便边界处理。

初始的两个指针如下:

  • 将a指针指向哑结点
  • 将b指针指向head(哑结点的下一个节点)

如果a指向的值不等于b指向的值,则两个指针都前进一位
否则,就单独移动b,b不断往前走,直到a指向的值不等于b指向的值。

注意,这里不是直接比较a.val==b.val,这么比较不对,因为初始的时候,a指向的是哑结点,所以比较逻辑应该是这样:

  1. a.next.val === b.next.val

当两个指针指向的值相等时,b不断往前移动,这里是通过一个while循环判断的,因为要过滤掉1->2->2->2->3重复的2。
那么整个逻辑就是两个while,但时间复杂度不是O(N^2),而是O(N),空间上也只是常数级别。

image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

  1. var deleteDuplicates = function(head) {
  2. if(head === null || head.next === null) {
  3. return head
  4. }
  5. let dummyNode = new ListNode(0)
  6. dummyNode.next = head
  7. let a = dummyNode, b = head
  8. while(a.next!== null && b.next!== null) {
  9. if (a.next.val !== b.next.val) {
  10. a = a.next
  11. b = b.next
  12. } else {
  13. while(b.next !== null) {
  14. if (a.next.val !== b.next.val) {
  15. break;
  16. }
  17. b = b.next
  18. }
  19. a.next = b.next
  20. b = b.next
  21. }
  22. }
  23. return dummyNode.next
  24. };

相交链表

https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/tu-jie-xiang-jiao-lian-biao-by-user7208t/

  1. var getIntersectionNode = function(headA, headB) {
  2. if (headA === null || headB === null) {
  3. return null
  4. }
  5. let pA = headA, pB = headB
  6. while(pA !== pB) {
  7. pA = pA===null? headB : pA.next
  8. pB = pB===null? headA:pB.next
  9. }
  10. return pA
  11. };

合并两个排序的链表

这道题可以使用递归实现,新链表也不需要构造新节点,我们下面列举递归三个要素
终止条件:两条链表分别名为 l1 和 l2,当 l1 为空或 l2 为空时结束
返回值:每一层调用都返回排序好的链表头
本级递归内容:如果 l1 的 val 值更小,则将 l1.next 与排序好的链表头相接,l2 同理
O(m+n)O(m+n),mm 为 l1的长度,nn 为 l2 的长度

https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/hua-jie-suan-fa-21-he-bing-liang-ge-you-xu-lian-bi/

  1. var mergeTwoLists = function(l1, l2) {
  2. if(l1 === null){
  3. return l2;
  4. }
  5. if(l2 === null){
  6. return l1;
  7. }
  8. if(l1.val < l2.val){
  9. l1.next = mergeTwoLists(l1.next, l2);
  10. return l1;
  11. }else{
  12. l2.next = mergeTwoLists(l1, l2.next);
  13. return l2;
  14. }
  15. };

image.png
image.png
image.png
image.png
image.png

https://leetcode-cn.com/problems/copy-list-with-random-pointer/

https://github.com/ConardLi/awesome-coding-js/blob/master/%E5%89%91%E6%8C%87offer/1.%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84%E6%9F%A5%E6%89%BE.md

http://www.conardli.top/docs/dataStructure/%E9%93%BE%E8%A1%A8/%E5%A4%8D%E6%9D%82%E9%93%BE%E8%A1%A8%E7%9A%84%E5%A4%8D%E5%88%B6.html#%E4%BB%A3%E7%A0%81

https://github.com/YaxeZhang/Just-Code