leecode算法题【链表】(一)

203移除链表元素

方法一:递归

var removeElements = function(head, val) {
    if (head === null) {
            return head;
    }
    head.next = removeElements(head.next, val);
    return head.val === val ? head.next : head;
};
var removeElements = function (head, val) {
  if (head == null) {
    return head;
  }
  head.next = removeElements(head.next, val);
  // 只需要考虑当前节点是否等于val值,如果当前节点等于val值,则返回当前节点的下一个节点,否则返回当前节点
  return head.val == val ? head.next : head;
};

方法二:迭代

链表常规解题法,创建dummy指针,通过判断当前指针的下一节点的值是否为val解题。

var removeElements = function(head, val) {
    const dummyHead = new ListNode(0);
    dummyHead.next = head;
    let temp = dummyHead;
    while (temp.next !== null) {
        if (temp.next.val == val) {
            temp.next = temp.next.next;
        } else {
            temp = temp.next;
        }
    }
    return dummyHead.next;
};
var removeElements = function (head, val) {
  let dummyNode = new ListNode(-1);
  dummyNode.next = head;
  let p = dummyNode;
  while (p.next != null) {
    if (p.next.val == val) {
      // 如果遍历到的节点的值等于val值,则将当前节点的next指针指向当前指向的节点的下一个节点
      p.next = p.next.next;
    } else {
      p = p.next;
    }
  }
  return dummyNode.next;
};
var removeElements = function(head, val) {
    let newList = new ListNode(0,head)
    let cur = newList
    while(cur.next){
        if(cur.next.val == val){
            cur.next = cur.next.next
        }else{
            cur = cur.next
        }
    }
    return newList.next
};
var removeElements = function(head, val) {
    const ret = new ListNode(0, head);
    let cur = ret;
    while(cur.next) {
        if(cur.next.val === val) {
            cur.next =  cur.next.next;
            continue;
        }
        cur = cur.next;
    }
    return ret.next;
};

如果不用虚拟头结点:

var removeElements = function(head, val) {
    // 先跳过所有头结点
    while(head !== null && head.val === val) {
        head = head.next
    }
    if(head === null) {
        return head
    }
    pre = head
    // 之后正常遍历即可~
    while(pre !== null && pre.next !== null) {
        if(pre.next.val === val) {
            pre.next = pre.next.next
        }
        else {
            pre = pre.next
        }
    }
    return head
};

83删除排序链表中的重复元素

不需要使用虚拟头结点

var deleteDuplicates = function(head) {
    let cur = head
    while(cur !== null && cur.next !== null) {
        if(cur.val === cur.next.val) {
            // 当前值与下一个值相同则 通过修改链表结点指向跳过这个值
            cur.next = cur.next.next
        }
        else {
            // 当前值与下一个值不相同则 结点继续移动 检查接下来的结点
            cur = cur.next
        }
    }
    return head
};

206&剑指offer24反转链表

方法一:迭代

时间复杂度:O(n),其中n是链表的长度,需要遍历链表一次
空间复杂度:O(1)

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

加一个为空的特殊情况,执行时间会少很多。

var reverseList = function(head) {
   if (head === null || head.next === null) return head;
   let prev = null;             // 定义一个前驱节点
   let cur = head;              // 定义一个当前节点
   while (cur) {
       let next = cur.next;     // 定义一个后继节点
       // 执行交换操作
       cur.next = prev;
       prev = cur;
       cur = next;
   }
   return prev;
};

方法二:递归

时间复杂度:O(n),其中n是链表的长度,需要遍历链表一次
空间复杂度:O(n)

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

方法三:ES6语法

var reverseList = function(head) {
    /* 
        类比思考 反转数组思路 双端指针实现
        while (l < r) swap(a, l++, r--)
        数组位置的交换是这样 [a[i], a[j]] = [a[j], a[i]]
        但是 单链表只能依次通过 next 访问 不能通过索引访问 
        链表的交换需要扩展一个指针 即next
        cur 当前项
        prev 上一项
        cur.next 当前指针指向
        [ cur.next, prev, cur ] = [prev, cur, cur.next]
        上面这段ES6语法表示
        当前cur 的指针next 指向prev上一项 并且 交换迭代prev 和 next
    */
    let [p, c] = [null, head]

    while (c) [c.next, p, c] = [p, c, c.next]

    return p
};

141环形链表

方法一:快慢指针

