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
};

//优化
var mergeTwoLists = function(list1, list2) {
    let cur1 = list1,cur2 = list2
    let newHead = new ListNode(0,null)
    let cur = newHead
    while(cur1&&cur2){
        if(cur1.val<=cur2.val){
            cur.next = cur1
            cur = cur.next
            cur1 = cur1.next
        }else{
            cur.next = cur2
            cur = cur.next
            cur2 = cur2.next
        }
    }
    cur.next = cur1?cur1:cur2
    return newHead.next
};

23.合并k个有序链表

在这里插入图片描述
题解参考

方法一:纵向比较

时间复杂度 O(n^2) 空间O(1)

var mergeKLists = function(lists) {
    //方法一:纵向比较 时间复杂度 O(n^2)  空间O(1)
    //首先去掉lists中的空链表
    for(let i=0;i<lists.length;i++){
        if(!lists[i]){
            lists.splice(i,1)
            //去掉一个成员之后 i-- res:删除一个之后,后面的向前移动了
            i--
        }
    }
    if(lists.length==0) return null
    let newHead = new ListNode(0,null)
    let cur = newHead
    while(lists.length>1){
        let minIndex = 0 //最小的索引
        //接下来比较每个链表的头节点的大小
        for(let j=0;j<lists.length;j++){ //list[i] 就是下标是i的链表的头节点
            if(lists[minIndex].val>lists[j].val){
                minIndex = j
            }
        }
        //连接
        cur.next = lists[minIndex]
        cur = cur.next
        //判断minIndex所在链表的后面是否还是结点
        if(lists[minIndex].next){
            lists[minIndex] = lists[minIndex].next
        }else{
            //当前的链表的所有的结点都已经连接了 那么将该链表从数组中删除
            lists.splice(minIndex,1)
        }
    }
    //while循环结束之后,lists中只剩下一个链表
    while(lists[0]){
        cur.next =lists[0]
        cur = cur.next
        lists[0] = lists[0].next
    }
    return newHead.next
};

方法二:横向比较

时间复杂度:O(n^2) 空间复杂度:O(n) 这里要注意:这里我声明了一个新的链表,而不是简单的进行了链表节点指针的改变

var mergeKLists = function(lists) {
    //横向比较
    //声明两个链表合并的函数
    const merge = (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 = cur.next
                cur1 = cur1.next
            }else{
                cur.next = cur2
                cur = cur.next
                cur2 = cur2.next
            }
        }
        //连接剩余的
        cur.next = cur1?cur1:cur2
        return newHead.next
    }
    //进行遍历合并
    let temp = null //合并之后的链表
    for(let i=0;i<lists.length;i++){
        temp = merge(temp,lists[i])
    }
    return temp
};

方法三:归并算法

时间复杂度:O(nlogn) ,res:合并两个链表时间是0(n) 归并排序的次数是O(logn)
空间复杂度:O(logn),res:递归调用栈使用

讲解参考

var mergeKLists = function(lists) {
    //横向比较
    //声明两个链表合并的函数
    const merge = (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 = cur.next
                cur1 = cur1.next
            }else{
                cur.next = cur2
                cur = cur.next
                cur2 = cur2.next
            }
        }
        //连接剩余的
        cur.next = cur1?cur1:cur2
        return newHead.next
    }
    //进行遍历合并  使用归并算法
    const helper = (lists,start,end) =>{
        //递归跳出
        // if(lists[start]==null) return null //这里是为了解决lists是空的情况 如果lists的第一个链表是空的,但是后面的不是空的 就会出现错误
        if(lists.length==0) return null 
        if(start==end){
            return lists[start]  //划分成了一个链表,那么直接返回
        }
        let mid = start + ((end - start) >>1) //二进制向右侧移动一个位置,相当于除以2
        let left = helper(lists,start,mid)
        let right = helper(lists,mid+1,end)
        return merge(left,right)
    }
    return helper(lists,0,lists.length-1)
};

使用小根堆实现

参考1
参考2

