写这篇文章的初衷,是因为上周约了一个线上的面试,面试官问我,你了解数据结构嘛,我很自信的说我会(实际不会想着碰碰就当录面试题库),面试官说,那你说一下怎么判定一个链表有没有环
当时人直接定住了,还好是线上尴尬的一批,假装思考一会儿后直接说这个不知道,面试官看这个都不知道就没有再问我了,也是尴尬的一批。
那么既然如此只能复习一下数据链表的题目了,下次再碰的不至于这么尴尬。
什么是链表
让我们以单向链表为例,使用简单的 JavaScript 实现一个链表,并添加一些基本操作,比如插入、删除和打印链表。
// 定义链表节点类
class Node {
constructor(data) {
this.data = data; // 节点数据
this.next = null; // 指向下一个节点的指针,默认为 null
}
}
// 定义链表类
class LinkedList {
constructor() {
this.head = null; // 头节点,默认为 null
}
// 在链表末尾添加节点
append(data) {
const newNode = new Node(data);
if (!this.head) {
this.head = newNode;
} else {
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
}
// 打印链表元素
print() {
let current = this.head;
let result = '';
while (current) {
result += current.data + ' -> ';
current = current.next;
}
result += 'null';
console.log(result);
}
}
// 创建一个链表实例
const linkedList = new LinkedList();
// 向链表中添加元素
linkedList.append(1);
linkedList.append(2);
linkedList.append(3);
// 打印链表
linkedList.print();
这个就是个基础的单向链表,那么我们下面就围绕着这个单向链表来做一下基础的题目,对链表有个基础的了解。
判定一个链表是否存在环
这里解释一下这里的环是什么概念,我们知道一个链表的都有一个next指向下一个节点,如果某一个节点的next指向了 前面的一个节点的话,这里就形成了环,如下图:
那如何判定是否存在环呢,我们可以给遍历环的时候给每个节点添加一个flag表示,如果这个flag已经存在,那么就说明这个链表存在环,多话不讲直接上代码伺候。
/**
* 判断一个链表是否成环 1 > 2 > 3 >4
*
* 假设这里的环是 4 > 2
*/
function hasCycle(head) {
while(head) {
// 如果立过flag 那么就说明存在环
if(head.flag) {
return true
} else {
// 没有立过就 立一个flag 再走下去
head.flag = true
head = head.next;
}
}
return false
}
这么一看是不是觉得这个很easy,要不是被面试官打击我自己也不会看到这里,再次留下技术不精悔恨的泪水!!!
有序排序链表删除重复的元素
链表的删除是一个基础且关键的操作,根据我们上面对链表的了解,删除一个重复的元素,那么就要需要把被删除的元素的前面一项 和 后面一项进行关联,直接判断前后两个元素值是否相等即可。
输入: 1 > 2 > 2 > 3 > 4 > 4 > 5 输出: 1 > 2 > 3 > 4 > 5
来人上代码
const deleteRepeat = (head) => {
let cur = head
while(cur !== null && cur.next !== null) {
if(cur.val === cur.next.val) {
cur.next = cur.next.next
} else {
cur = cur.next
}
}
return cur
}
这里需要注意的是这个链表是有序排序列表
反转一个链表
反转链表看起来很简单,实际做起来也很简单,具体怎么做呢,反转链表顾名思义就是反转链表咯,不开玩笑看图
输入: 1 > 2 > 3 > 4 输出: 4 > 3 > 2 > 1
那么如何实现反转呢,这里我们需要用到三个变量,第一个pre 用来储存 目标的节点的上一个节点, cur节点就是目标节点,还有next节点用来存储目标节点的下一个节点, 这里我只需要一个简单的cur.next = pre,就做到了 next 指针的反转。什么听不明白 上图
一目了然,这不简简单单易如反掌嘛
老夫我不看图都一把梭 上代码
/**
* 反转链表
*/
const reverListNode = function (head) {
let pre = null
let cur = head
while(cur !== null) {
// 记录一下 next 结点
let next = cur.next;
// 反转指针
cur.next = pre;
// pre 往前走一步
pre = cur;
// cur 往前走一步
cur = next;
}
return pre;
}
这里的next一直储存着原始排序的链表结构,pre存储着上一个已经反转的节点节点,从第一个结点开始,每个结点都给它进行一次 next 指针的反转。到最后一个结点时,整个链表就已经被我们彻底反转掉了。
最后
上面就是一些很基础的链表问题,真的需要了解一下,现在的行情机会都会问一下数据结构和算法这类的,没有碰到算是走运了,但是话说回来我们的确是为了面试儿复习这些,我们无法改变行情市场,那只能改变我们自己。
加油打工人
这些知识我也买的掘金小册看的,这个册子去年买的,到现在快一年了,但是之前没有耐心看完,刚好拿来复习一下,巩固一下自己