「Floyd 判圈算法」(又称龟兔赛跑算法):我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置head,而快指针在位置head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
观察下面的代码,我们使用的是 while 循环,循环条件先于循环体。由于循环条件一定是判断快慢指针是否重合,如果我们将两个指针初始都置于 head,那么 while 循环就不会执行。因此,我们可以假想一个在 head 之前的虚拟节点,慢指针从虚拟节点移动一步到达 head,快指针从虚拟节点移动两步到达 head.next,这样我们就可以使用 while 循环了。
时间复杂度:O(n)
空间复杂度:O(1)

var hasCycle = function(head) {
    if(head==null||head.next==null){return false;}
    let fast=head.next;
    let slow=head;
    while(fast!=slow){
        if(fast==null||fast.next==null){return false;}
        slow=slow.next;
        fast=fast.next.next;
    }
    return true;
};
var hasCycle = function(head) {
  // 快慢指针初始化指向 head
  let slow = head;
  let fast = head;
  // 快指针走到末尾时停止
  while (fast && fast.next) {
    // 慢指针走一步,快指针走两步
    slow = slow.next;
    fast = fast.next.next;
    // 快慢指针相遇,说明含有环
    if (slow == fast) {
      return true;
    }
  }
  // 不包含环
  return false;
};

方法二:哈希表

时间复杂度:O(n)
空间复杂度:O(n)

var hasCycle = (head) => {
    let map = new Map();
    while (head) {
        if (map.has(head)) return true;//如果当前节点在map中存在就说明有环
        map.set(head, true);//否则就加入map
        head = head.next;//迭代节点
    }
    return false;//循环完成发现没有重复节点,说明没环
};

方法三:巧用JS

var hasCycle = function(head) {
    try {
        JSON.stringify(head)
        return false
    } catch {
        return true
    }
};

142环形链表II

方法一:快慢指针

时间复杂度:O(n)
空间复杂度:O(1)

var detectCycle = function(head) {
    if (head === null) {
        return null;
    }
    let slow = head, fast = head;
    while (fast !== null) {
        slow = slow.next;
        if (fast.next !== null) {
            fast = fast.next.next;
        } else {
            return null;
        }
        if (fast === slow) {
            let ptr = head;
            while (ptr !== slow) {
                ptr = ptr.next;
                slow = slow.next;
            }
            return ptr;
        }
    }
    return null;
};
var detectCycle = function(head) {
    if(head==null||head.next==null){return null;}
    let fast=head;
    let slow=head;
    while(fast&&fast.next){
        fast=fast.next.next;
        slow=slow.next;
        if(fast==slow){
            let ptr=head;
            while(ptr!=slow){
                ptr=ptr.next;
                slow=slow.next;
            }
            return ptr;
        }
    }
    return null;
};
var detectCycle = function(head) {
  // 快慢指针初始化指向 head
  let slow = head;
  let fast = head;
  // 快指针走到末尾时停止
  while (fast && fast.next) {
    // 慢指针走一步,快指针走两步
    slow = slow.next;
    fast = fast.next.next;
    // 快慢指针相遇,说明含有环
    if (slow == fast) {
      // 任一一节点指向头节点
      fast = head;
      // 同步向前进
      while (fast != slow) {
        fast = fast.next;
        slow = slow.next;
      }
      // 返回入口节点
      return fast;
    }
  }
  // 不包含环
  return null;   
};

方法二:哈希表

时间复杂度:O(n)
空间复杂度:O(n)

var detectCycle = function(head) {
    const visited = new Set();
    while (head !== null) {
        if (visited.has(head)) {
            return head;
        }
        visited.add(head);
        head = head.next;
    }
    return null;
};

剑指offer22链表中倒数第k个节点

方法一:顺序查找

时间复杂度:O(n)
空间复杂度:O(1)

var getKthFromEnd = function(head, k) {
    let node = head, n = 0;
    while (node) {
        node = node.next;
        n++;
    }
    node = head;
    for (let i = 0; i < n - k; i++) {
        node = node.next;
    }
    return node; 
};

方法二:快慢指针

时间复杂度:O(n)
空间复杂度:O(1)
应该考虑的特殊情况有:
head为空指针;
k大于链表的长度;
输入的参数k为0;

var getKthFromEnd = function(head, k) {
    let fast=head;
    let slow=head;
    for(let i=0; i<k; i++){
        fast=fast.next;
    }
    while(fast){
        slow=slow.next;
        fast=fast.next;
    }
    return slow;
};
var getKthFromEnd = function(head, k) {
    let fast = head, slow = head;
    
    while (fast && k > 0) {
        [fast, k] = [fast.next, k - 1];
    }
    while (fast) {
        [fast, slow] = [fast.next, slow.next];
    }
    return slow;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值