2.2 链表(上)

链表(上)

链表:一种线性数据结构,通过“指针”将一组零散的内存串联起来,来存储数据。

组成链表各部分的称谓:

  • 节点:每一个内存块称为链表的节点
  • 后继指针 next:记录下一个节点地址的指针
  • 头节点:习惯把第一个节点叫做头节点
  • 尾节点:习惯把最后一个节点叫做尾节点。一个特殊的地方,指针不是指向下一个节点,而是指向一个空地址 NULL,表示这是链表上最后一个节点。

那问题来了,什么是线性表的数据结构呢?使用“指针”串联一组零散内存的意义在那里?为何能支持存储不同类型的数据呢?

问题 回答
什么是线性表的数据结构? 就是每个线表上的数据最多只有前、后两个方向
什么是非线性表的数据结构呢? 不是简单的前后关系
使用“指针”串联一组零散内存的意义 插入、删除操作迅速
为何能支持存储不同类型的数据? 可能是,保存“指针”的内存可以单独“识别”出来。所以,不同节点之间跳转直接遍历节点的内存就好

线性表和非线性表差别,可以通过下图直观的看出:
线性表
非线性表

单链表的具体形式可以看下图:

单链表

单链表的插入、删除、随机访问操作及程序:

单链表的插入、删除
/**
 * 1)单链表的插入、删除、查找操作;
 * 2)链表中存储的是int类型的数据;
 */
class Node {
    constructor (element) {
        this.element = element
        this.next = null
    }
}
class LinkedList {
    constructor () {
        this.head = new Node('head')
    }
    // 根据value查找节点
    findByValue (item) {
        let currentNode = this.head.next
        while (currentNode !== null && currentNode.element !== item) {
            currentNode = currentNode.next
        }
        console.log(currentNode)
        return currentNode === null ? -1 : currentNode
    }

    // 指定元素向后插入
    insert (newElement, element) {
        const currentNode = this.findByValue(element)
        if (currentNode === -1) {
            console.log('未找到插入位置')
            return
        }
        const newNode = new Node(newElement)
        newNode.next = currentNode.next
        currentNode.next = newNode
    }

    // 根据值删除
    remove (item) {
        const prevNode = this.findPrev(item)
        if (prevNode === -1) {
            console.log('未找到元素')
            return
        }
        prevNode.next = prevNode.next.next
    }
}

三种最常见的链表结构:

  • 循环链表

    • 循环链表的尾节点指针指向链表的头节点。

      • 循环链表内存示意图
  • 双向链表

    • 每一个节点支持两个方向:前驱指针prev指向前面的节点,后继指针指向next指针。在消耗更多内存的同时,可以访问前一个节点,更加高效(用空间换时间)。实际开发中,使用的也更加广泛。

      • 双向链表内存示意图
  • 双向循环链表

    • 双向循环链表内存分布图

数组基本功能实现程序

 * 1)单链表的插入、删除、查找操作;
 * 2)链表中存储的是int类型的数据;
 */
class Node {
    constructor (element) {
        this.element = element
        this.next = null
    }
}
​
class LinkedList {
    constructor () {
        this.head = new Node('head')
    }
    // 根据value查找节点
    findByValue (item) {
        let currentNode = this.head.next
        while (currentNode !== null && currentNode.element !== item) {
            currentNode = currentNode.next
        }
        console.log(currentNode)
        return currentNode === null ? -1 : currentNode
    } 

    // 根据index查找节点,下标从0开始
    findByIndex (index) {
        let currentNode = this.head.next
        let pos = 0
        while (currentNode !== null && pos !== index) {
            currentNode = currentNode.next
            pos++
        }
        console.log(currentNode)
        return currentNode === null ? -1 : currentNode
    }
​
    // 向链表末尾追加节点
    append(newElement) {
        const newNode = new Node(newElement)
        let currentNode = this.head
        while(currentNode.next) {
            currentNode = currentNode.next
        }
        currentNode.next = newNode
    }

    // 指定元素向后插入
    insert (newElement, element) {
        const currentNode = this.findByValue(element)
        if (currentNode === -1) {
            console.log('未找到插入位置')
        return
    }
        const newNode = new Node(newElement)
        newNode.next = currentNode.next
        currentNode.next = newNode
    } 

    // 查找前一个
    findPrev (item) {
        let currentNode = this.head
        while (currentNode.next !== null && currentNode.next.element !== item) {
            currentNode = currentNode.next
        }
        if (currentNode.next === null) {
            return -1
        }
        return currentNode
    } 

    // 根据值删除
    remove (item) {
        const prevNode = this.findPrev(item)
        if (prevNode === -1) {
            console.log('未找到元素')
            return
        }
        prevNode.next = prevNode.next.next
    }

    // 遍历显示所有节点
    display () {
        let currentNode = this.head.next // 忽略头指针的值
        while (currentNode !== null) {
            console.log(currentNode.element)
            currentNode = currentNode.next
        }
    }
}
// Test
const LList = new LinkedList()
LList.append('chen')
LList.append('curry')
LList.append('sang')
LList.append('zhao') // chen -> curry -> sang -> zhao
console.log('-------------insert item------------')
LList.insert('qian', 'chen') // 首元素后插入
LList.insert('zhou', 'zhao') // 尾元素后插入
LList.display() // chen -> qian -> curry -> sang -> zhao -> zhou
console.log('-------------remove item------------')
LList.remove('curry')
LList.display() // chen -> qian -> sang -> zhao -> zhou
console.log('-------------find by item------------')
LList.findByValue('chen')
console.log('-------------find by index------------')
LList.findByIndex(2)
console.log('-------------与头结点同值元素测试------------')
LList.insert('head', 'sang')
LList.display() // chen -> qian -> sang -> head -> zhao -> zhou
LList.findPrev('head') // sang
LList.remove('head')
LList.display() // chen -> qian -> sang -> zhao -> zhou

相关章节:链接
相关程序:链接

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。