*①时间复杂度 O(nklogk) n链表的长度 k是有几个链表(也即是堆中同时有几个数据) 堆的高度是logk 那么每次调整的时间复杂度是O(logk) 每个链表有nk个结点 记住就行
②空间复杂度:O(k) ,res: 堆结构中声明了一个长度为k的数组
*

堆的常见操作包括插入、删除最大/最小元素、堆排序等,它们的时间复杂度如下:
堆的创建:O(n),其中n为堆中元素的数量。
插入元素:O(log n),其中n为堆中元素的数量。
删除堆顶元素(最大/最小元素):O(log n),其中n为堆中元素的数量。
堆排序:O(n log n),其中n为堆中元素的数量。
需要注意的是,这里的时间复杂度都是平均时间复杂度,在最坏情况下,时间复杂度会退化为O(n)。例如,如果在一个最大堆中,需要删除最小元素,则需要将堆中的所有元素都进行调整,时间复杂度会变为O(n)。但是,在实际应用中,由于堆的随机性,该情况出现的概率相对较小

//定义小根堆 使用数组来保存结构
class minHeap{
    constructor(){ //可以将构造函数看作是类的初始化函数,在创建对象时执行它,相当于其他语言中的构造函数
        this.heap = [] //使用数组来保存堆的相关信息
    }
    //获取小根堆的成员个数
    size(){
        return this.heap.length
    }
    //获取左侧节点的索引
    getLeft(i){
        return 2*i + 1 
    }
    getRight(i){
        return 2*i + 2
    }
    //获取parent节点的索引
    getParent(i){
        return (i-1)>>1
    }
    swap(i1,i2){
        // [list1.val,list2.val] = [list2.val,list1.val]
        // [this.heap[i1].val,this.heap[i2].val] = [this.heap[i2].val,this.heap[i1].val]
        [this.heap[i1],this.heap[i2]] = [this.heap[i2],this.heap[i1]] //注意这里交换的是结点,res:只有交换节点才能保证 出堆的时候能够node.next找到原来的链表的下一个结点
    }
    //向上调整
    shiftUp(index){
        if(index==0) return //如果index是根节点 那么直接返回
        let parentIndex = this.getParent(index)
        if(this.heap[parentIndex]&&this.heap[parentIndex].val>this.heap[index].val){
            this.swap(parentIndex,index)
        }
        //继续向上交换
        this.shiftUp(parentIndex)
    }
    shiftDown(index){
        let left = this.getLeft(index)
        let right = this.getRight(index)
        if(this.heap[left]&&this.heap[left].val<this.heap[index].val){
            this.swap(left,index)
            this.shiftDown(left)
        }
        if(this.heap[right]&&this.heap[right].val<this.heap[index].val){
            this.swap(right,index)
            this.shiftDown(right)
        }
    }
    //向堆中添加成员
    insert(val){
        this.heap.push(val)
        //向上进行调整
        this.shiftUp(this.size()-1)
    }
    //堆顶元素出堆
    getPop(){
        if(this.size()==1) return this.heap.shift()
        //别的情况使用堆的最后一个元素替换
        let top = this.heap[0]
        this.heap[0] = this.heap.pop()
        this.shiftDown(0)
        return top
    }

}

//具体操作
var mergeKLists = function(lists) {
    let newHead = new ListNode(0,null)
    let cur = newHead
    let h = new minHeap()
    //首先链表的头节点入堆
    // for(let i=0;i<lists.length;i++){
    //     if(lists[i]){
    //         h.insert(lists[i])
    //     }
    // }
    lists.forEach(item =>{
        if(item) h.insert(item)
    })
    while(h.size()){
        let top = h.getPop()
        cur.next = top
        cur = cur.next
        if(top.next){
            h.insert(top.next)
        }
    }
    return newHead.next
};

148.排序链表

在这里插入图片描述

方法一:使用数组进行排序,然后重新组成新链表

