数据结构与算法之链表

有更好的方法或者是有问题想讨论的同学可以评论or私信我,大家一起学习一起进步~

2022/4/2
I.链表环相关
牛客网-在线编程-算法篇-面试必刷top101-链表bm6
1.判断链表中是否有环
-方法1. 使用快慢指针 time: O(n) space: O(1)
定义慢指针slow和快指针fast, slow一次走一步,fast一次走两步。若链表无环,则slow和fast不可能相遇。若slow和fast相遇了,则链表一定有环。具体代码如下,
function hasCycle( head ) {
  //corner case, empty linked list has no cycle
  if (!head) return false;
  //Traverse linked list using two pointers.
  let slow = head, fast = head;
  //Move one pointer(slow_p) by one and another pointer(fast_p) by two.
  while (fast != null && fast.next != null) {
      slow = slow.next;
      fast = fast.next.next;
      //If these pointers meet at the same node then there is a loop. 
      if (slow == fast) return true;
  }
  //If pointers do not meet then linked list doesn’t have a loop.
  return false;
}
-方法2.使用哈希表 time: O(n) space: O(n)
定义一个哈希表,每次访问过一个结点我们都把这个结点加入哈希表中,如果新访问的结点已经存在于该哈希表中就说明链表有环。具体代码如下,
function hasCycle( head ) {
  // use hashtable, everytime we visited a node, add it into the hashtable
  let set = new Set();
  while (head != null) {
  if (set.has(head)) return true;
      set.add(head);
      head = head.next;
  }
  return false;
}
牛客网-在线编程-算法篇-面试必刷top101-链表bm7
2.链表中环的入口结点
-方法1.使用哈希表 time: O(n) space: O(n)
定义一个哈希表,每次访问过一个结点我们都把这个结点加入哈希表中,如果新访问的结点已经存在于该哈希表中就返回该结点。如果遍历完成也没有重复访问的节点就无环,返回null. 具体代码如下,
function EntryNodeOfLoop(pHead)
{
  //corner case
  if (!pHead) return null;
  //hashtable time: O(n) space: O(n)
  let set = new Set();
  while (pHead != null) {
  if (set.has(pHead)) return pHead;
      set.add(pHead);
      pHead = pHead.next;
  }
  return null;
}
-方法2.使用双指针 time: O(n) space: O(1)
定义一个慢指针一次走一步,一个快指针一次走两步,快指针和慢指针将会在环中某点相遇。设从链表头结点到环入口点到距离为a, 环入口点到相遇点到距离为b, 相遇点到环入口点到距离为c, 如下图所示(图片来源网络,侵删)

慢指针走的距离长度等于——a+b
快指针走的距离长度等于——a+k(b+c)+b, 其中k(k > 0)为快指针走的圈数。就像在环形操场跑步,快指针同学的速度是慢指针同学速度的两倍,那么快指针同学总会在某个点追上慢指针同学,并且此时快指针同学已经比慢指针同学多跑了一圈以上了。
我们知道快指针的速度是慢指针的两倍,相同时间下快指针走过的路程也该是慢指针的两倍,于是可以得到等式:
2(a+b) = a+k(b+c)+b  --> a = k(b+c) - b --> a = (k-1)(b+c) + c, k-1 > 0.
所以如果我们让一个指针从起点出发,一个指针从相遇点出发,最后它们一定会相遇在环入口。
代码如下,
function EntryNodeOfLoop(pHead)
{
  //corner case
  if (!pHead) return null;
  let slow = pHead, fast = pHead;
  while (fast != null && fast.next != null) {
      slow = slow.next;
      fast = fast.next.next;
      if (slow == fast) {
          slow = pHead;
          while (slow != fast) {
              slow = slow.next;
              fast = fast.next;
          }
      return slow;
      }
  }
  return null;
}

II.链表其他题目(剑指offer)

