您现在的位置是:首页 >技术交流 >leetcode刷题之链表相关问题(js)网站首页技术交流

leetcode刷题之链表相关问题(js)

Faith_gyz 2024-07-01 06:01:02
简介leetcode刷题之链表相关问题(js)

21.合并两个有序链表

在这里插入图片描述

var mergeTwoLists = function(list1, list2) {
    if(list1==null) return list2
    if(list2==null) return list1
    let newHead = new ListNode(0,null) //创建一个虚拟节点
    let cur = newHead
    let cur1 = list1,cur2 = list2
    while(cur1&&cur2){
        if(cur1.val<=cur2.val){
            cur.next = cur1
            cur = cur1
            cur1 = cur1.next
        }else{
            cur.next = cur2
            cur = cur2
            cur2 = cur2.next
        }
    }
    while(cur1){
        cur.next = cur1
        cur = cur1
        cur1 = cur1.next
    }
    while(cur2){
        cur.next = cur2
        cur = cur2
        cur2 = cur2.next
    }
    return newHead.next
};

203.移除链表元素

在这里插入图片描述

方法一:新建虚拟节点+pre

var removeElements = function(head, val) {
    if(head==null) return null
    let newHead = new ListNode(-1)
    newHead.next = head
    let cur = head,pre = newHead
    while(cur){
        if(cur.val==val){
            cur = cur.next
            pre.next = cur
        }else{
            pre = cur
            cur = cur.next
        }
    }
    return newHead.next
};

方法二:参考大佬的写法

var removeElements = function(head, val) {
    //设置一个虚拟头节点
    let newHead = new ListNode(0,head)
    let cur = newHead
    while(cur.next){
        if(cur.next.val==val){
            cur.next = cur.next.next
            continue
        }
        cur = cur.next
    }
    return newHead.next
};

707.设计链表

//节点的构造函数
class ListNode {
    constructor(val,next){
        this.val = val
        this.next = next
    }
}
var MyLinkedList = function() {
    //链表的初始化
    this.size = 0
    this.head = null
    this.tail = null
};

/** 
 * @param {number} index
 * @return {number}
 */
