1

链表反转

输入一个链表,反转链表后,输出新链表的表头。

思路:
遍历链表,分别用currentNode、nextNode、preNode记录当前节点、上一个节点、下一个节点

function reverseList(head) {
    let currentNode = head;
    let nextNode = head.next;
    let preNode = null;
    while (currentNode) {
        currentNode.next = preNode;
        preNode = currentNode;
        currentNode = nextNode;
        nextNode = currentNode && nextNode.next;
    }
    return preNode;
}

从尾到头打印链表

输入一个链表,从尾到头打印链表每个节点的值。
思路:先将链表每个结点的值存入数组中,然后通过数组的reverse方法,即可从尾到头打印。

  function ListNode(x){
    this.val = x;
    this.next = null;
  }

  function printListFromTailToHead(head){
    if(!head) {
      return 0;
    }
    else {
      let arr = new Array();
      let cur = head;
      while(cur) {
        arr.push(cur.val);
        cur = cur.next;
      }
      return arr.reverse();
    }
  }

  let node1 = new ListNode(1);
  let node2 = new ListNode(2);
  let node3 = new ListNode(3);
  node1.next = node2;
  node2.next = node3;

  console.log(printListFromTailToHead(node1));

这里需要反向打印链表,于是很自然的可以想到用递归来实现。要实现反过来输出链表,我们每次访问到一个节点的时候,先递归输出它后面的节点,再输出该节点自身,这样链表的输出结果就反过来了。

  function printListFromTailToHead(head) {
    if(head !== null) {
      printListFromTailToHead(head.next);
      console.log(head.val);
    }
  }

删除链表中的节点

给定单链表的头指针和要删除的指针节点,在O(1)时间内删除该节点。

思路:
1.删除的节点不是尾部节点 - 将next节点覆盖当前节点
2.删除的节点是尾部节点且等于头节点,只剩一个节点 - 将头节点置为null
3.删除的节点是尾节点且前面还有节点 - 遍历到末尾的前一个节点删除
只有第三种情况时间复杂度是O(n),且这种情况只会出现1/n次,所以算法时间复杂度是O(1)

function deleteNode(head, node) {
    if (node.next) {
        node.val = node.next.val;
        node.next = node.next.next;
    } else if (node === head) {
        node = null;
        head = null;
    } else {
        node = head;
        while (node.next.next) {
        node = node.next;
        }
        node.next = null;
        node = null;
    }
    return head;
}

复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。

思路:
拆分成三步

1.复制一份链表放在前一个节点后面,即根据原始链表的每个节点Node创建Node',把Node'直接放在Node的next位置,Node'的next指向Node的next,让复制后的链表和原始链表组成新的链表,相当于把克隆的节点插入在被克隆节点的后面。形成链表比如:Node0->Node0'->Node1->Node1'->Node2->Node2'->Node3->Node3'
2.给复制的链表random赋值,即Node'.random=Node.random.next。
3.拆分链表,将Node'和Node进行拆分,保证原始链表不受影响。

function Clone(pHead) {
    if (pHead === null) {
    return null;
    }
    cloneNodes(pHead);
    cloneRandom(pHead);
    return reconnetNodes(pHead);
}

// 第一步:克隆节点
function cloneNodes(pHead) {
    let current = pHead;
    while(current) {
        let cloneNode = {
            val : '',
            next : null,
            random : null
        }
        cloneNode.val = current.val;
        cloneNode.next = current.next;
        current.next = cloneNode;
        current = cloneNode.next;
    }
    return pHead;
}

// 第二步:给克隆的节点random赋值
function cloneRandom(pHead) {
    let current = pHead;
    let cloneNode;
    while(current) {
        cloneNode = current.next;
        cloneNode.random = current.random && current.random.next;
        current = cloneNode.next;
    }
    return pHead;
}

// 第三步:拆分链表
function reconnetNodes(pHead) {
    let current = pHead;
    let cloneNode;
    let cloneHead = current.next;
    while(current) {
        cloneNode = current.next;
        current.next = cloneNode.next;
        cloneNode.next = current.next && current.next.next;
        current = current.next;
    }
    return cloneHead;
}