var sortList = function(head) {
    //方法一:首先将value值保存到数组中,然后进行排序,重新创建链表
    let cur = head
    let arr = []
    while(cur){
        arr.push(cur.val)
        cur = cur.next
    }
    arr.sort((a,b)=>a-b)
    let newHead = new ListNode(0,null)
    cur = newHead
    for(let i=0;i<arr.length;i++){
        cur.next = new ListNode(arr[i])
        cur = cur.next
    }
    return newHead.next
};

方法二:归并 同23的归并思路

参考

var sortList = function(head) {
    //使用归并排序
    //合并链表  时间复杂度:每次合并是O(n) 递归是O(logn) =>O(nlogn) 空间复杂度O(logn) 主要是递归栈
    const merge = (head1,head2) =>{
        //递归跳出
        if(head1==null) return head2
        if(head2==null) return head1
        let newHead = new ListNode(0,null)
        let cur = newHead
        let cur1 = head1,cur2 = head2
        while(cur1&&cur2){
            if(cur1.val<cur2.val){
                cur.next = cur1
                cur = cur.next
                cur1 = cur1.next
            }else{
                cur.next = cur2
                cur = cur.next
                cur2 = cur2.next
            }
        }
        cur.next = cur1?cur1:cur2
        return newHead.next
    }
    //进行归并 start是左侧链表的起始 end是右侧链表的起始
    const helper = (start,end) =>{
        if(start==null) return start  //这种情况是用来对付链表为空的
        if(start.next==end){
            //前后两个链表是不相关的 断开链子 分别返回
            start.next = null
            return start
        }  //进行划分过程中 划分成了一个 那么直接返回
        //使用快慢指针寻找中间结点
        let slow = start,fast = start
        while(fast!==end&&fast.next!==end){ //自己可以数手指头 当链表的个数是奇数个 那么最后slow指向最中间 如果链表的个数是偶数个 那么最后slow指向中间两个节点的后一个 反正这里不用管位置到底那样 只需要找一个位置将这个链表划分成两个即可
            slow = slow.next
            fast  = fast.next.next
        }
        let mid = slow
        return merge(helper(start,mid),helper(mid,end)) //对中点的左右两部分继续递归,然后对递归的返回值进行合并
    }

    return helper(head,null) //最开始只有一条链表 
};

203.移除链表元素

在这里插入图片描述

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

var removeElements = function(head, val) {
    if(head==null) return head
    let newHead = new ListNode(0,null)
    let cur = head,pre = newHead
    while(cur){
        if(cur.val==val){
            cur = cur.next
            pre.next = cur//加上这一句才能保证pre会随着cur指针进行筛选
        }else{
            pre.next = cur
            pre = pre.next
            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
};


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
        }else{
            cur = cur.next
        }
    }
    return newHead.next
};

707.设计链表

//定义listnode
class ListNode{
    constructor(val,next){
        this.val = val
        this.next = next
    }
}
//初始化
var MyLinkedList = function() {
    this.head = null
    this.tail = null
    this.size = 0
};
//根据索引值找到节点的位置
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
}

