算法与数据结构 链表

110 阅读3分钟
基本题型题目链接难度
合并两个有序列表21. 合并两个有序链表
返回倒数第k个节点剑指 Offer 22. 链表中倒数第k个节点
反转链表剑指 Offer 24. 反转链表
区间反转92. 反转链表 II⭐⭐⭐
两两交换链表中的节点24. 两两交换链表中的节点
k个一组翻转链表25. K 个一组翻转链表⭐⭐⭐
合并k个排序链表23. 合并K个升序链表⭐⭐
回文链表234. 回文链表⭐⭐
链表相交160. 相交链表⭐⭐

合并两个有序列表

  1. 创建一个节点hail,并且使用cur指向它
  2. 然后进行循环,比较每次的点list1list2的值的大小,小的话,就接在cur的后面
  1. 然后每次循环cur = cur.next
  2. 跳出循环,判断各自链表中是否有存在值,如果存在的话,就让cur指向那个有值的链表
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function(list1, list2) {
    let hail = new ListNode(0)
    let cur = hail
    while(list1 && list2){
        if(list1.val < list2.val){
            cur.next = list1
            cur = cur.next
            list1 = list1.next
        }else{
            cur.next = list2
            cur = cur.next
            list2 = list2.next
        }
    }
    if(list1){
        cur.next = list1
    }
    if(list2){
        cur.next = list2
    }
    return hail.next
};

好像有递归的方法

返回倒数第k个节点

  1. 计数总共n个节点,然后使用for循环n-k
  2. 另外设置一个链表,让他先走k,然后两个同时到达终点
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var getKthFromEnd = function(head, k) {
    // 17点09分
    // 第一步:获取链表的总结点数
    let cur = head
    let count = 0
    while(cur){
        count++
        cur = cur.next
    }
    cur = head
    // count = 3 k = 1 2
    for(let i = 0;i < count - k;i++){
        head = head.next
    }
    return head
};
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var getKthFromEnd = function(head, k) {
    let l2 = head
    let l1 = head
    while(k){
        l2 = l2.next
        k--
    }
    while(l2){
        l2 = l2.next
        l1 = l1.next
    }
    return l1
};

反转链表

  1. 首先设置一个pre结点,表示前面的值
  2. 然后每次循环的话,就先保存head.next,接着让head.next指向pre,然后pre变为head,然后headtem
  1. 最后返回上一个结点就是pre

\

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    // 17点21分
    let pre = null
    while(head){
        let tem = head.next
        head.next = pre
        pre = head
        head = tem
    }
    return pre
    // 17点23分
};
  1. 使用递归方法
  2. 递归结束curnull
  1. 每次cur指向pre
  2. 递归循环为(cur,next)
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    // 使用递归
    let rever = (pre,cur) => {
        if(!cur)return pre
        // 下一个数
        let next = cur.next
        // 当当前的数指向上一个数
        cur.next = pre
        return rever(cur,next)
    }
    return rever(null,head)
};

区间反转

  1. 拿到left前面的点fontleft对应的点tail
  2. 拿到开始进行遍历
  1. font连接right点,tail连接right+1个点
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} left
 * @param {number} right
 * @return {ListNode}
 */
var reverseBetween = function(head, left, right) {
    // 21点25分
    let newNode = new ListNode(0)
    // 这样子方便计数
    let cur = newNode
    cur.next = head
    // 做左到右的距离
    let count = right - left
    for(let i = 0;i < left - 1;i++){
        cur = cur.next
    }
    // 在left前面的根,用来连接第right个结点
    let font = cur
    // 表示的作为翻转链表的最后一个结点,作为结尾用来连接第right+1个结点
    let tail = cur.next
    // 表示上一个数
    let pre = cur.next

    // 从左节点开始
    let curr = pre.next
    for(let i = 0;i < count;i++){
        let next = curr.next
        // 为上一个结点
        curr.next = pre
        pre = curr
        curr = next
    }
    // tail连接right+1个结点
    tail.next = curr
    // font连接第right个结点
    font.next = pre

    return newNode.next
};

