前端常见算法题(链表篇)--下

229 阅读9分钟

三、删除节点

2021.03.04

No.203 移除链表元素

删除链表中等于给定值 _val _的所有节点。 示例: 输入: 1->2->6->3->4->5->6, val = 6 输出: 1->2->3->4->5

方案:

/*
 * @lc app=leetcode.cn id=203 lang=javascript
 *
 * [203] 移除链表元素
 */

// @lc code=start
/**
 * 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} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    if(!head) return null;

    let p = phead = new ListNode(null);
    p.next = head;
    while(p.next) {
        if(p.next.val == val) {
            p.next = p.next.next;
        } else {
            p = p.next;
        }
    }
    return phead.next;
};

链表的构造,使用哑节点构造头指针进行双指针遍历



2021.03.06

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

 

示例 1: remove_ex1.jpg

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 示例 2:

输入:head = [1], n = 1 输出:[] 示例 3:

输入:head = [1,2], n = 1 输出:[1]  

提示:

链表中结点的数目为 sz 1 <= sz <= 30 0 <= Node.val <= 100 1 <= n <= sz

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/re… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=19 lang=javascript
 *
 * [19] 删除链表的倒数第 N 个结点
 */

// @lc code=start
/**
 * 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} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    let p = head, q = head, m = phead = new ListNode(head);
    m.next = head;
    // 将q指针变为 q = head.next···next 有n个next
    for( let _n = n; _n > 0; _n-- ) {
        q = head.next;
        head = head.next;
    };

    // 当q为null时 停止遍历
    while(q) {
        p = p.next;
        q = q.next;
        m = m.next;
    };

    // 删除此时q的节点
    p = p.next;
    m.next = p;

    return phead.next;
};

方案二:

/*
 * @lc app=leetcode.cn id=19 lang=javascript
 *
 * [19] 删除链表的倒数第 N 个结点
 */

// @lc code=start
/**
 * 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} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
  let lastN = 0;

  const recursion = (head) => {
    if (!head) {
      return null;
    }

    const next = recursion(head.next);

    lastN++;

    if (lastN === n) {
      head = next;
    }

    if (lastN === n + 1) {
      head.next = next;
    }

    return head;
  };
  
  return recursion(head);
};

方案三:

/*
 * @lc app=leetcode.cn id=19 lang=javascript
 *
 * [19] 删除链表的倒数第 N 个结点
 */

// @lc code=start
/**
 * 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} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
  const nowHead = [head];
  
  let tempHead = head;
  while (tempHead.next) {
    nowHead.push(tempHead.next);
    tempHead = tempHead.next;
  }
  
  let lastN = 0;

  let isHead = true;

  while (nowHead.length) {
    lastN++;
    const now = nowHead.pop();

    if (lastN - 1 === n) {
      isHead = false;
      now.next = now.next.next;
    }
  }

  if (isHead) {
    head = head.next;
  }

  return head;
};

有三种方案:1、快慢指针,设置快慢指针间距为n,进行遍历;2、递归;3、迭代



2021.03.09

No.82 删除排序链表中的重复元素 ii

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

输入: 1->2->3->3->4->4->5 输出: 1->2->5 示例 2:

输入: 1->1->1->2->3 输出: 2->3

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/re… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=82 lang=javascript
 *
 * [82] 删除排序链表中的重复元素 II
 */

// @lc code=start
/**
 * 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 deleteDuplicates = function (head) {
    if (!head) return null;
    let h = p = new ListNode(head),
        q = head;
    h.next = head;
    p.next = head
    while (q && q.next) {
        if ( q.val == q.next.val) {
            while(q.next && q.val == q.next.val) q = q.next; 
            p.next = q.next;
        } else {
            p = p.next;
        }
        q = q.next;
    }
    return h.next;
};

双指针处理,需要注意边界处理



2021.03.10

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

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2 输出: 1->2 示例 2:

输入: 1->1->2->3->3 输出: 1->2->3

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/re… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=83 lang=javascript
 *
 * [83] 删除排序链表中的重复元素
 */

// @lc code=start
/**
 * 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 deleteDuplicates = function(head) {
    if(!head) return null;

    let h = p = new ListNode(head),
        q = head;
    h.next = head;
    p.next = head;

    while(q && q.next) {
        if(q.next.val == q.val) {
            while(q.next && q.next.val == q.val) {
                q = q.next;
            }
            p.next = q;
        }
        p = p.next;
        q = q.next;
    };

    return h.next;
};

方案二:

/*
 * @lc app=leetcode.cn id=83 lang=javascript
 *
 * [83] 删除排序链表中的重复元素
 */

