【数据结构】手写链表

307 阅读2分钟

目标:

使用js实现一个链表结构。

学习资料:

juejin.cn/post/708869…

b23.tv/zdfocdg

链表特点

  • 有序;
  • 内存中无序;
  • 移动、删除只需要改变前后指针;

链表属性、方法

属性:size、head、tail

方法:append、appendAt、removeAt、indexOf...

image.png

链表类型

单向链表、单向循环链表、双向链表、双向循环链表、环形链表

单向链表

先实现一个Node类,表示链表中的一个元素,包含两个属性 :链表节点的值和next指针。

class Node {
    constructor(v) {
        this.value = v;
        this.next = null;
    }
}

再实现一个linkedList类。

class LinkedList {
    constructor() {
        this.size = 0;
        this.head = null;
    }
    
    // 链表末尾追加元素
    append(){}
    
    // 链表任意位置 添加元素
    appendAt(){}
    
    // 删除指定位置元素
    removeAt(){}
    
    // 查找元素索引
    indexOf(){}
}

添加元素

image.png

append(v) {
    let node = new Node(v);
    if (this.head === null) {
        this.head = node
    } else {
        let cur = this.getNode(this.size - 1)
        cur.next = node
    }
    this.size++
}

getNode(index) {
    if (index < 0 || index >= this.size) {
        throw new Error('out range')
    }
    let cur = this.head
    for (var i = 0; i < index; i++) {
        cur = cur.next
    }
    return cur

}

在指定位置添加元素

image.png

appendAt(i, v) {
    if (i < 0 || i > this.size) {
        throw new Error('out range')
    }
    let node = new Node(v)
    if (i === 0) {
        node.next = this.head
        this.head = node
    } else {
        let prev = this.getNode(i - 1)
        node.next = prev.next
        prev.next = node
    }
    this.size++
}

根据索引移除元素

image.png

removeAt(i) {
    if (i < 0 || i >= this.size) {
        throw new Error('out range')
    }
    if (i === 0) {
        this.head = this.head.next
    } else {
        let prev = this.getNode(i - 1);
        prev.next = prev.next.next
    }
    this.size--;
}

实现indexOf功能

indexOf(v) {
    let cur = this.head
    for (var i = 0; i < this.size; i++) {
        if (cur.value === v) {
        return i
        }
        cur = cur.next
    }
    return -1
}

完整代码

class Node {
constructor(v) {
        this.value = v;
        this.next = null;
    }
}

class LinkedList {
    constructor() {
        this.size = 0;
        this.head = null;
    }

    append(v) {
        let node = new Node(v);
        if (this.head === null) {
            this.head = node
        } else {
            let cur = this.getNode(this.size - 1)
            cur.next = node
        }
        this.size++
    }

    getNode(index) {
        if (index < 0 || index >= this.size) {
            throw new Error('out range')
        }
        let cur = this.head
        for (var i = 0; i < index; i++) {
            cur = cur.next
        }
        return cur
    }

    appendAt(i, v) {
        if (i < 0 || i > this.size) {
            throw new Error('out range')
        }
        let node = new Node(v)
        if (i === 0) {
            node.next = this.head
            this.head = node
        } else {
            let prev = this.getNode(i - 1)
            node.next = prev.next
            prev.next = node
        }
        this.size++
    }

    removeAt(i) {
        if (i < 0 || i >= this.size) {
            throw new Error('out range')
        }
        if (i === 0) {
            this.head = this.head.next
        } else {
            let prev = this.getNode(i - 1);
            prev.next = prev.next.next
        }
        this.size--;
    }

    indexOf(v) {
        let cur = this.head
        for (var i = 0; i < this.size; i++) {
            if (cur.value === v) {
                return i
            }
        cur = cur.next
        }
        return -1
    }
}

单向循环链表

image.png

class CircLinkedList extends LinkedList{

    append(v){
        const node = new Node(v);
        if(this.head === null){
            this.head = node;
            node.next = this.head;
        }else{
            let cur = this.getNode(this.size - 1)
            cur.next = node ;
            node.next = this.head;
        }
    }
    
    appendAt(i,v){
        if (i < 0 || i > this.size) { 
            throw new Error('out range') 
        }
        let node = new Node(v) 
        if (i === 0) {
            if(this.head===null){
                this.head = node 
                node.next = this.head
            }else{
                node.next = this.head
                this.head = node
                this.getNode(this.size - 1).next = this.head;
            }
        } else {
            let prev = this.getNode(i - 1) 
            node.next = prev.next 
            prev.next = node 
        }
        this.size++
    }
    
