【前端js】实现剑指offer|leetcode(五)——链表题目集合

文章目录

    • 一、查询链表
        • 1. 从尾到头打印链表(牛客网-剑指offer)
        • 2. 链表中倒数第k个结点(牛客网-剑指offer)
    • 二、删除链表节点
        • 1. 删除链表中重复的结点(牛客网-剑指offer)
    • 三、链表排序
        • 1. 反转链表(牛客网-剑指offer)
        • 2. 合并两个排序的链表(牛客网-剑指offer)
        • 3.奇偶链表(leetcode 328. Odd Even Linked List)
        • 4.奇位增偶位减链表(leetcode 328. Odd Even Linked List)
    • 四、链表的环
        • 1. 判断是否有环(leetcode 141. Linked List Cycle)
        • 2. 链表中环的入口结点(牛客网-剑指 offer)


一、查询链表

1. 从尾到头打印链表(牛客网-剑指offer)

题目描述
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
思路

  • 从尾到头打印使用unshift
function printListFromTailToHead(head)
{
   // write code here
   if(!head) return [];//空链表/单链表
   if(!head.next) return [head.val];//空链表/单链表
    let res =[];
    let cur=head;
    while(cur){
        res.unshift(cur.val);
        cur =cur.next;
    }
    return res;
}

2. 链表中倒数第k个结点(牛客网-剑指offer)

题目描述
输入一个链表,输出该链表中倒数第k个结点。
思路

  1. 特殊情况:空链表和k小于0
  2. 一个快指针fast,一个慢指针slow,初始位置为头节点
  3. fast向后移动k-1次,如果移动中下一个节点为空,那么k值过小,返回
  4. 快指针fast和慢指针slow一起向后移动,直到fast为尾节点,返回slow指向的节点
function FindKthToTail(head, k) {
  // write code here
  if (!head || k <= 0) return null;
  let fast = head;
  let slow = head;
  //fast指针向后移动k-1
  for (let i = 1; i < k; i++) {
    if (fast.next) {
      //下一个节点不空,指向下一个
      fast = fast.next;
    } else {
      return null; //下一个节点空,说明k过小
    }
  }
  //fast和slow一起后移,直到fast是尾节点
  while (fast.next) {
    fast = fast.next;
    slow = slow.next;
  }
  return slow;//后面节点的位置就是倒数第k个节点
}

二、删除链表节点

1. 删除链表中重复的结点(牛客网-剑指offer)