// @lc code=start
/**
 * 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 deleteDuplicates = function(head) {
    if(!head || !head.next) return head;

    head.next = deleteDuplicates(head.next);

    if(head.next.val == head.val) {
        head.next = head.next.next; 
    }

    return head;
};

同82题,有两种方案:1、快慢指针,也可以使用单指针遍历;2、递归



2021.03.11

No.237 删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。

 

现有一个链表 -- head = [4,5,1,9],它可以表示为:

237_example.png

  示例 1:

输入:head = [4,5,1,9], node = 5 输出:[4,1,9] 解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 示例 2:

输入:head = [4,5,1,9], node = 1 输出:[4,5,9] 解释:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.  

提示:

链表至少包含两个节点。 链表中所有节点的值都是唯一的。 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 不要从你的函数中返回任何结果。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/de… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=237 lang=javascript
 *
 * [237] 删除链表中的节点
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} node
 * @return {void} Do not return anything, modify node in-place instead.
 */
var deleteNode = function(node) {
    if(!node) return null;

    node.val = node.next.val;
    node.next = node.next.next;
};

没有头结点删除节点的方案,移花接木,需要替换当前值,然后删除下一个节点值



总结:

  1. 删除节点最常见的方案是快慢指针的遍历删除,快指针探索,慢指针作为最后需要返回的链表路径对节点进行相应的控制;
  2. 也可以使用递归、迭代、栈等额外的空间来换取相应的时间效率

四、特殊链表

2021.03.15

No.141 环形链表

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

 

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

 

示例 1: circularlinkedlist.png

输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。 示例 2: circularlinkedlist_test2.png

输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。 示例 3:

输入:head = [1], pos = -1 输出:false 解释:链表中没有环。  

提示:

链表中节点的数目范围是 [0, 104] -105 <= Node.val <= 105 pos 为 -1 或者链表中的一个 有效索引 。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/li… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=141 lang=javascript
 *
 * [141] 环形链表
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    if(!head) return false;
    let hash = new Map();

    while(head) {
        if(hash.has(head)) return true;
        hash.set(head, true);
        head = head.next;
    }

    return false;
};

方案二:

/*
 * @lc app=leetcode.cn id=141 lang=javascript
 *
 * [141] 环形链表
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
  let fast = head;
  let slow = head;
  while (fast) {                        
    if (fast.next == null) return false; 
    slow = slow.next;                 
    fast = fast.next.next;             
    if (slow == fast) return true;   
  }
  return false;
};

方案三:

/*
 * @lc app=leetcode.cn id=141 lang=javascript
 *
 * [141] 环形链表
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    try {
        JSON.stringify(head)
        return false
    } catch {
        return true
    }
};

有三种解法:1、构造hash表;2、快慢指针判断;3、取巧,利用JSON.stringify的不能循环引用



2021.04.26

No.160 相交链表

编写一个程序,找到两个单链表相交的起始节点。 如下面的两个链表: 160_statement.png

在节点 c1 开始相交。

 

示例 1: 160_example_1.png

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Reference of the node with value = 8 输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。  

示例 2: 160_example_2.png

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Reference of the node with value = 2 输入解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。  

示例 3: 160_example_3.png

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 解释:这两个链表不相交,因此返回 null。  

注意:

如果两个链表没有交点,返回 null. 在返回结果后,两个链表仍须保持原有的结构。 可假定整个链表结构中没有循环。 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/in… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=160 lang=javascript
 *
 * [160] 相交链表
 */

// @lc code=start
/**
 * 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) {
  if (!headA || !headB) return null;

  let pA = headA;
  while (pA) {
    let pB = headB;

    while (pB) {
      if (pA === pB) return pA;
      pB = pB.next;
    }

    pA = pA.next;
  }
};

方案二:

/*
 * @lc app=leetcode.cn id=160 lang=javascript
 *
 * [160] 相交链表
 */

// @lc code=start
/**
 * 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) {
    if (!headA || !headB) return null;

    const hashmap = new Map();

    let pA = headA;
    while (pA) {
        hashmap.set(pA, 1);
        pA = pA.next;
    }

    let pB = headB;
    while (pB) {
        if (hashmap.has(pB)) return pB;
        pB = pB.next;
    }
};

方案三:

/*
 * @lc app=leetcode.cn id=160 lang=javascript
 *
 * [160] 相交链表
 */

// @lc code=start
/**
 * 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) {
  if (!headA || !headB) return null;

  let pA = headA,
      pB = headB;
  while(pA != pB) {
    pA = pA === null ? headB : pA.next;
    pB = pB === null ? headA : pB.next;
  }

  return pA;
};

方案四:

/*
 * @lc app=leetcode.cn id=160 lang=javascript
 *
 * [160] 相交链表
 */