    removeAt(i) {
        if (i < 0 || i >= this.size) {
            throw new Error('out range')
        }
        if (i === 0) {
            ifthis.size === 1){
                this.head = null
            }else{
                const tail = this.getNode(this.size - 1)
                this.head = this.head.next;
                tail.next = this.head
            }
        } else {
            let prev = this.getNode(i - 1);
            prev.next = prev.next.next
        }
        this.size--;
    }
}

双向链表

image.png

// 双向链表

class DoubleLinkedList {
    constructor() {
        this.size = 0;
        this.head = null;
        this.tail = null
    }

    [Symbol.iterator]() {
        let cur = this.head;
        return {
            next() {
                if (cur) {
                    let res = cur
                    cur = cur.next;
                    return {
                    done: false, value: res.value
                    }
                } else {
                    return {
                    done: true, value: null
                    }
                }
            }
        }
    }

    append(v) {
        let node = new DoubleNode(v);
        if (this.head === null) {
            this.head = node
            this.tail = node
        } else {
            let cur = this.tail
            cur.next = node
            node.prev = cur
            this.tail = node
        }
        this.size++
    }

    getNode(index) {
        if (index < 0 || index >= this.size) {
            throw new Error('out range')
        }
        let cur = this.head
        for (var i = 0; i < index; i++) {
            cur = cur.next
        }
        return cur
    }

    appendAt(i, v) {
        if (i < 0 || i > this.size) {
            throw new Error('out range')
        }
        let node = new DoubleNode(v)
        if (i === 0) {
            node.next = this.head
            this.head.prev = node
            this.head = node
        } else if (i === this.size) {
            node.prev = this.tail
            this.tail.next = node
            this.tail = node
        }
        else {
            let prev = this.getNode(i - 1)
            node.next = prev.next
            node.prev = prev
            node.next.prev = node
            prev.next = node
        }
        this.size++
    }

    removeAt(i) {
        if (i < 0 || i >= this.size) {
            throw new Error('out range')
        }
        if (i === 0) {
            this.head = this.head.next
            this.head.prev = null
        } else if (i === this.size - 1) {
            this.tail = this.tail.prev
            this.tail.next = null
        } else {
            let prev = this.getNode(i - 1);
            prev.next = prev.next.next
            prev.next.prev = prev
        }
        this.size--;
    }

    indexOf(v) {
        let cur = this.head
        for (var i = 0; i < this.size; i++) {
            if (cur.value === v) {
                return i
            }
            cur = cur.next
        }
        return -1
    }
}

双向循环链表

image.png


// 双向循环链表

class DoubleCircLinkedList extends DoubleLinkedList {
    append(v) {
        let node = new DoubleNode(v);
        if (this.head === null) {
            this.head = node
            this.head.prev = this.head
            this.head.next = this.head
        } else {
            let cur = this.tail
            cur.next = node
            node.prev = cur
            node.next = this.head
            this.head.prev = node
        }
        this.tail = node
        this.size++
    }
    
    appendAt(i, v) {
        if (i < 0 || i > this.size) {
            throw new Error('out range')
        }
        let node = new DoubleNode(v)
        if (i === 0) {
            node.next = this.head
            this.head.prev = node
            this.head = node
        } else if (i === this.size - 1) {
            let cur = this.tail
            cur.next = node
            node.prev = cur
            node.next = this.head
            this.head.prev = node
            this.tail = node
        }
        else {
            let prev = this.getNode(i - 1)
            node.next = prev.next
            node.prev = prev
            node.next.prev = node
            prev.next = node
        }
        this.size++
    }
    
    removeAt(i) {
        if (i < 0 || i >= this.size) {
            throw new Error('out range')
        }
        if (i === 0) {
            this.head = this.head.next
            this.head.next.prev = this.head
            this.head.prev = this.tail
            this.tail.next = this.head
        } else if (i === this.size - 1) {
            this.tail = this.tail.prev
            this.tail.next = this.head
            this.head.prev = this.tail
        } else {
            let prev = this.getNode(i - 1);
            prev.next = prev.next.next
            prev.next.prev = prev
        }
        this.size--;
    }
}