单向链表 -- 不好意思,我只单向行驶

257 阅读3分钟

1、何为链表

顾名思义,所谓链表

  • 首先它得是个表,用来存储数据
  • 其次它是用链条把这些数据链接起来

所以链表就是存储着有序的元素集合,链表中的每一个元素由元素本身的节点指向下一个元素的指针 组成

img

它的第一个节点我们称之为头节点,最后一个节点为尾节点,并且尾节点的next 指针指向null(为空)

2、链表 VS 数组

数组我们是再熟悉不过了,顺序存储,按照索引来访问

所以,如果说数组在内存中是顺序存储的话,那么链表在内存中就是随机存储,然后通过链条来把这些零散的节点连接起来

所以在存储上:

  • 数组在内存中是占用了连续完整的存储空间
  • 链表则是零散分,充分利用碎片空间

基于它们的结构特点,数组的优势在于:能够快速定位,访问比较迅速;而链表每次都需要从头部节点开始查找

链表的优势是能够灵活地进行插入和删除操作,每次只需要找到要插入的节点位置,修改 next 指针的指向即可;而数组进行删除或者插入操作的时候,其后面的元素都需要变动。

如:一排座椅坐着一些人,每个人就是紧挨着,这时他们的boss来了,要坐C位,那么C位后面的人都得往后挪一个位置,那太也麻烦了。设想一下,如果是一个庞大的数据,需要频繁进行增删改查的操作,用数组那效率得有多低的,估计修改个数据都得半天

所以,如果需要频繁进行插入和删除元素的,用链表更合适些;对于读取多的,操作少的场景来说,那数组更合适些

3、创建链表

链表分给单向链表、双向链表、循环链表 和 环形链表

上面所示的就会是单向链表

双向链表

img

循环链表

img

今天我们就先来讨论一下单向链表,我们用类来实现一下链表,如果对类不熟悉的话,可以看下 面向对象 这篇文章,了解个一二,理解下面就某得问题了

首先我们先创建一个节点对象,当我们需要创建节点的时候,就实例一个节点对象

 class Node {
     constructor(element) {
         this.element = element;
         this.next = undefined;
     }
 }

链表的几个主要的功能(增删改查):

  1. 向链表尾部添加节点
  2. 删除节点
  3. 在链表任意位置添加节点
  4. 修改链表节点
  5. 遍历打印链表的节点值

其结构如下:

// 创建一个链表,先定义其表头、节点个数
class LinkList {
    constructor() {
        this.count = 0; // 节点个数
        this.head = null; // 表头
    }
    
    // 根据位置查找节点
    getElement(index) {}
    
    // 向链表尾部添加元素
    pull(element) {}
    
    // 根据位置从链表中移除元素
    removeAt(index) {}
    
    // 在任意位置插入元素  元素+位置
    insert(element, index) {}
    
    // 遍历显示当链表
	display() {}
    
    // 返回一个元素的位置
    indexof(element) {}
    
    // 根据节点从链表中移除元素
    remove(element) {}
    
    // 将对象转换为字符串
    toString() {}
}

根据位置查找节点,将它疯转成一个方法,后面会经常调用

getElement(index) {
    if (index >= 0 && index < this.count) {
        let curret = this.head;
        for (let i = 0; i < index && curret != null; i++) {
            curret= curret.next
        }
        return curret
    }
    return undefined
}

向链表尾部添加元素

pull(element) {
    // 创建节点
    let node = new Node(element);
    let current;
    // 如果链表为空,直接令添加的节点为头节点
    if (this.head === null) {
        this.head = node;
    } else {
        // 查找链表的最后一个元素
        current = this.getElement(this.count - 1)
        current.next = node
    }
    this.count++;
}

从链表中删除一个元素

removeAt(index) {
    if (index >= 0 && index < this.count) {
        let current = this.head
        // 如果删除的是头部节点
        if (index === 0) {
            // this代表链表,其头部this.head等于下一个值
            this.head = current.next
        } else {
            // 获取要删除元素的前一个元素
            let previous = this.getElement(index - 1);
            // 删除的元素
            current = this.getElement(index)
            // 删除元素
            previous.next = current.next
        };
        // 删除一个元素记得要将节点个数-1
        this.count--;
        return current.element;
    }
    return undefined
}

在任意位置插入元素

insert(element, index) {
    let node = new Node(element);
    if (index >= 0 && index <= this.count) {
        let current = this.head
        // 如果是插入在第一个
        if (index === 0) {
            node.next = current
            this.head = node
        } else {
            // 获取插入的前一个节点
            let previous = this.getElement(index - 1);
            // 让插入节点指向下一个节点
            node.next = previous.next
            // 前一个节点指向下一个节点
            previous.next = node
        }
        this.count++;
        return true
    }
    return undefined
}

修改链表节点

modify(index, newElemnet) {
    if (index >= 0 && index < this.count) {
        // 如果修改的是第一个值
        if (index === 0) {
            this.head.element = newElemnet
        } else {
            let node = this.getElement(index);
            node.element = newElemnet
        }
        return newElemnet
    }
    return undefined
}

遍历显示链表

