链表
链表是一种物理结构,不受编程语言限制,java python等都有链表的数据结构概念。链表各元素间是连续的,但是在内存存储上是离散的,这有区别于数组。常见考察:
- 链表的处理:合并、删除
- 链表的反转
- 链表成环问题
实现一个数组转链表:
interface ILineListNode {
val: number
next?: ILineListNode
}
function createLinkList(nums: number[]): ILineListNode {
const len = nums.length
let linkNode: ILineListNode = {
val: nums[len - 1]
}
for (let i = len - 2; i >= 0; i--) {
linkNode = {
val: nums[i],
next: linkNode
}
}
return linkNode
}
console.log('LinkList', createLinkList([1,2,3,4]))
合并两个有序链表
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
const mergeTwoLists = function(l1, l2) {
// 定义头结点,确保链表可以被访问到
let head = new ListNode()
// cur 这里就是咱们那根“针”
let cur = head
// “针”开始在 l1 和 l2 间穿梭了
while(l1 && l2) {
// 如果 l1 的结点值较小
if(l1.val<=l2.val) {
// 先串起 l1 的结点
cur.next = l1
// l1 指针向前一步
l1 = l1.next
} else {
// l2 较小时,串起 l2 结点
cur.next = l2
// l2 向前一步
l2 = l2.next
}
// “针”在串起一个结点后,也会往前一步
cur = cur.next
}
// 处理链表不等长的情况
cur.next = l1!==null?l1:l2
// 返回起始结点
return head.next
};
删除排序链表中的重复元素
有序的链表,因此对前后节点进行比较就行。
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
var deleteNode = function(head, val) {
let dummy = new ListNode()
dummy.next = head
let cur = dummy
while(cur !== null && cur.next !== null) {
if(cur.next.val === val) {
cur.next = cur.next.next
} else {
cur = cur.next
}
}
return dummy.next
};
删除排序链表中的重复元素 II
这道题与以上不一样的地方在于,重复的值都删除掉。
/**
* @param {ListNode} head
* @return {ListNode}
*/
var deleteDuplicates = function(head) {
// head有0个或1个结点时,直接返回
if(!head || !head.next) {
return head
}
// dummy结点
let dummy = new ListNode()
dummy.next = head
let cur = dummy
// 遍历比较
while(cur.next && cur.next.next) {
if(cur.next.val === cur.next.next.val) {
// 有重复的值时,记下val,遍历是否有多个重复的值
let val = cur.next.val
while(cur.next && cur.next.val === val) {
// 有重复cur继续往下走
cur.next = cur.next.next
}
} else {
// 无重复,cur正常遍历
cur = cur.next
}
}
// 返回链表的起始结点
return dummy.next
};
删除链表的倒数第 N 个结点
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
// 定义dummy结点
let dummy = new ListNode()
dummy.next = head
// 快指针
let fast = dummy
// 慢指针
let slow = dummy
// fast先往前冲n步
while(n) {
fast = fast.next
n--
}
// 快慢指针齐头并进, 走完链表
while(fast.next) {
slow = slow.next
fast = fast.next
}
slow.next = slow.next.next
// 返回头节点
return dummy.next
};
反转链表
/**
* @param {ListNode} head
* @return {ListNode}
*/
const reverseList = function(head) {
// 初始化前驱结点为 null
let pre = null;
// 初始化目标结点为头结点
let cur = head;
// 只要目标结点不为 null,遍历就得继续
while (cur !== null) {
// 记录一下 next 结点
let next = cur.next;
// 反转指针
cur.next = pre;
// pre 往前走一步
pre = cur;
// cur往前走一步
cur = next;
}
// 反转结束后,pre 就会变成新链表的头结点
return pre
};
环形链表
/**
* @param {ListNode} head
* @return {boolean}
*/
// 入参是头结点
const hasCycle = function(head) {
// 只要结点存在,那么就继续遍历
while(head){
// 如果 flag 已经立过了,那么说明环存在
if(head.flag){
return true;
}else{
// 如果 flag 没立过,就立一个 flag 再往下走
head.flag = true;
head = head.next;
}
}
return false;
};
环形链表 II
/**
* @param {ListNode} head
* @return {ListNode}
*/
const detectCycle = function(head) {
while(head){
if(head.flag){
return head;
}else{
head.flag = true;
head = head.next;
}
}
return null;
};