//用来获取某个索引的结点
MyLinkedList.prototype.getNode = function(index){
    if(index<0||index>=this.size) return null
    let cur = new ListNode(0,this.head) //创建一个虚拟节点
    while(index-- >=0){
        cur = cur.next
    }
    return cur
}
//获取某个index的值
MyLinkedList.prototype.get = function(index) {
    if(index<0||index>=this.size) return -1
    return this.getNode(index).val
};
/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function(val) {
    let newHead = new ListNode(val,this.head)
    this.head = newHead
    //判断尾巴结点
    if(!this.tail){
        this.tail = this.head
    }
    this.size++
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function(val) {
    let newTail = new ListNode(val,null)
    this.size++
    if(this.tail){
        this.tail.next = newTail
        this.tail = newTail
        return
    }
    this.head = newTail
    this.tail = newTail
    
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if(index>this.size) return
    if(index<=0){
        this.addAtHead(val)
        return
    }
    if(index==this.size){
        this.addAtTail(val)
        return
    }
    let preNode = this.getNode(index-1)
    preNode.next = new ListNode(val,preNode.next)
    this.size++
};

/** 
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if(index<0||index>=this.size) return  //这里已经排除空的情况了
    if(index==0){
        this.head = this.head.next
        //处理尾巴结点
        if(index==this.size-1){
            this.tail = this.head
        }
        this.size--
        return
    }
    //获取上一个结点
    let preNode = this.getNode(index-1)
    preNode.next = preNode.next.next
    if(index==this.size-1){
        this.tail = preNode
    }
    this.size--
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * var obj = new MyLinkedList()
 * var param_1 = obj.get(index)
 * obj.addAtHead(val)
 * obj.addAtTail(val)
 * obj.addAtIndex(index,val)
 * obj.deleteAtIndex(index)
 */

206.反转链表

在这里插入图片描述

方法一:头插法

var reverseList = function(head) {
    //头插法
    if(head==null||head.next==null) return head
    //创建一个虚拟节点
    let newHead = new ListNode(-1,null)
    let cur = head
    let nextNode = null
    while(cur){
        //保留cur的下一个节点
        nextNode = cur.next
        cur.next  = newHead.next
        newHead.next = cur
        //然后找回nextNode
        cur = nextNode
    }
    return newHead.next
};

递归法一

var reverseList = function(head) {
//这个递归式从后往前进行翻转
    const helper = curNode =>{
        //递归跳出的条件 要考虑空链表的情况所以要加上curNode==null这一判断条件
        if(curNode==null||curNode.next==null) return curNode
        //p相当于是最后一个结点
        let p = helper(curNode.next)
        // if(p!==null){   这里的if语句可以去掉了 只要链表不是空的 那么curNode.next都可以条粗递归
            curNode.next.next = curNode
            curNode.next = null
        // }
        return p
    }
    return helper(head)
};

递归法二

var reverseList = function(head) {
    //这种方法是从第一个结点开始向前翻转,一直到最后一个结点
    const helper = (pre,head) =>{
        //递归跳出的条件
        if(head==null) return pre
        let temp = head.next
        head.next = pre
        pre = head
        //向后进行遍历
        return helper(pre,temp)    //整个递归的最后返回值是pre 也就是原链表的最后一个结点
    }
    return helper(null,head)
};

使用栈来进行翻转

var reverseList = function(head) {
    //使用栈 将所有的结点全都压入栈中 然后创建一个虚拟结点 然后栈中的结点出栈
    if(head==null||head.next==null) return head
    let stack = []
    let cur = head
    //全部入栈
    while(cur){
        stack.push(cur)
        cur = cur.next
    }
    let newNode = new ListNode(0,null)
    let curNode = newNode
    while(stack.length){
        let node = stack.pop()
        curNode.next = node
        curNode = node
    }
    curNode.next = null//防止最后形成一个圈
    return newNode.next
};

24.两两交换链表中的结点

在这里插入图片描述

使用队列进行交换

var swapPairs = function(head) {
    if(head==null||head.next==null) return head
    let queue = []
    let cur = head
    while(cur){
        queue.push(cur)
        cur = cur.next
    }
    let newNode = new ListNode(0,null)
    let now = newNode
    //当队列中有偶数个结点
    while(queue.length>=2){
        let node1 = queue.shift()
        let node2 = queue.shift()
        //在使用队列或者栈,我试了好多次 在出栈之前将 每个节点的next指针置为空 这样才能避免
        // node1.next = null
        // node2.next = null
        now.next = node2
        now = node2
        now.next = node1
        now = node1
    }
    //当只剩下一个节点了
    while(queue.length==1){
        let node1 = queue.shift()
        // node1.next = null
        now.next = node1
        now = node1
    }
    now.next = null   //修改一下 将最后一个结点的next 置为空即可
    return newNode.next
};

原地进行交换

var swapPairs = function(head) {
    //原地进行交换
    if(head==null||head.next==null) return head
    //进行交换
    let newHead = new ListNode(0,head) //创建一个虚拟节点
    let curNode = newHead.next
    let pre = newHead
    while(curNode&&curNode.next){
        let nextNode = curNode.next.next //保存结点
        pre.next = curNode.next
        pre = curNode
        curNode.next.next = curNode
        curNode.next = nextNode
        curNode = nextNode
    }
    //最后只剩下一个结点 就不用进行交换了
    return newHead.next
};

递归实现

参考

var swapPairs = function(head) {
    //和翻转链表很类似
    const helper = head =>{ //判断条件head==null是用来判断链表是否为空的
        if(head==null||head.next==null) return head
        //每次返回的节点是处理好之后的
        let nextNode = head.next
        head.next = helper(nextNode.next)
        nextNode.next = head
        return nextNode
    }
    return helper(head)
};

19.删除链表的倒数第n个结点

在这里插入图片描述

方法一:使用两次循环找到第n个节点的位置

var removeNthFromEnd = function(head, n) {
    //方法一:从头开始进行遍历找到第n个结点
    if(head==null) return head
    let sum = 1 //统计链表的总长度
    let cur = head
    while(cur.next){
        sum++
        cur = cur.next
    }
    //如果链表的长度不够
    if(n>sum) return head
    if(n==sum){
         return head.next
    }
    //也即是删除第 sum-n+1个结点  需要找它前面的结点
    let i = 1
    cur = head
    while(i<sum-n){
        i++
        cur = cur.next
    }
    cur.next = cur.next.next
    return head
};

方法二:使用快慢指针

var removeNthFromEnd = function(head, n) {
    //使用快慢指针 当fast指针走了n个位置之后 slow指针开始走动 当fast走到空的时候 slow所指的位置就是倒数第n个位置
    if(head==null) return head
    let slow = head, fast = head
    let i = 1 //记录位置
    //首先fast指针要移动到第n+1个位置
    while(fast.next&&i<=n){
        i++
        fast = fast.next
    }
    if(i<n) return head //也就是说链表的长度小于n
    if(i==n) return head.next//链表的长度正好是n
    //链表的长度够了 那么slow fast同时向前移动
    while(fast.next){
        fast = fast.next
        pre = slow
        slow = slow.next  //slow正好是要删除的位置的前面
    }
    slow.next = slow.next.next
    return head
};

面试题02.07.链表相交

在这里插入图片描述

var getIntersectionNode = function(headA, headB) {
    if(headA==null||headB==null) return null
    //首先找出两个链表的长度
    let cur = headA
    let sumA = 1,sumB = 1
    while(cur.next){
        sumA++
        cur = cur.next
    }
    cur = headB
    while(cur.next){
        sumB++
        cur = cur.next
    }
    // console.log(sumA,sumB)
    let len = Math.abs(sumA - sumB)
    // console.log(len,'changdu ')
    if(sumA>sumB){
        //A链表先去移动len个位置
        while(len-- >0){
            headA = headA.next
        }
    }
    if(sumA<sumB){
        while(len-- >0){
            headB = headB.next
        }
    }
    // console.log(headA,headB,'jiedian')
    //移动完成之后 a b 后面的长度相同
    while(headA){
        if(headA==headB) return headA
        headA = headA.next
        headB = headB.next
    }
    return null
};

141.环形链表

在这里插入图片描述

使用快慢指针

var hasCycle = function(head) {
    //使用快慢指针 slow每次走一个位置 fast每次走两个位置 如果链表中有圈 那么fast一定会转一圈然后赶上slow,可以这么理解,slow相对于的fast的位置是不变的,fast相对于slow每次走一个位置,这样如果链表中有圈,那么fast一定可以回到原来的位置
    if(head==null||head.next==null) return false
    let slow = head,fast = head
    while(fast.next){
        fast = fast.next
        if(fast.next){
            fast = fast.next
        }
        slow = slow.next
        // if(slow==fast){ //这样会出现错误 由于上面 进行while循环的时候,fast可以只走一个位置,而此时slow也走一个位置 这样即使是一条链 也会相等
        //     return true
        // }
        if(fast.next==slow){
            return true
        }
    }
    return false
};

快慢指针修改

var hasCycle = function(head) {
    //使用快慢指针 slow每次走一个位置 fast每次走两个位置 如果链表中有圈 那么fast一定会转一圈然后赶上slow,可以这么理解,slow相对于的fast的位置是不变的,fast相对于slow每次走一个位置,这样如果链表中有圈,那么fast一定可以回到原来的位置
    if(head==null||head.next==null) return false
    let slow = head,fast = head
    while(fast&&fast.next){
        fast = fast.next.next
        slow = slow.next
        if(slow==fast){
            return true
        }
    }
    return false
};

142.环形链表2

方法一:使用map保存节点信息

var detectCycle = function(head) {
    if(head==null||head.next==null) return null
    //使用map对象来保存遍历过的结点
    let map = new Map()
    let cur = head
    while(cur.next){
        if(map.has(cur)){
            //如果map中有这个结点 那么这个结点就是圈的起点
            return cur
        }else{
            map.set(cur,1)
        }
        cur = cur.next
    }
    return null
};

方法二:快慢指针

在这里插入图片描述
思路:
①当slow指针和fast指针相遇的时候,slow走了x+y;fast走了x+y+n(y+z);
②由于fast的速度是slow的2倍,所以有2(x+y)=x+y=n(y+z)
③移项得x = n (y + z) - y
④令n=1得到x=z,也即是当slow和fast第一次相遇的时候,如果这时候有一个结点temp从head出发,那么temp和slow同时移动,当temp==slow的时候,temp就是入口结点

参考代码随想录

var detectCycle = function(head) {
    if(head==null||head.next==null) return null
    let slow = head,fast = head
    while(fast&&fast.next){
        fast = fast.next.next
        slow = slow.next
        if(slow==fast){
            //既然是有环的 那么就要去找这个环的入口
            fast = head
            while(fast!==slow){
                fast = fast.next
                slow = slow.next
            }
            return fast
        }
    }
    return null //没有圈
};
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。