两两交换链表中的节点

  1. 首先一个newNode
  2. 每次循环确定有curcur.next的存在
  1. 需要pretwocur
  2. pre表示两个数的前面那个数,指向twotwo表示两个数的第二个数,指向curcur表示两个数的第一个数,指向next
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    // 只有临近的两个点才要进行交换,如果没有两个点不用进行交换
    let newNode = new ListNode(0)
    newNode.next = head
    let cur = head
    let pre = newNode
    while(cur && cur.next){
        let next = cur.next.next
        let two = cur.next
        two.next = cur
        pre.next = two
        cur.next = next
        pre = cur
        cur = next
    }
    return newNode.next
};

k个一组翻转链表

  1. 建立一个函数用来进行翻转前后
  2. 对次数进行判断链是否够长
  1. 后续的遍历
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var reverseKGroup = function(head, k) {
    let rever = (head,tail) => {
        // 指向前面的数
        let pre = tail.next
        let cur = head
        while(pre !== tail){
            let next = cur.next
            cur.next = pre
            pre = cur
            cur = next
        }
        return [tail,head]
    }

    let hair = new ListNode(0)
    hair.next = head
    let pre = hair

    while(head){
        // 尾部
        let tail = pre
        // 查看剩余的长度是否大于等于k
        for(let i = 0;i < k;i++){
            tail = tail.next 
            if(!tail)return hair.next            
        }
        // 下一个开始
        let next = tail.next;
        // 调换
        [head,tail] = rever(head,tail)
        // 把子链表接入原来的链表
        pre.next = head
        tail.next = next
        pre = tail
        head = tail.next
    }
    return hair.next
};

合并k个排序链表

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    //18点39分 平时的话是两个链表,所以会比较简单一些
    //这道题目的给出的链表数目很多,所以需要考虑到每一个链表,而且必须考虑到每一个链表对总链表的数
    //新建一个函数用来两个李安表进行互换
    //把两个链表从小到大进行排序
    var sort = function(list1,list2){
        if(!list1)return list2
        if(!list2)return list1
        var cur1 = list1
        var cur2 = list2
        //有一个结束的话
        var res = new ListNode(0)
        var cur = res
        //如果两个都存在的话,就进入下一步
        while(cur1 && cur2){
            //如果谁大的话,谁就赋值下一个
            if(cur1.val < cur2.val){
                var tem = cur1.val
                cur1 = cur1.next
            }else{
                var tem = cur2.val
                cur2 = cur2.next               
            }
            var temNode = new ListNode(tem)
            cur.next = temNode
            cur = cur.next
        }
        if(cur1)cur.next = cur1
        if(cur2)cur.next = cur2
        return res.next
    }
    if(lists.length == 0) return null
    var cur = lists[0]
    var len = lists.length
    //就是两两进行比较
    for(let i = 1;i < len;i++){
        cur =  sort(cur,lists[i])
    }
    return cur
    //19点06分 做是做出来,但是就是结果的性能比较低
};

回文链表

  1. 使用快慢指针,找出中间的值,并在这个过程中将前半部分进行反转,反转最后是首指针pre
  2. 通过判断fast是否为null,判断是否奇数还是偶数
  1. 对反转的链表和slow之后的链表进行一一判断
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {boolean}
 */
var isPalindrome = function(head) {
    // 23点47分
    // 没有或者只有一个元素
    if(!head || !head.next)return head
    let pre = null,cur = head, fast = head,slow = head
    while(fast && fast.next){
        // 反转前半段的
        slow = slow.next
        fast = fast.next.next
        cur.next = pre
        pre = cur
        cur = slow
    }
    // 如果是偶数的话,那么结果 fast刚好为null,奇数的话fast不为null
    if(fast) slow = slow.next
    while(slow){
        if(pre.val !== slow.val)return false
        pre = pre.next
        slow = slow.next
    }
    return true
};

链表相交

  1. 可以说是跑完自己的赛道,跑到别人的赛道,然后比较公平
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
var getIntersectionNode = function(headA, headB) {
    // 00点31分
    // 通过两次遍历,不同的话走各自的路
    let curA = headA
    let curB = headB
    while(curA !== curB){
        curA = curA === null?hQeadB: curA.next
        curB = curB === null?headA: curB.next
    }
    return curA
};