// 方法一 for循环
display() {
    if (this.head === null) {
        return undefined
    }
    var result = ''
    for (let i = 0; i < this.count; i++) {
        let current = this.getElement(i)
    	result += current.element
        if (i < this.count - 1) {
            result += '->'
        }
    }
    return result
}

// 方法二 while循环
display() {
    if (this.head === null) {
        return undefined
    }
    var result = ''
    let current = this.head
    while (current) {
        result += current.element;
        current = current.next;
        if (current) {
            result += '->'
        }
    }
    return result
}

根据节点从链表中删除元素

// 返回一个元素的位置
indexOf(element) {
    for (let i = 0; i < this.count; i++) {
        let current = this.getElement(i);
        if (element === current.element) {
            return i
        }
    }
    return -1
}

// 从链表中删除元素
remove(element) {
    // 获取该元素的节点
    let index = this.indexOf(element)
    // 调用根据节点位置删除节点的函数
    return this.removeAt(index)
}

将对象转换为字符串

toString() {
    if (this.head === null) {
        return ''
    }
    let objString = this.head.element;
    let current = this.head.next;
    for (let i = 1; i < this.count; i++) {
        // 拼接节点的值
        objString += ',' + current.element
        current = current.next
    }
    return objString
}

单链表的功能基本实现了,整个一下

// 创建节点
class Node {
    constructor(element) {
        this.element = element;
        this.next = undefined;
    }
}

// 创建一个链表
class LinkList {
    constructor() {
        this.count = 0; // 链表个数
        this.head = null; // 表头
    }

    getElement(index) {
        if (index >= 0 && index < this.count) {
            let node = this.head;
            for (let i = 0; i < index && node != null; i++) {
                node = node.next
            }
            return node
        }
        return undefined
    }

    // 向链表尾部添加元素
    pull(element) {
        // 创建节点
        let node = new Node(element);
        let current;
        if (this.head === null) {
            this.head = node;
        } else {
            // 查找链表的最后一个元素
            current = this.getElement(this.count - 1)
            current.next = node
        }
        this.count++;
    }

    // 从链表中删除一个元素
    removeAt(index) {
        if (index >= 0 && index < this.count) {
            let current = this.head
            if (index === 0) {
                // this代表链表,其头部this.head等于下一个值
                this.head = current.next
            } else {
                // 获取要删除元素的前一个元素
                let previous = this.getElement(index - 1);
                // 删除的元素
                current = this.getElement(index)
                    // 删除元素
                previous.next = current.next
            };
            this.count--;
            return current.element;
        }
        return undefined
    }

    // 在任意位置插入元素
    insert(element, index) {
        let node = new Node(element);
        if (index >= 0 && index <= this.count) {
            let current = this.head
            if (index === 0) {
                node.next = current
                this.head = node
            } else {
                let previous = this.getElement(index - 1);
                node.next = previous.next
                previous.next = node
            }
            this.count++;
            return true
        }
        return undefined
    }

    // 修改链表节点
    modify(index, newElemnet) {
        if (index >= 0 && index < this.count) {
            if (index === 0) {
                this.head.element = newElemnet
            } else {
                let node = this.getElement(index);
                node.element = newElemnet
            }
            return newElemnet
        }
        return undefined
    }

    // 遍历显示链表
    display() {
        if (this.head === null) {
            return undefined
        }
        var result = ''
        let current = this.head
        while (current) {
            result += current.element;
            current = current.next;
            if (current) {
                result += '->'
            }
        }
        return result
    }

    // 返回一个元素的位置
    indexOf(element) {
        for (let i = 0; i < this.count; i++) {
            let current = this.getElement(i);
            if (element === current.element) {
                return i
            }
            // current = current.next
        }
        return -1
    }

    // 从链表中删除元素
    remove(element) {
        let index = this.indexOf(element)
        return this.removeAt(index)
    }

    // 判断链表是否为空
    isEmpty() {
        return this.head === null
    }

    // 返回链表长度
    size() {
        return this.count;
    }

    // 返回尾部节点
    getLast() {
        return this.getElement(this.count - 1)
    }

    // 清空链表
    clear() {
        this.head = null
        this.count = 0
        return true
    }

    // 将对象转换为字符串
    toString() {
        if (this.head === null) {
            return ''
        }
        let objString = this.head.element;
        let current = this.head.next;
        for (let i = 1; i < this.count; i++) {
            objString += ',' + current.element
            current = current.next
        }
        return objString
    }

}

// 实例化链表对象
let l = new LinkList();
// 尾部添加节点
l.pull(1)
l.pull(2)
l.pull(3)
l.pull(4)
    // 插入节点
l.insert(5, 0)
l.insert(6, 5)
    // 打印整个链表(以目录的形式)
console.dir(l, {
    depth: 100
});
// 删除位置为0的节点 返回该值
console.log(l.removeAt(0)); // 5
// 删除值为1的节点 返回该值
console.log(l.remove(1));  // 1
// 遍历该节点
console.log(l.display());  // 2->3->4->6
//最后一个节点
console.log(l.getLast()); // Node { element: 6, next: undefined }
// 修改节点
console.log(l.modify(2, 100));  // 100

console.log(l.display()); // 2->3->100->6
// 转化为字符串
console.log(l.toString()); // 2,3,100,6

拿走不客气哈!