牛客网-剑指offer-知识分类篇-数据结构-链表jz6
3.从尾到头打印链表 time: O(n) space: O(n) 太简单不说了
牛客网-剑指offer-知识分类篇-数据结构-链表jz24
4.反转链表
-方法:先对原链表作头结点删除操作,再对新链表作头结点插入操作。代码如下,
function ReverseList(pHead) {
    let prev = null, curr = pHead;
    while (curr != null) {
        let next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}
牛客网-剑指offer-知识分类篇-数据结构-链表jz52
5.两个链表的第一个公共结点
-方法:两个链表若是有公共结点且链表长度不相等,那么让长链表先把差值走了,短链表再开始走,就会碰到公共结点。代码如下,
function FindFirstCommonNode(pHead1, pHead2)
{
  //corner case
  if (pHead1 == null || pHead2 == null) return null;
  let p1 = pHead1, p2 = pHead2;
  while (p1 != p2) {
      p1 = p1.next;
      p2 = p2.next;

      if (p1 == p2) break;
      //pHead2 is longer, let it to walk first
      if (p1 == null) p1 = pHead2;
      //pHead2 is longer, let it to walk first
      if (p2 == null) p2 = pHead1;
  }
  return p1;
}

2022/4/3
牛客网-剑指offer-知识分类篇-数据结构-链表jz52
6.链表中倒数最后k个结点
     -方法1:先遍历一遍链表,计算出它的长度存入length变量。于是现在倒数第k个结点就是正数第(length - k)个结点。这里k可能大于length, 可能等于length, 也可能小于length. 当k大于length时,链表中不存在该结点,返回null; 当k等于length时,倒数第k个结点恰好是链表第一个结点,返回pHead; 当k小于length时,结点存在于链表中的(length - k)位置,遍历(length - k)次链表,然后返回剩余链表元素。代码如下,
function FindKthToTail( pHead , k ) {
  // conrner case
  if (pHead === null || k === 0) return null;

  // count the length of the linked list
  let length = 0, p = pHead;
  while (p) {
      length++;
      p = p.next;
  }

  // traverse the linked list by (length - k) times
  let times = length - k;
  if (times < 0) {
      return null;
  } else if (times === 0) {
      return pHead;
  } else {
      while (times > 0) {
          pHead = pHead.next;
          times--;
      }
      return pHead;
    }
}
     -方法2:思路与方法1相同,不过是不计算链表长度,直接遍历k次,剩余元素数量即为正序遍历次数。复制链表pHead, 然后遍历(k - 1)次该复制链表。最后(复制链表的剩余元素数量 - 1)则为正序遍历次数。代码如下,
function FindKthToTail( pHead , k ) {
  //corner case
  if (pHead == null || k == 0) return null;

  let node = pHead;
  //traverse the linked list by k times, and the length of the left linked list will be (length - k)
  for(let i = 1; i < k; i++){
      pHead = pHead.next;
      if (pHead == null) return null;
  }

  //traverse the linked list by (length - k) times, and we'll get the result
  while (pHead.next){
      node = node.next;
      pHead = pHead.next;
  }
  return node;
}

2022/4/4
牛客网-剑指offer-知识分类篇-数据结构-链表jz18
7.删除链表的结点
     -方法:找到待删结点的前一个结点,改变改结点指向。代码如下,
function deleteNode( head , val ) {
  // corner case
  if (!head) return null;
  if (head.val === val) return head.next;

  // find the element before the toBeDelete element
  let res = head;
  while (head !== null && head.next !== null && head.next.val !== val) head = head.next;

  // delete the toBeDelete element
  head.next = head.next.next;

  return res;
}
#数据结构与算法面试常考题##春招##实习##笔试题目##前端##校招#
全部评论

相关推荐

11-15 19:28
已编辑
蚌埠坦克学院 硬件开发
点赞 评论 收藏
分享
ProMonkey2024:5个oc?厉害! 但是有一个小问题:谁问你了?😡我的意思是,谁在意?我告诉你,根本没人问你,在我们之中0人问了你,我把所有问你的人都请来 party 了,到场人数是0个人,誰问你了?WHO ASKED?谁问汝矣?誰があなたに聞きましたか?누가 물어봤어?我爬上了珠穆朗玛峰也没找到谁问你了,我刚刚潜入了世界上最大的射电望远镜也没开到那个问你的人的盒,在找到谁问你之前我连癌症的解药都发明了出来,我开了最大距离渲染也没找到谁问你了我活在这个被辐射蹂躏了多年的破碎世界的坟墓里目睹全球核战争把人类文明毁灭也没见到谁问你了(别的帖子偷来的,现学现卖😋)
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-26 15:46
已编辑
字节国际 电商后端 24k-35k
点赞 评论 收藏
分享
评论
4
3
分享
牛客网
牛客企业服务