JS中的算法与数据结构--链表

208 阅读4分钟

写这篇文章的初衷,是因为上周约了一个线上的面试,面试官问我,你了解数据结构嘛,我很自信的说我会(实际不会想着碰碰就当录面试题库),面试官说,那你说一下怎么判定一个链表有没有环

WX20240512-191241@2x.png 当时人直接定住了,还好是线上尴尬的一批,假装思考一会儿后直接说这个不知道,面试官看这个都不知道就没有再问我了,也是尴尬的一批。

那么既然如此只能复习一下数据链表的题目了,下次再碰的不至于这么尴尬。

什么是链表

让我们以单向链表为例,使用简单的 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指向了 前面的一个节点的话,这里就形成了环,如下图:

1712658d244622c4~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

那如何判定是否存在环呢,我们可以给遍历环的时候给每个节点添加一个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 指针的反转。什么听不明白 上图

1712613307139215~tplv-t2oaga2asx-jj-mark_3024_0_0_0_q75.png

一目了然,这不简简单单易如反掌嘛

老夫我不看图都一把梭 上代码

/**
 * 反转链表
 */

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 指针的反转。到最后一个结点时,整个链表就已经被我们彻底反转掉了。

最后

上面就是一些很基础的链表问题,真的需要了解一下,现在的行情机会都会问一下数据结构和算法这类的,没有碰到算是走运了,但是话说回来我们的确是为了面试儿复习这些,我们无法改变行情市场,那只能改变我们自己。

加油打工人

这些知识我也买的掘金小册看的,这个册子去年买的,到现在快一年了,但是之前没有耐心看完,刚好拿来复习一下,巩固一下自己