// @lc code=start
/**
 * 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) {
  	let aNum = 0
    let bNum = 0
    let short = headA
    let long = headB
    let tempA = headA
    let tempB = headB
    while (short) {
        aNum += 1
        short = short.next
    }
    while (long) {
        bNum += 1
        long = long.next
    }
    if (aNum > bNum) {
        let dig = aNum - bNum
        for (let i = 0; i < dig; i++) {
            tempA = tempA.next
        }
        while (tempA) {
            if(tempA == tempB) {
                return tempA
            } else {
                tempA = tempA.next
                tempB = tempB.next
            }
        }
    }
    if (aNum < bNum) {
        let dig = bNum - aNum
        for (let i = 0; i < dig; i++) {
            tempB = tempB.next
        }
        while (tempA) {
            if(tempA == tempB) {
                return tempA
            } else {
                tempA = tempA.next
                tempB = tempB.next

            }
        }
    }
    if (aNum = bNum) {
        while (tempA) {
            if(tempA == tempB) {
                return tempA
            } else {
                tempA = tempA.next
                tempB = tempB.next

            }
        }
    }
};

有四种方案:1、暴解,让A去找B;2、先遍历一遍生成hash表,然后判断;3、形成交叉的环形链表,交叉遍历;4、先遍历算出最长表,让最长表先走然后同步判断



2021.04.27

No.142 环形链表 ii

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

进阶:

你是否可以使用 O(1) 空间解决此题?  

示例 1: circularlinkedlist.png

输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。 示例 2: circularlinkedlist_test2.png

输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。 示例 3: circularlinkedlist_test3.png

输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。  

提示:

链表中节点的数目范围在范围 [0, 104] 内 -105 <= Node.val <= 105 pos 的值为 -1 或者链表中的一个有效索引

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/li… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=142 lang=javascript
 *
 * [142] 环形链表 II
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
    if(!head) return null;
    let hash = new Map();

    while(head) {
        if(hash.has(head)) return head;
        hash.set(head);
        head = head.next;
    }

    return null;
};

方案二:

/*
 * @lc app=leetcode.cn id=142 lang=javascript
 *
 * [142] 环形链表 II
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
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;
};

有两种方案:1、利用map或set数据结构进行链表的记录,空间复杂度为O(N);2、利用快慢指针,计算相遇的距离,省去了数据结构的存储空间,空间复杂度为O(1)



2021.05.10

No.143 重排链表

给定一个单链表 L:L0→L1→…→Ln-1→Ln ,将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

给定链表 1->2->3->4, 重新排列为 1->4->2->3. 示例 2:

给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/re… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=143 lang=javascript
 *
 * [143] 重排链表
 */

// @lc code=start
/**
 * 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 {void} Do not return anything, modify head in-place instead.
 */
