什么是链表?以及链表的算法

160 阅读3分钟

什么是链表?

  • 链表与数组的区别
  • 链表是如何实现的
  • 链表常见的算法有哪些呢?

什么是链表? 链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素;

单向链表

image.png

单向链表包含两个域,一个信息域,一个指针域,也就是单向链表的节点被分成两部分,一部分是保存或显示关于节点的信息,第二部分存储下一个节点的地址,而最后一个节点则指向一个空值。

双向链表

image.png

双向链表有2个指针,一个是指向前一个节点的指针,另一个则指向后一个节点的指针。

image.png

循环链表就是首节点和末节点被连接在一起。循环链表中第一个节点之前就是最后一个节点,反之亦然。

链表与数组的区别

不同:链表是链式的存储结构,数组是顺序的存储结构 !链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。 链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难; 数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。

相同:两种结构均可实现数据的顺序存储,构造出来的模型呈[线性结构]

那么如何实现一个链表呢

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);
}