题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路

  1. 特殊情况:单链表,空链表
  2. 新建一个节点newHead,放在头节点前面,当头节点需要被删除时,方便返回结果
  3. pre指针指向前一个节点(初始为newHeadcur指向当前节点(初始为头节点),next指向下一个节点(初始为null)
  4. cur不空,cur.next不空时,进入循环,依次比较节点,next保存cur.next
  5. 如果cur和next值相等,就进入循环,依次向后查找所有重复元素,然后删除中间所有重复元素(pre.next = next;),cur指向next的当前位置
  6. 如果cur和next值不相等,pre和cur依次向后移动,继续比较
  7. 最后遍历结束,退出循环,返回头节点:newHead.next
function deleteDuplication(pHead) {
  if (!pHead || !pHead.next) return pHead;
  let newHead = new ListNode("head"); //新建一个节点
  newHead.next = pHead; //充当新的头节点,当head节点被删除时可以返回正确的头节点
  let pre = newHead; //pre指向前一个节点
  let cur = pHead; //cur指向当前节点
  let next = null; //next指向下一个节点
  while (cur && cur.next) {
    //当前节点不空且下一个节点不空时,进入比较循环
    next = cur.next; //next存放下个节点的位置
    if (next.val === cur.val) {
      //cur和next值相等
      //进入循环向后查找所有重复元素
      while (next && next.val === cur.val) {
        next = next.next; //next后移一位
      }
      //next空或者next和cur值不相等,退出循环
      pre.next = next; //删除中间重复的节点
      cur = next; //cur指针指向next的位置
    } else {
      //cur和next值不相等
      pre = cur; //pre和cur都后移
      cur = next;
    }
  }
  return newHead.next;
}

三、链表排序

1. 反转链表(牛客网-剑指offer)

(leetcode 206. Reverse Linked List)
leetcode 206. Reverse Linked List
https://leetcode.com/problems/reverse-linked-list/

var reverseList = function(head) {
    if(!head||!head.next){//空链表/单链表,不需要反转
        return head;
    }
    let pre,next=null;
    while (head) {
        next=head.next;//next先保存下一个节点
        head.next=pre;//next指向pre,反转
        pre=head;//pre和head向后移动一位
        head=next;
    }
    return pre;//pre指向反转以后的头节点
};

2. 合并两个排序的链表(牛客网-剑指offer)

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

  1. 特殊情况:两个链表中有任意一个空链表,输出为另外一个链表
  2. 需要结果为单调递增的链表,所以取出头节点中较小的一个minHead作为合并头节点。
  3. 保存较大的maxHeadminHead.next指向下一次合并得出的头节点,这是一个递归的过程,参数为minHead.nextmaxHead,递归过程如下。
  4. 最后返回minHead
    【前端js】实现剑指offer|leetcode(五)——链表题目集合_第1张图片
function Merge(pHead1, pHead2) {
  // write code here
  //有一个链表为空,合并结果为另一个链表
  if (!pHead1 || !pHead2) return pHead1 || pHead2;
  //链表都不为空
  let minHead, //头节点中值较小的
    maxHead = null; //头节点中值较大的
  pHead1.val < pHead2.val
    ? ((minHead = pHead1), (maxHead = pHead2))
    : ((minHead = pHead2), (maxHead = pHead1));
  minHead.next = Merge(minHead.next, maxHead);
  return minHead;
}

3.奇偶链表(leetcode 328. Odd Even Linked List)

题目:给定单链表,将所有奇数节点组合在一起,然后是偶数节点。
思路

  1. 特殊情况,空/单/双链表不需要修改顺序
  2. odd指向奇数节点,even指向偶数节点,evenHead保存第一个偶节点
  3. while循环控制后移,条件:even && odd && even.next,因为even.next需要even存在,所以要先判断even,因为odd.next夹在了中间,所以只需要判断最后的额 even.next存在
  4. oddeven前,所以先移动odd——先改变.next指针,再将odd/even指向.next的位置。
  5. 最后连接奇偶链表,返回头节点head
var oddEvenList = function(head) {
  if (!head || !head.next || !head.next.next) {
    return head;
  }
  let odd = head, //odd指向奇数节点
    evenHead= head.next,
    even = head.next; //even指向偶数节点,evenHead保存第一个偶节点
  while (even && odd && even.next) {
    odd.next = even.next; //奇节点指向奇节点
    odd = odd.next; //odd指针移向下一个奇节点
    even.next = odd.next; //偶节点指向偶节点
    even = even.next; //even指针移向下一个奇节点
  }
  odd.next = evenHead; //连接奇偶链表
  return head;
};

4.奇位增偶位减链表(leetcode 328. Odd Even Linked List)

题目:一个链表,奇数位升序偶数位降序,让链表变成升序的。
思路
这道题可以分成三步:

  • 首先根据奇数位和偶数位拆分成两个链表。返回偶链表的头节点
  • 然后对偶数链表进行反转。返回反转后的头节点
  • 最后将两个有序链表进行合并。
function sortOddEvenList(head) {
  if (!head || !head.next) return null;
  let evenHead = splitLists(head); //拆分节点,保存偶链表的头节点
  reverseList(evenHead); //反转偶链表
  //   evenHead = reverseList(evenHead); //反转偶链表
  return mergeLists(head, evenHead); //合并两个奇偶链表
}
// 拆分链表
function splitLists(head) {
  if (!head || !head.next) return head;
  let odd = head,
    even = head.next,
    evenHead = head.next;
  while (odd && even && even.next) {
    odd.next = even.next; //拆分,先拆奇节点
    odd = odd.next; //后移
    even.next = odd.next; //拆分
    even = even.next; //后移
  }
  return evenHead;
}
// 反转链表
function reverseList(head) {
  if (!head || !head.next) return head;
  let pre = null,
    cur = head,
    next = null;
  while (cur) {
    next = cur.next; //保存下一位
    cur.next = pre; //反转
    pre = cur; //后移
    cur = next; //后移
  }
  //   return pre;
  evenHead = pre;
}
//合并两个有序链表
function mergeLists(head1, head2) {
  if (!head1 || !head2) return head1 || head1;
  let minHead,
    maxHead = null;
  head1.val < head2.val
    ? ((minHead = head1), (maxHead = head2))
    : ((minHead = head2), (maxHead = head1));
  minHead.next = mergeLists(minHead.next, maxHead);
  return minHead;
}
sortOddEvenList({
  val: 1,
  next: { val: 4, next: { val: 2, next: { val: 3, next: null } } }
});

四、链表的环

1. 判断是否有环(leetcode 141. Linked List Cycle)

141.Linked List Cycle:https://leetcode.com/problems/linked-list-cycle/submissions/
如何判断有环的存在?
在追及问题中,我们可以用两个速度不同的物体从同一地点出发,如果相遇则证明存在环(可用反证法证明,若不存在环,则速度不同的物体从同一地点出发则一定不会相遇),因此可以类比过来,定义两个指针fast、slow,令两指针以不同速度向后指,则相遇时证明有环存在若fast指向NULL,则不存在环。
注意相遇点不一定是环的入口
【前端js】实现剑指offer|leetcode(五)——链表题目集合_第2张图片

var hasCycle = function(head) {
  if (!head || !head.next) {
    //空链表/单链表,无环
    return false;
  }
  let slow = head,
    fast = head.next;
  while (fast != slow) {
    if (!fast || !fast.next) {
      //fast指针链表到头,无环
      return false;
    }
    slow = slow.next;
    fast = fast.next.next;
  }
  return true;
};

2. 链表中环的入口结点(牛客网-剑指 offer)

题目,给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
怎么找到环的入口结点
在问题一中两指针相遇后,让一个指针从头结点开始,另一个从相遇结点开始,并以相同速度向后指,再次相遇时就是环的入口结点。

function EntryNodeOfLoop(pHead) {
  // write code here
  //空链表/单链表,无环
 if (!pHead) return 1;
  if (!pHead.next) return null;
  let slow = pHead, //慢指针
    fast = pHead, ///快指针
    head = pHead; //头节点指针
  while (fast.next != null && slow != null) {//指针不为空,进入循环,继续后移
    slow = slow.next;
    fast = fast.next.next;
    if (slow == fast) break;//相遇,跳出循环
  }
  while (fast != head) {//相遇,跳出循环
    head = head.next;
    fast = fast.next;
  }
  return head;
}

参考:https://blog.csdn.net/liushall/article/details/80444753

你可能感兴趣的:(【前端js】实现剑指offer|leetcode(五)——链表题目集合)