合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

循环版本:

function merge(list1, list2) {
    let head = {};
        head.next=null;
        let root = head;
    while(list1!=null&&list2!=null) {
        if(list1.val<list2.val){
            head.next=list1;
            head=list1;
            list1=list1.next;
        }
        else {
            head.next=list2;
            head=list2;
            list2=list2.next;
        }
    }
    //如果有一个链表为空,另一个链表非空,则应该把非空链表合并到链表尾部。
    if(list1!=null) {
        head.next=list1;
    }
    if(list2!=null) {
        head.next=list2;
    }
            return root.next;
}

递归版本:

function merge(list1, list2) {
    if (!list1) {
        return list2;
    }
    if (!list2) {
        return list1;
    }
    let head;
    if (list1.val < list2.val) {
        head = list1;
        head.next = merge(list1.next, list2);
    } else {
        head = list2;
        head.next = merge(list1, list2.next);
    }
    return head;
}

链表倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。

思路:
简单思路: 循环到链表末尾找到 length 在找到length-k节点 需要循环两次。
优化:
设定两个节点,间距相差k个节点,当前面的节点到达终点,取后面的节点。
前面的节点到达k后,后面的节点才出发。
代码鲁棒性: 需要考虑head为null,k为0,k大于链表长度的情况。

function FindKthToTail(head, k) {
    if (!head || !k) return null;
    let front = head;
    let behind = head;
    let index = 0;
    while (front.next) {
        index++;
        if (index >= k) {
            behind = behind.next;
        }
        front = front.next;
    }
    return (k <= index) && behind;
}

链表中环的入口节点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

思路:
声明两个指针 P1 P2
1.判断链表是否有环: P1 P2 从头部出发,P1走两步,P2走一步,如果可以相遇,则环存在
2.从环内某个节点开始计数,再回到此节点时得到链表环的长度 length
3.P1、P2 回到head节点,让 P1 先走 length 步 ,当P2和P1相遇时即为链表环的起点

function EntryNodeOfLoop(pHead) {
    if (!pHead || !pHead.next) {
        return null;
    }
    let P1 = pHead.next;
    let P2 = pHead.next.next;
    // 1.判断是否有环
    while (P1 != P2) {
        if (P2 === null || P2.next === null) {
            return null;
        }
        P1 = P1.next;
        P2 = P2.next.next;
    }
    // 2.获取环的长度
    let temp = P1;
    let length = 1;
    P1 = P1.next;
    while (temp != P1) {
        P1 = P1.next;
        length++;
    }
    // 3.找公共节点
    P1 = P2 = pHead;
    while (length-- > 0) {
        P2 = P2.next;
    }
    while (P1 != P2) {
        P1 = P1.next;
        P2 = P2.next;
    }
    return P1;
}

两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。

思路:
1.先找到两个链表的长度length1、length2
2.让长一点的链表先走length2-length1步,让长链表和短链表起点相同
3.两个链表一起前进,比较获得第一个相等的节点
时间复杂度O(length1+length2) 空间复杂度O(0)

function FindFirstCommonNode(pHead1, pHead2) {
  if (!pHead1 || !pHead2) { return null; }
  // 获取链表长度
  let length1 = getLength(pHead1);
  let length2 = getLength(pHead2);
  // 长链表先行
  let lang, short, interval;
  if (length1 > length2) {
    lang = pHead1;
    short = pHead2;
    interval = length1 - length2;
  } else {
    lang = pHead2;
    short = pHead1;
    interval = length2 - length1;
  }
  while (interval--) {
    lang = lang.next;
  }
  // 找相同节点
  while (lang) {
    if (lang === short) {
      return lang;
    }
    lang = lang.next;
    short = short.next;
  }
  return null;
}

function getLength(head) {
  let current = head;
  let result = 0;
  while (current) {
    result++;
    current = current.next;
  }
  return result;
}

鸡蛋炒番茄
1.1k 声望1.3k 粉丝

hello world