什么是链表?
- 链表与数组的区别
- 链表是如何实现的
- 链表常见的算法有哪些呢?
什么是链表? 链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素;
单向链表
单向链表包含两个域,一个信息域,一个指针域,也就是单向链表的节点被分成两部分,一部分是保存或显示关于节点的信息,第二部分存储下一个节点的地址,而最后一个节点则指向一个空值。
双向链表
双向链表有2个指针,一个是指向前一个节点的指针,另一个则指向后一个节点的指针。
循环链表就是首节点和末节点被连接在一起。循环链表中第一个节点之前就是最后一个节点,反之亦然。
链表与数组的区别
不同:链表是链式的存储结构,数组是顺序的存储结构 !链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。 链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难; 数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。
相同:两种结构均可实现数据的顺序存储,构造出来的模型呈[线性结构]
那么如何实现一个链表呢
class node {
constructor(element){
this.element = el
this.next = null
}
}
class LinkedList {
constructor() {
this.head = null
this.length = 0
}
append(element) {
let node = new Node(element)
// 处理当链表为空的情况,直接将创建的节点指向指向为head
if (this.head === null) {
this.head = node
} else {
// 找到链表最尾端
let current = this.getNode(this.length - 1)
current.next = node
}
this.length ++
}
insert(position, element) {
let node = new Node(element)
// 处理边界问题
if (position >= this.length || position < 0) {
return
}
// 假如插入位置为0, 直接将node的next指向head, 并把head同步为node
if (position === 0) {
node.next = this.head
this.head = node
}else {
// 同append一样找到链表的最尾端
let pre = this.getNode(position - 1)
// 同步尾端的next与要插入的node的next
node.next = pre.next
// 将尾端的next指向我们要插入的node
pre.next = node
}
this.length++
}
getNode(index) {
// 处理边界值问题
if (index < 0 || index > this.length) {
return
}
// 保存当前的head指针
let current = this.head
// 遍历当前的一个长度,改变指针到尾端
for (let i = 0; i < index; i++) {
current = current.next
}
return current
}
remove(position) {
let current = this.head
// 处理边界值问题
if (position < 0 || position >= this.length) return
// 当下标位置为0的时候,直接将head指向current的next
if (position === 0) {
this.head = current.next
} else {
// 找到要删除的前一位
const pre = this.getNode(position - 1)
// 保存要删除的next以及下面的next
current = pre.next;
// 将要删除的next 指向被删除的前一位的next
pre.next = current.next
}
this.length--
}
indexOf(position) {
}
clear() {
}
}
链表常见的算法
// 反转链表
function reverse(head) {
let current = head
let pre = null
while(current) {
const next = current.next
current.next = pre
pre = current
current = next
}
return pre
}
// 旋转链表
// 环形链表
// 暴力解法,利用js特性
function hasCycle(head) {
try {
JSON.stringify(head)
}
catch {
return true
}
return false
}
// map解法
function hasCycle1(head) {
if(!head || !head?.next) {
return false
}
const map = new Map()
while(head.next) {
if(map.has(head)) {
return true
}
map.set(head, true)
head = head.next
}
return false
}
// K个一组翻转链表
const myReverse = (head, tail) => {
let prev = tail.next;
let p = head;
while (prev !== tail) {
const nex = p.next;
p.next = prev;
prev = p;
p = nex;
}
return [tail, head];
}
var reverseKGroup = function(head, k) {
const 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;
}
}
const nex = tail.next;
[head, tail] = myReverse(head, tail);
// 把子链表重新接回原链表
pre.next = head;
tail.next = nex;
pre = tail;
head = tail.next;
}
return hair.next;
};
// 两两交换链表中的节点
function swapPairs(head) {
if (head === null|| head.next === null) {
return head;
}
const newHead = head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
return newHead;
};
// 删除链表的倒数弟n个结点
function removeNth(head, n) {
let dummy = new ListNode();
dummy.next = head;
let n1 = dummy;
let n2 = dummy;
for (let i = 0; i <= n; i++) {//n2移动n+1次
n2 = n2.next;
}
while (n2 !== null) {//同时移动n1,n2
n1 = n1.next;
n2 = n2.next;
}
n1.next = n1.next.next;//删除元素
return dummy.next;
}
// 删除排序链表中的重复元素
function deleteDuplicates(head) {
if (!head) {
return head;
}
let cur = head;
while (cur.next) {
if (cur.val === cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
};
// 分隔链表
function partition(head, x) {
let small = new ListNode(0);
const smallHead = small;
let large = new ListNode(0);
const largeHead = large;
while (head !== null) {
if (head.val < x) {
small.next = head;
small = small.next;
} else {
large.next = head;
large = large.next;
}
head = head.next;
}
large.next = null;
small.next = largeHead.
}
// 复制带随机指针的链表
function copyRandomList(head) {
if (!head) return head;
let cur = head;
const map = new Map();
// 第一次遍历,生成一个具有val属性的链表;
while (cur) {
map.set(cur, new Node(cur.val))
cur = cur.next
}
//第二次遍历,根据map映射关系,将random和next指针指向对应的节点或者null;
cur = head
while (cur) {
map.get(cur).next = map.get(cur.next) || null
map.get(cur).random = map.get(cur.random) || null
cur = cur.next
}
return map.get(head);
}