var reorderList = function(head) {
    // 翻转链表
    const reverseList = head => {
        let cur = head;
        let pre = null;
        while(cur !== null){
            let temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
    // 分割链表
    const spliceList = head => {
        let dummy = new ListNode(0);
            dummy.next = head;

        let slow = dummy;
        let quick = dummy;

        while (quick && quick.next) {
            slow = slow.next;
            quick = quick.next;
            quick = quick.next;
        }

        let right = slow.next;
            slow.next = null;
        let left = dummy.next;

        return {
            left,
            right,
            dummy
        }
    }

    let { left, right, dummy } = spliceList(head);

    right = reverseList(right);

    while (left && right) {
        let lNext = left.next;
        let rNext = right.next;
        right.next = left.next;
        left.next = right;
        left = lNext;
        right = rNext;
    }

    return dummy.next
};

方案二:

/*
 * @lc app=leetcode.cn id=143 lang=javascript
 *
 * [143] 重排链表
 */

// @lc code=start
/**
 * 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 {void} Do not return anything, modify head in-place instead.
 */
var reorderList = function(head) {
  let list = []; // 使用数组存储链表
  let node = head; // 使用node遍历链表

  // 遍历链表,将每个元素依次存入数组
  while (node) {
    list.push(node);
    node = node.next;
  }

  let i = 0; // 使用i指针从头往后遍历list
  let j = list.length - 1; // 使用j指针从后往前遍历list

  // 两个指针向中间推进,直到相遇
  while (i < j) {
    // 将i指向j,并将i向后移动一位
    list[i++].next = list[j];
    // 将j指向i,并将j向前移动一位
    list[j--].next = list[i];
  }
  // list[i].next需要设置为null,否则链表会成环
  list[i].next = null;

  // head也是新链表的头结点
  return head;
};

有两种方案:1、快慢指针分割左右链表,利用左右链表的插入合成新链表;2、利用数组的位置进行合并处理



2021.05.11

No.148 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 进阶:

你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?  

示例 1: sort_list_1.jpeg

输入:head = [4,2,1,3] 输出:[1,2,3,4] 示例 2: sort_list_2.jpeg

输入:head = [-1,5,3,4,0] 输出:[-1,0,3,4,5] 示例 3:

输入:head = [] 输出:[]  

提示:

链表中节点的数目在范围 [0, 5 * 104] 内 -105 <= Node.val <= 105

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/so… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=148 lang=javascript
 *
 * [148] 排序链表
 */

// @lc code=start
/**
 * 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 sortList = function(head) {
    // 链表转数组
    const  list2Arr = head => {
        if(!head) return [];
        const a = [head.val];
        while(head.next) {a.push(head.next.val);head = head.next;}
        return a;
    }
    // 数组转链表
    const arr2List = arr => {
        if(arr.length == 0) return null;
        let head = new ListNode(arr[0]);
        let cur = head;
        for(let i=1; i < arr.length; i++) {
            cur.next = new ListNode(arr[i]);
            cur = cur.next;
        };
        return head;
    };
    // 对数组重新排序
    const sortArr = arr => {
        return arr.sort((a,b) => a-b)
    }

    return arr2List(sortArr(list2Arr(head)))
};

方案二:

/*
 * @lc app=leetcode.cn id=148 lang=javascript
 *
 * [148] 排序链表
 */

// @lc code=start
/**
 * 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 sortList = function(head) {
    if (!head || !head.next)  return head;
    let slow = head, fast = head;
    let preSlow = null;
    while (fast && fast.next) {
        preSlow = slow;
        slow = slow.next;
        fast = fast.next.next;
    }
    preSlow.next = null;
    const l = sortList(head);
    const r = sortList(slow);
    // 合并链表函数
    const merge = (l1, l2) => {
        const dummy = new ListNode(0);
        let prev = dummy;
        while (l1 && l2) {
            if (l1.val < l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
        if (l1) prev.next = l1;
        if (l2) prev.next = l2;
        return dummy.next;
    };
    return merge(l, r);
};

有两种方案:1、数组操作,利用自带的sort排序;2、归并排序,快慢指针实现



2021.05.12

No.234 回文链表

请判断一个链表是否为回文链表。​ 示例 1: ​

输入: 1->2 输出: false 示例 2: ​

输入: 1->2->2->1 输出: true 进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/pa… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=234 lang=javascript
 *
 * [234] 回文链表
 */

// @lc code=start
/**
 * 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) {
    // 翻转链表
    const reverseList = head => {
        let cur=head;
        let pre=null;
        while(cur !== null){
            let temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }

    // 分割链表
    const splitList = head => {
        let fast = head;
        let slow = head;
        while (fast.next !== null && fast.next.next !== null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

    if (head == null) return true;

    let l = head; 
    let _l = reverseList(splitList(head).next);

    while( l !== null && _l !== null ) {
        if(l.val !== _l.val) {
            return false;
        }
        l = l.next;
        _l = _l.next;
    }

    return true;
};

方案二:

/*
 * @lc app=leetcode.cn id=234 lang=javascript
 *
 * [234] 回文链表
 */

// @lc code=start
/**
 * 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) {
    let a='',b='';
    while(head!=null){
        a=a+head.val;
        b=head.val+b;
        head=head.next;
    }
  return a===b;
};

方案三:

/*
 * @lc app=leetcode.cn id=234 lang=javascript
 *
 * [234] 回文链表
 */

// @lc code=start
/**
 * 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) {
    const vals = [];
    while (head !== null) {
        vals.push(head.val);
        head = head.next;
    }
    for (let i = 0, j = vals.length - 1; i < j; ++i, --j) {
        if (vals[i] !== vals[j]) {
            return false;
        }
    }
    return true;
};

有三种方案:1、利用快慢指针拿到后半段链表进行翻转比较;2、利用js的加号特性,实现了一个类似栈型的操作;3、利用数组实现翻转数组的比较



2021.05.13

No.328 奇偶链表

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。​ 请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。 ​

示例 1: ​

输入: 1->2->3->4->5->NULL 输出: 1->3->5->2->4->NULL 示例 2: ​

输入: 2->1->3->5->6->4->7->NULL 输出: 2->3->6->7->1->5->4->NULL 说明: ​

应当保持奇数节点和偶数节点的相对顺序。 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/od… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=328 lang=javascript
 *
 * [328] 奇偶链表
 */

// @lc code=start
/**
 * 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 oddEvenList = function(head) {
    if(!head) return null;

    let p = head,
        q = head.next,
        tmp = q;
    
    while(q && q.next) {
        p.next = p.next.next;
        q.next = q.next.next;
        p = p.next;
        q = q.next;
    }

    p.next = tmp;

    return head;
};

利用双指针进行分割处理



总结:

  1. 特殊链表最常见的方案是快慢指针的进行相关的链表查找及处理,然后根据特殊链表的形式要求进行拆分及组合等;
  2. 也可以使用额外的数据结构如栈及hash表等进行处理