/** 
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function(index) {
    if(index<0||index>=this.size) return -1
    return this.getNode(index).val  //注意这里返回val
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function(val) {
    let newHead = new ListNode(val,this.head)
    this.head = newHead
    if(this.tail==null){
        this.tail = this.head
    }
    this.size++             //注意size自增
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function(val) {
    let node = new ListNode(val)
    this.size++
    if(this.tail){      //错误判断,不能直接this.tail = node
        this.tail.next = node
        this.tail = node
        return
    }
    this.head = node
    this.tail = node
    
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    //判断是否是在头添加结点
    if(index<=0){
        this.addAtHead(val)
        return
    }
    if(index>this.size) return
    if(index==this.size){    //这里判断出错,当index==this.size的时候才是在尾部插入
        this.addAtTail(val)
        return
    }
    //在其他位置添加结点
    let preNode = this.getNode(index - 1)
    let newNode = new ListNode(val,preNode.next)
    preNode.next = newNode
    this.size++
};

/** 
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if(index<0||index>=this.size) return	//当this.size==0的时候一定会跳出 
    if(index==0){
        this.head = this.head.next
        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) {
    //递归法
    let newHead = null
    const helper = node =>{
        if(node==null||node.next==null){
            newHead = node
            return node
        }
        let nextNode = helper(node.next)
        nextNode.next = node
        node.next = null
        return node
    }
    helper(head)
    return newHead
};

递归法二

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
};

234.回文链表

在这里插入图片描述

方法一:使用数组比对

var isPalindrome = function(head) {
    //方法一:放入到数组中 判断数组是否是回文数组
    let cur = head
    let arr = []
    while(cur){
        arr.push(cur.val)
        cur = cur.next
    }
    //从数组的两头进行比较
    let start = 0,end = arr.length - 1
    while(start<end){
        if(arr[start]!==arr[end]){
            return false
        }
        start++
        end--
    }
    return true
};

方法二:分割链表,然后比对

var isPalindrome = function(head) {
    //思路:使用快慢指针找到中间节点,将后一段结点进行翻转 然后从头开始进行比对
    if(head==null||head.next==null) return true
    let slow = head,fast = head
    let pre = null
    while(fast&&fast.next){ //如果只有一个结点,那么while循环结束之后,slow fast指向head;如果有两个结点 那么最后slow fast都会指向中间两个结点的后一个结点;总之,当链表的个数是奇数,那么最后slow指向中间结点,如果链表的个数是偶数个,那么最后slow指向中间两个结点的后一个。
        pre = slow
        slow = slow.next
        fast = fast.next.next
    }
    pre.next = null//将前后两段进行分开
    let tempNode = null
    let cur  = slow
    while(cur){
        let nextNode = cur.next
        cur.next = tempNode
        tempNode = cur
        cur = nextNode
    }
    let head2 = tempNode
    while(head&&head2){ //当链表的个数是奇数的时候,中间节点会分在后一段链表中 只要把两段链表的相应部分进行比对完成即可 中间结点不用比对
        if(head.val!==head2.val){
            return false
        }
        head = head.next
        head2 = head2.next
    }
    return true
};

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

在这里插入图片描述

使用队列进行交换

var swapPairs = function(head) {
    //使用队列完成
    if(head==null||head.next==null) return head
    let queue = []
    let newHead = new ListNode(0,null)
    let cur = newHead
    //将全部的结点放入队列
    while(head){
        queue.push(head)
        head = head.next
    }
    while(queue.length>=2){  //不满足当前的条件的时候,queue中可能剩余1个或者0个
        let node1 = queue.shift()
        let node2 = queue.shift()
        cur.next = node2
        cur = cur.next
        cur.next = node1
        cur = cur.next
    }
    if(queue.length){    //这里判断是否还有剩余
        let lastNode = queue.shift()
        cur.next = lastNode
        cur = cur.next
    }
    cur.next = null //消除圈
    return newHead.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
};

//修改 ingingingingingingingingigninginginginginginging
var swapPairs = function(head) {
    if(head==null||head.next==null) return head
    //原地进行交换
    let newHead = new ListNode(0,head)
    let cur = newHead
    let nextNode = null
    //cur的下一个结点 cur的下一个的下一个结点
    let firstNode = cur.next,secondNode = cur.next.next
    while(firstNode&&secondNode){
        nextNode = secondNode.next
        secondNode.next = firstNode
        firstNode.next = nextNode
        cur.next = secondNode
        cur = firstNode
        if(cur.next&&cur.next.next){
            firstNode = cur.next
            secondNode = cur.next.next
            continue
        }else{
            break
        }
    }
    return newHead.next
};

递归实现

参考

var swapPairs = function(head) {
    //使用递归
    const helper = head =>{
        if(head==null||head.next==null) return head
        let nextNode = head.next //上面的判断语句没有执行,也即是说nextNode!==null
        head.next = helper(nextNode.next)//可以理解为head在承接后面已经处理好的链表
        nextNode.next = head  //nextNode 放到原先的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) {
    //使用两次循环 第一次先找到链表的长度 第二次循环 找到相应的位置
    if(head==null) return head
    let len = 0
    let cur = head
    while(cur){
        len++
        cur = cur.next
    }
    if(n>len) return null
    if(n==len) return head.next
    let needIndex = len - n + 1//比如len = 5 n = 3 那么len - n = 2 也就是要删除第三个结点
    cur = head
    let num = 1
    let pre = null
    while(num!==needIndex){
        num++
        pre = cur
        cur = cur.next
    }
    //最后cur就是要删除的结点
    pre.next = cur.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
};

方法二:根据a+b = b + a

var getIntersectionNode = function(headA, headB) {
    let A = headA
    let B = headB
    while(A !== B){
        A = A !== null ? A.next : headB
        B = B !== null ? B.next : headA
    }
    console.log(null==null)//true
    return A
};


var getIntersectionNode = function(headA, headB) {
    if(headA==null||headB==null) return null
    //方法二 根据 a+b = b+a 的原理
    let cur1 = headA, cur2 = headB
    while(cur1||cur2){
        if(cur1==cur2) return cur1
        cur1==null?cur1=headB:cur1=cur1.next
        cur2==null?cur2=headA:cur2=cur2.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){//如果有圈的话,那么fast slow会在圈中相遇 如果没有圈,那么fast会提前为空,然后跳出while循环
            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 //没有圈
};



var detectCycle = function(head) {
    //快慢指针 fast每次走两个位置 slow每次走一个位置
    // a + 2b + c = 2(a + b) => c = a 当fast指针进入环之后,slow还未进入环,假设fast slow同时入环,那么当fast走了一圈之后,slow走了半圈,可是fast比slow先入环,那么当fast与slow相遇的时候,slow在环内正在第一圈。这里我们假设从入环结点到fast slow相遇的结点之间的距离是b,head到入环结点之间的距离是a,c是二者相遇的结点到入口结点之间的距离,当二者相遇的时候,令fast = head,那么二者再次相遇时候,即是入口
    if(head==null||head.next==null) return null
    let slow = head,fast = head
    while(fast&&fast.next){
        slow = slow.next
        fast = fast.next.next
        if(slow==fast) break
    }
    if(fast==null||fast.next==null) return null //这种情况说明没有环
    fast = head
    while(fast!==slow){
        fast = fast.next
        slow = slow.next
    }
    return slow
};

146.LRU缓存

在这里插入图片描述
思路:使用map对象,因为map对象的成员是有先后循序的,每次get的时候,先判断原来的map中是否有这个成员,如果有的话,将其先删除再将其set,这样这个刚刚访问过的成员就被重新放入到了map的最后一个,经过这样一直的操作,最后map的第一个成员就是根据LRU算法的要求,要出去的。
参考教程
map使用了iterator的相关信息,参考

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    //使用map对象 map对象中的每个键是有先后顺序的,
    this.map = new Map() //this指向LRUCache
    this.capacity = capacity
};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    if(this.map.has(key)){
        let value = this.map.get(key)
        this.map.delete(key)  //删除之后再去set 相当于将其放到了最后一位
        this.map.set(key,value)
        return value
    }else{
        return -1
    }
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    //为了实现LRU 最近最少使用的要求 每次访问一个元素之后,先将其删除,让后再添加到map中,这样就是加入到map对象的末尾,一直这样操作,map对象的第一个key就是最近最少使用的
    if(this.map.has(key)){
        this.map.delete(key)
        this.map.set(key,value)
    }else{
        //这里是先将key value加入到map中然后判断map中的成员数量,如果超了 那么就删除第一个成员
        this.map.set(key,value)
        if(this.map.size>this.capacity){
            //map对象是可迭代的 keys()返回一个新的迭代对象 next()是迭代器中的函数 它可以获取map的迭代对象的第一个成员 如果再去next 那么获取第二个成员
            this.map.delete(this.map.keys().next().value)
        }
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * var obj = new LRUCache(capacity)
 * var param_1 = obj.get(key)
 * obj.put(key,value)
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值