• 链表


    本篇博客的所有代码都整理到自己的github库中,以便大家查看及学习。

    本篇文章主要内容有:

    • 链表数据结构
    • 向链表添加元素
    • 从链表移除元素
    • 使用 linkList
    • 双向链表
    • 循环链表
    • 排序链表
    • 通过链表实现栈

    1. 链表的数据结构

    链表存储有序的元素集合,链表中的元素在内存中并不是连续放置的,每个元素都是有一个存储元素本身的节点和一个指向下一个元素的引用(这个引用也称为指针或链接)组成。

    链表.png

    通过这幅图,我们可以更清晰理解链表的数据结构。也从这个图中可以看到,当添加或者移除元素的时候,不需要移动其他的元素,可以改变指针的指向。需要注意的是,链表的表头(head)指向链表第一个元素。

    现实生活中链表的例子也比较常见,最常见的就是火车。

    2. 创建链表

    接下来,我们将会创建一个链表的骨架

    function defaultEquals(a, b) {
        return a === b;
    }
    
    class LinkList{
        constructor(equalsFn = defaultEquals){
            this.count = 0
            this.head = undefined
            this.equalsFn = equalsFn
        }
    }
    
    // 创建链表中的节点
    class Node {
        constructor(element){
            this.element = element
            this.next = undefined
        }
    }
    

    接下来我们会实现以下方法

    • push(elements) : 向链表尾部添加数据
    • insert(element,position) : 向链表中特定位置插入数据
    • getElementAt(index) : 返回指定位置的元素
    • remove(element) : 从链表中移除某一元素
    • indexOf(element) : 返回元素在链表中的索引值,如果没有找到,则返回-1
    • removeAt(position) : 移除链表特定位置的值
    • isEmpty() : 返回链表是否为空
    • size() : 返回链表元素的个数
    • toString(): 返回表示整个链表的字符串,由于链表中使用了Node类,需要重新定义指输出value值的元素方法

    xuxieban.png

    这里需要注意的是: 链表的最后一个节点的下一个元素始终都是undefinednull

    function defaultEquals(a, b) {
        return a === b;
    }
    
    class LinkList{
        constructor(equalsFn = defaultEquals){
            this.count = 0
            this.head = undefined
            this.equalsFn = equalsFn
        }
        push(element){
            const node = new Node(element) // 创建一个节点
            let current
            if (this.head == null) { // 判断链表是否为空 ,如果链表为空,则链表表头指针指向新的节点
                this.head = node
            } else { // 否则,通过遍历的方式将
                current  = this.head
                while (current.next != null) {
                    current = current.next
                }
                current.next = node
            }
            this.count++
        }
        insert(element,position){
            const node = new Node(element)
            // 向链表尾部插入值
            if (position == this.count) {
                this.push(element)
            } else {
                // 向链表头部插入值
                if (position == 0) {
                    const current = this.head
                    this.head = node
                    this.head.next = current
                    this.count++
                }
                // 向链表中间插入值
                if (position > 0 && position < this.count) {
                    let prev 
                    let current = this.head
                    for (let i = 0; i < position; i++) {
                        prev = current
                        current = current.next
                    }
                    prev.next = node
                    node.next = current
                    this.count++
                }
            }
        }
        getElementAt(index){
            if (index <= this.size() && index >= 0) {
               let current = this.head
               for (let i = 0; i < index && current.next != null; i++) {
                  current = current.next
               }
               return current.element
            }
            return undefined 
        }
        remove(element){
           const index = this.indexOf(element)
           this.removeAt(index)
        }
        removeAt(index){
            // 检查越界值
            if (index >= 0 && index < this.count) { // 合法值
                let current = this.head
                if (index == 0) {
                    this.head = current.next
                } else {
                    let prev  // 定义一个值 将下一项值的next
                    for (let i = 0; i < index; i++) {
                        prev = current
                        current = current.next
                    }
                    prev.next = current.next
                }
                this.count --
                return current.element
            }
            return undefined
        }
        indexOf(element){
            let current = this.head
            for (let i = 0; i < this.size() && current != null; i++) {
                if (this.equalsFn(element,current.element)) {
                  return i  
                }
                current = current.next
            }
            return -1
    
        }
        size(){
            return this.count
        }
        toString(){
            let resultString = ''
            let current = this.head
            if (this.head != null) {
                resultString = current.element
                while (current.next != null) { 
                    resultString = `${resultString},${current.next.element}` 
                    current = current.next
                }
            }
            return resultString
        }
        isEmpty(){
            return this.size() === 0
        }
    }
    
    // 创建链表中的节点
    class Node {
        constructor(element){
            this.element = element
            this.next = undefined
        }
    }
    
    var linkList = new LinkList()
    
    linkList.push(1)
    
    linkList.push(2)
    
    linkList.push(3)
    
    linkList.push(4)
    
    /** 
    LinkList {
        count: 4,
        head: Node { element: 1, next: Node { element: 2, next: [Node] } },
        equalsFn: [Function: defaultEquals]
    } 
    **/
    
    console.log(linkList.toString()) // 1,2,3,4
    
    console.log(linkList.indexOf(2))  // 1
    
    console.log(linkList.indexOf(5)) // -1
    
    linkList.remove(2)
    
    console.log(linkList.toString()) // 1,3,4
    
    console.log(linkList.size()) // 3
    
    linkList.insert(2,0) 
    
    console.log(linkList)
    
    console.log(linkList.toString())  // 2,1,3,4
    
    linkList.insert(5,2)
    
    console.log(linkList.toString()) // 2,1,5,3,4
    
    linkList.insert(6,5)
    
    console.log(linkList.toString()) // 2,1,5,3,4,6
    
    

    这里的重点是理解代码中的添加和移除元素

    3. 双向链表

    双向链表和普通链表的区别在于在链表中一个节点只有链向下一个节点的链接,而双向链表中,链接是双向的,一个链接指向下一个节点的链接,一个链接指向上一个节点的链接。如下图所示

    双向链表.jpg

    我们这次在链表的基础上扩展一下双向链表的方法

    class DoubleLinkList extends LinkList {
        constructor(equalsFn = defaultEquals){
            super(equalsFn)
            this.tail = undefined
        }
    }
    

    这里的defaultEquals方法在创建链表时有定义

    双向链表中的DoublyNode也是在链表中Node的基础上继承扩展的

    class DoublyNode {
        constructor(element){
            super(element,next)
            this.prev = undefined
        }
    }
    

    4.双向链表的方法

    创建的双向链表的类中的注释方法可以理解为上述方法的第二种方法。双向链表中重点掌握在任意位置插入和移除元素就可以了。

    class DoubleLinkList extends LinkList {
        constructor(equalsFn = defaultEquals){
            super(equalsFn)
            this.tail = undefined
        }
        push(element){
            const node = new DoublyNode(element)
            if (this.head == null) {
                this.head = node
                this.tail = node
            } else {
                node.prev = this.tail
                this.tail.next = node
                this.tail = node
            }
        }
        insert(element,index){
            if (index == this.count) { // 在链表尾部追加
                this.push(element)
            } else {
                const node = new DoublyNode(element)
                // 在链表头部追加
                if (index == 0) {
                    if (this.head == null) { // 链表为空
                        this.head = node
                        this.tail = node
                    } else {
                        const firstNode = this.head
                        this.head = node
                        firstNode.prev = node
                        node.next = firstNode 
                    }
                }
                // 在链表中间插入值,
                const prevItem = this.getElementAt(index -1) // 拿到的前一项
                let current = prevItem.next
                prevItem.next = node
                node.next = current
                node.prev = prevItem
                current.prev = node
    
                this.count++
                return true
                // let current = this.head
                // let prev
                // for (let i = 0; i < index && index < this.count; i++) {
                //    prev = current
                //    current = current.next
                // }
                // prev.next = node
                // node.next = current
            }
            return false
        }
        indexOf(element){
            if (this.head == null) {
                return -1
            }
            let current = this.head
            let index = 0
            while (current.next != null) {
                if (this.equalsFn(current.element , element)) {
                   return index
                }
                index ++
                current = current.next
            }
        }
        // getElementAt(index){ //查找所在位置的元素,和链表中的方法一样
        //     if (this.head == null) {
        //         return undefined
        //     }
        //     let current = this.head
        //     let prev
        //     for (let i = 0; i < index; i++) {
        //         current = current.next
        //     }
        //     return current
        // }
        removeAt(index){ // 从任意位置移除元素
            if (index >= 0 && index < this.count) {
                let current 
                if (index == 0) { // 移除双向链表头部的元素
                    current = this.head
                    this.head = current.next
                } else if (index == this.count - 1) { // 移除双向链表尾部的元素
                    current = this.tail
                    this.tail = current.prev
                    this.tail.next = undefined
                } else { // 移除链表中间的元素,可用getElementAt()的方法
                    let current = this.getElementAt(index )
                    let prevItem = current.prev
                    prevItem.next = current.next
                    current.next.prev = prevItem
                    // let prev
                    // for (let i = 0; i < index ; i++) {
                    //     prev = current
                    //     current = current.next
                    // }
                    // prev.next = current.next
                    // current.next.prev = prev
                }
                this.count --
                return current.element
            }
            return undefined
        }
        getHead(){
            return this.head
        }
        getTail(){
            return this.tail
        }
        clear(){
            super.clear()
            this.tail = undefined
        }
        toString(){}
        inverseToString(){}
    }
    

    5. 循环链表

    循环链表和链表唯一的区别就在于 最后一个元素指向下一个元素的指针,最后一个元素的tail.next指向的是第一个元素(head),如下图所示,看图理解会更容易一些

    循环链表.jpg

    接下来我们创建一个循环链表的类circleLinkList,circleLinkList类仍是继承基础链表的某些方法

    import LinkList from './Linklist'
    import DoublyNode from './DoublyNode'
    import { defaultEquals } from '../utils/index'
    class CircleLinkList extends LinkList{
        constructor(equals = defaultEquals){
            super(equals)
        }
        push(element){
            const node = new DoublyNode(element)
            if (this.head == null) {
                this.head = node
            } else {
                let current = this.getElementAt(this.size() - 1)
                current.next = node
                this.tail = node
                node.next = this.head
            }
        }
        insert(element,index){
           const node = new DoublyNode(element)
           if (index > 0 && index <= this.count) {
                if (this.head == null) {
                    this.head = node 
                    node.prev = this.head
                } else {
                    if (index == this.count) { // 最后一个元素
                        let lastItem = this.getElementAt(this.count - 1)
                       lastItem.next = node
                       node.prev = this.head
                       node.next = undefined
                    } else {
                        // 插入到链表中间位置
                        let prevItem = this.getElementAt(index - 1)
                        let current = prevItem.next
                        prevItem.next = node
                        node.prev = prevItem
                        node.next = current
                    }
                }
                this.count ++ 
                return true
           }
           return false
             
        }
        removeAt(index){
            if (this.head = null) {
                return undefined
            }
            let prevItem = this.getElementAt(index -1) // 前一个元素
            const result = prevItem.next
            prevItem.next = result.next
            result.prev = prevItem
            return result
        }
    }
    

    6. 有序链表

    有序链表是值保持元素有序的链表结构,除了使用排序的算法外,还可以将元素插入到正确的位置确保链表的有序性

    这里不再创建有序链表类了,有需要的可以自己尝试创建一下。

    7. 通过链表表示栈

    用双向链表更容易表示栈的数据结构,双向数据链表可以直接获取头尾元素,减少过程的消耗,他的时间复杂度和原始的Stack类实现相同,为O(1)

  • 相关阅读:
    dgango
    django
    pymysql 增删改 查 索引
    mysql 单表,多表,符合条件,子查询
    mysql 数据的增删改
    mysql foreignkey
    mysql基础知识之数据类型与约束
    MySol序
    MySql
    Python Selenium 常用方法总结
  • 原文地址:https://www.cnblogs.com/bllx/p/15967202.html
Copyright © 2020-2023  润新知