链表-线性表

链表–线性表

image-20210818132523475

一、线性表定义
零个或多个数据元素的有限序列
1. 抽象数据类型定义:
ADT list
Data
    集合{a1,a2,a2..an},有限,每个元素的数据类型都为DataType,其中,除第一个元素a1外,每个元素有且只有一个直接前驱元素,除了最后一个元素an外,每个元素有且只有一个直接后继元素。数据元素间是一对一的关系!
Operation
    InitList(*L)初始化操作,建立一个空的线性表L
    ListEmpty(L)若线性表为空,返回了true,否则返回false
    ClearList(*L)清空线性表
    GetElem(L,i,*e) 将线性表L的第i个位置元素值返回给e
    LocateElem(L,e) 在线性表L查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号,失败则返回0
    ListInsert(*L,i,e)在线性表L的第i个位置,插入新元素e
    ListDelete(*L,i,e)删除线性表L中第i位置元素,并用e返回其值
    ListLength(L)  返回线性表L的元素个数
endADT

在实际开发中,更为复杂的操作,都可使用以上组合实现!!!
二、顺序存储结构–数组
线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

image-20210818104323539

在线性表的某个位置存入或者取出数据,时间复杂度:O(1)

插入和删除:时间复杂度:O(n)
1. 数组插入

image-20210818110911961

2. 数组删除

image-20210818110929789

3.数组优缺点

image-20210818111043002

三、链式存储结构

使用链表的原因:

  • 数组创建需要一段连续的空间(一整块内存),并且大小是固定的。
  • 在数组的开头或中间插入数据的成本很高,需要进行大量元素的位移!
  • 在 JavaScript 中的 Array 类方法背后原理依旧如此!

链表的优势:

  • 内存空间不必连续,可以充分利用计算机内存,实现灵活的内存动态管理
  • 创建时,不必确定大小,并且可以无限延伸下去
  • 插入和删除数据时,时间复杂度可以达到 O ( 1 ) O(1) O(1) ,对比数组高效很多

链表的缺点:

  • 访问任何一个位置的元素,都必须从开头开始访问(无法跳过第一个元素访问任何一个元素)
  • 无法通过下标直接访问元素,必须一个个寻找!

瑕不掩瑜–链表很有必要:

  • 链式存储是解决顺序存储的解决方案!各有优劣,结合实际开发使用!
头指针:指的是第一个结点的物理地址!如果有头结点,指向的便是头结点的物理地址!
头结点:方便对第一个元素统一操作的预设结点,数据域一般不含数据(可放公共信息)

image-20210921003011387

image-20210818113142824

1.单链表
  1. 基本思路

    head–头指针

    Node–节点

    • data next
      // 单链表封装
      function LinkedList() {
        // 内部类:节点类,暂时不传下一节点的指向地址
        function Node(data) {
          this.data = data
          this.next = null
        }
        // 属性
        this.head = null  //对应第一个元素
        this.length = 0
      }
    
    • 常见操作

    image-20210921172244286

  2. 实现代码

      // 单链表封装
      function LinkedList() {
    
        // 内部类:节点类,暂时不传下一节点的指向地址
        function Node(data) {
          this.data = data
          this.next = null
        }
    
        // 属性
        this.head = null
        this.length = 0
    
        // 1.追加方法:
        LinkedList.prototype.append = function (data) {
          // 1.创建新的节点
          var newNode = new Node(data)
    
          // 2.判断是否添加的是第一个节点
          if (this.length == 0) {
            // 2.1 是第一个节点
            this.head = newNode
          } else {
            // 2.2不是第一个节点,找到最后一个节点
            var current = this.head
            while (current.next) {
              current = current.next
            }
    
            // 最后节点的next指向新的节点
            current.next = newNode
          }
    
          //3.链表长度加 1
          this.length += 1
        }
    
        // 2. toString 方法
        LinkedList.prototype.toString = function () {
          // 1.定义变量
          var current = this.head
          var listString = ""
    
          // 2. 循环获取一个个的节点数据
          while (current) {
            listString += current.data + " "
            current = current.next
          }
    
          return listString
        }
    
        // 3. insert方法  position 插入的位置,data插入的数据
        LinkedList.prototype.insert = function (position,data) {
          // 1. 做越界判断 0 -- length
          if (position < 0 || position > this.length) {
            return false
          }
    
          // 2.根据 data 创建newNode
          var newNode = new Node(data)
    
          // 3.判断插入的位置是否是第一个
          if (position == 0) {
            newNode.next = this.head
            this.head = newNode
          } else {
            // 不是第一个位置
            // 找到目标位置,循环,需要两个变量,一个 current 一个 previous
            var current = this.head
            var previous = null
    
            for (var i = 0; i < position; i++) {
              previous = current
              current = current.next
            }
            newNode.next = current
            previous.next = newNode
          }
          // 4. length + 1
          this.length += 1
          return true
        }
    
    
        // 4. get方法
        LinkedList.prototype.get = function (position) {
          // 1.越界判断
          if (position <= 0 || position > this.length) {
            return null
          }
          // 2.获取对应的data
          var current = this.head
          var index = 1
          while (index++ < position) {
            current = current.next
          }
          return current.data
        }
    
        // 5.indexOf 返回下标的索引
        LinkedList.prototype.indexOf = function (data) {
          // 1.定义变量
          // this.head 就是 第一个元素第一个节点
          var current = this.head
          var index = 0
    
          // 2. 开始查找
          while (current) {
            if (current.data == data) {
              return index
            }
            current = current.next
            index += 1
          }
          // 3.找到最后没有找到 返回-1
          return -1
    
        }
    
    
        // 6.update
        LinkedList.prototype.update = function (position,newData) {
          // 1.越界判断
          if (position < 0 || position >= this.length) return false
    
          // 2. 查找正确的节点
          var current = this.head
          var index = 0
          while (index++ < position) {
            current =current.next
          }
          // 3. 将 position 位置的 node 的 data 修改成 newData
          current.data = newData
          return true
        }
    
        // 7. removeAt
        LinkedList.prototype.removeAt = function (position) {
          // 1. 越界判断
          if (position < 0 || position >= this.length) return null
    
          // 2. 判断是否删除的是第一个结点
          var current = this.head
          if (position == 0) {
            this.head = this.head.next
          } else {
            var index = 0
            var previous = null
            while (index++ < position) {
              previous = current
              current = current.next
            }
    
            // 前一个结点的 next 指向,current 的 next 即可
            previous.next = current.next
          }
    
          // 3. length - 1
          this.length -= 1
    
          return current.data
        }
    
        // 8.remove 方法
        LinkedList.prototype.remove = function (data) {
          // 1. 获取 data 在列表的位置
          var position = this.indexOf(data)
    
          // 2.根据位置信息,删除节点
          return this.removeAt(position)
        }
    
        // 9. isEmpty
        LinkedList.prototype.isEmpty = function () {
          return this.length == 0
        }
    
        // 10. size 方法
        LinkedList.prototype.size = function () {
          return this.length
        }
    
    
      }
    
     // 测试代码
      var list = new LinkedList()
    
      //2. 测试 append 方法
      list.append('abc1')
      list.append('cba3')
      list.append('bca2')
      // list.append('acb8')
      alert(list)
    
    
      // 3.测试 insert 方法
      list.insert(0,'aaa')
      list.insert(3,'bbb')
      list.insert(5,'ccc')
      alert(list)
    
      // 4.测试 get 方法 aaa abc1 cba3 bbb bca2 ccc
      // 目前是从 0 开始计算的 应该从1才对
      // alert(list.get(1))
      // alert(list.get(5))
      // alert(list.get(6))
      //
      // // 5. 测试 indexOf
      // alert(list.indexOf('aaa'))
      // alert(list.indexOf('bbb'))
      // alert(list.indexOf('ccc'))
    
      // 6. 测试 update 方法,根据下标的索引修改
      // list.update(0,'mmm')
      // list.update(2,'nnn')
      // alert(list)
    
      // 7.removeAt 按照索引下标删除 元素
      // list.removeAt(5)
      // alert(list)
      // list.removeAt(0)
      // alert(list)
      // list.removeAt(2)
      // alert(list)
    
      // 8.测试 remove
      list.remove('aaa')
      alert(list)
      list.remove('ccc')
      alert(list)
    
      // 9. 10. 判断是否为空,获取长度
      alert("长度:"+list.size())
      alert("是否为空:"+list.isEmpty())
    
    
  3. 单链表分析

    单向链表

    • 只能从头到尾遍历或者尾到头

    • 整个链表的相连过程是单向的,上一个链表中有下一个指向的引用

    • 可以轻松到达下一个节点,但是回到前一个结点是很难的

    双向链表

    • 可从头遍历到尾,又可以从尾到头遍历
    • 相连过程是双向过程
    • 具有向前和向后连接的引用
    • 插入或删除某节点时,需要处理四个引用,相对单向链表,占用内存空间更大

    image-20210818131222572

2. 双向链表

既有直接前驱指针,又有直接后继指针,中间放数据域

图示:image-20210921191156516

image-20210921191214819

  1. 基本思路

    有 前后指针,数据三部分组成,头指针 head、尾指针 tail

    • 其实和单向链表差不多,只是内部的实现不太一样

    常见操作

    image-20210921193331624

  2. 实现代码

      // 封装双向链表
      function DoublyLinkedList() {
        // 属性
        function Node(data) {
          this.data = data
          this.prev = null
          this.next = null
        }
    
        this.head = null
        this.tail = null
        this.length = 0
    
        // 1. append 方法
        DoublyLinkedList.prototype.append = function (data) {
          // 1.根据 data 创建节点
          var newNode = new Node(data)
    
          // 2. 判断添加的是否是第一个节点
          if (this.length == 0) {
            this.head = newNode
            this.tail = newNode
          } else {
            newNode.prev = this.tail
            this.tail.next = newNode
            this.tail = newNode
          }
          // 3. length + 1
          this.length += 1
        }
    
        // 2. 将链表转成字符串形式
        // 2.1 toString
        DoublyLinkedList.prototype.toString = function () {
          return this.backwardString()
        }
    
        // 2.2 forwardString 方法
        DoublyLinkedList.prototype.forwardString = function () {
          // 向前遍历
          // 1. 定义变量
          var current = this.tail
          var resultString = ""
    
          // 2. 依次向前遍历,获取每一个节点
          while (current) {
            resultString += current.data + " "
            current = current.prev
          }
    
          return resultString
        }
    
        // 2.3 backwardString
        DoublyLinkedList.prototype.backwardString = function () {
          // 向后遍历
          // 1. 定义变量
          var current = this.head
          var resultString = ""
    
          // 2. 依次向后遍历,获取每一个节点
          while (current) {
            resultString += current.data + " "
            current = current.next
          }
    
          return resultString
        }
    
    
        // 3.insert 方法
        DoublyLinkedList.prototype.insert = function (position,data) {
          // 1.越界判断
          if (position < 0 || position > this.length) return false
    
          // 2. 根据 data 创建新的节点
          var newNode = new Node(data)
    
          // 3.判断原来的列表是否为空
          if (this.length == 0) {
            this.head = newNode
            this.tail = newNode
          } else {
            // 3.1 判断 position 是否为0
            if (position == 0) {
              this.head.prev = newNode
              newNode.next = this.head
              this.head = newNode
            } else if (position == this.length) {
    
              newNode.prev = this.tail
              this.tail.next = newNode
              this.tail = newNode
            } else {
              // 新节点连接,在该旧节点
              var current = this.head
              var index = 0
    
              while(index++ < position) {
                current = current.next
              }
    
              // 修改指针
              newNode.next = current
              newNode.prev = current.prev
              current.prev.next = newNode
              current.prev = newNode
    
            }
          }
    
          this.length += 1
          return true
        }
    
    
        // 4.get 方法
        DoublyLinkedList.prototype.get = function (position) {
          // 1.越界判断
          if (position < 0 || position >= this.length) return null
    
          // 双向链表的查找
          if (this.length / 2 > position) {
            // 从头向后遍历
            // 2.获取元素
            var current = this.head
            var index = 0
    
            while (index++ < position) {
              current = current.next
            }
    
          } else if (this.length / 2 < position) {
            // 从尾到前,遍历
            // 2.获取元素
            var current = this.tail
            var index = this.length -1
    
            while (index-- > position) {
              current = current.next
            }
          }
          return current.data
    
        }
    
        // 5. indexOf 方法
        DoublyLinkedList.prototype.indexOf = function (data) {
          // 1.定义变量
          var current = this.head
          var index = 0
    
          // 2.查找和 data 相同的节点
          while (current) {
            if (current.data == data) {
              return index
            }
            current = current.next
            index += 1
          }
          return -1
        }
    
        // 6.update 方法
        DoublyLinkedList.prototype.update = function (position,newData) {
          // 1.越界的判断
          if (position < 0 || position >= this.length) return false
    
          if (this.length / 2 > position) {
            // 从头向后遍历
            // 2.获取元素
            // 2. 寻找正确的节点
            var current = this.head
            var index = 0
            while (index++ < position) {
              current = current.next
            }
    
            // 3. 修改找到的节点 data 信息
            current.data = newData
    
            return true
    
          } else if (this.length / 2 < position) {
            // 从尾到前,遍历
            // 2.获取元素
            // 2. 寻找正确的节点
            var current = this.tail
            var index = this.length - 1
            while (index-- > position) {
              current = current.next
            }
    
            // 3. 修改找到的节点 data 信息
            current.data = newData
            return true
          }
        }
    
        // 7. removeAt 方法
        DoublyLinkedList.prototype.removeAt = function (position) {
          // 1.越界判断
          if (position < 0 || position >= this.length) return null
    
          // 2.判断是否只有一个节点
          var current = this.head
    
          if (this.length == 1) {
            this.head = null
            this.tail = null
          } else {
            if (position == 0) {
              // 判断是否删除第一个节点
              this.head.next.prev = null
              this.head = this.head.next
            } else if (position == this.length - 1) {
              // 最后一个节点
              current = this.tail
              this.tail.prev.next = null
              this.tail = this.tail.prev
            } else {
              var index = 0
    
              while (index++ < position) {
                current = current.next
              }
              current.prev.next = current.next
              current.next.prev = current.prev
            }
          }
    
          // 3.length -1
          this.length -= 1
    
          return current.data
        }
    
        // 8. remove
        DoublyLinkedList.prototype.remove = function (data) {
          // 1.根据data 获取下标值
          var index = this.indexOf(data)
    
          // 2.根据 index 删除对应位置的节点
          return this.removeAt(index)
        }
    
        // 9. 判断是否为空
        DoublyLinkedList.prototype.isEmpty = function () {
          return this.length == 0
        }
    
        // 10. size 方法
        DoublyLinkedList.prototype.size = function () {
          return this.length
        }
    
        // 11. 获取第一个元素
        DoublyLinkedList.prototype.getHead = function () {
          return this.head.data
        }
        // 12. 获取链表得最后一个元素
        DoublyLinkedList.prototype.getTail = function () {
          return this.tail.data
        }
      }
    
      // 测试代码
      var list = new DoublyLinkedList()
    
      //1.测试 append 方法
      list.append('1a')
      list.append('2b')
      list.append('3c')
    
      // 2. 测试转成字符串的方法
      alert(list)
      // alert(list.backwardString())
      // alert(list.forwardString())
    
      // 3. 测试 insert  方法
      list.insert(0,'0g')
      list.insert(4,'4r')
      list.insert(2,'22')
      alert(list)
    
      // 4.测试 get  0g 1a 22 2b 3c 4r
      // alert(list.get(0))
      // alert(list.get(2))
      // alert(list.get(5))
    
      // 5. 测试 indexOf
      // alert(list.indexOf('0g'))
      // alert(list.indexOf('2b'))
      // alert(list.indexOf('22'))
    
    
      // 6.测试 update 方法
      list.update(0,'ttt')
      list.update(1,'lll')
      list.update(5,'520')
      alert(list)
    
      // 7.测试 removeAt
      // alert(list.removeAt(2))
      // alert(list)
      // alert(list.removeAt(4))
      // alert(list)
    
      // 8. 测试 remove 方法
      // alert(list.remove('2b'))
      // alert(list)
      // alert(list.remove('520'))
      // alert(list)
      // alert(list.remove('ttt'))
      // alert(list)
    
      // 9测试其他方法
      alert(list.isEmpty())
      alert(list.size())
      alert(list.getHead())
      alert(list.getTail())
    
    
  3. 双向链表分析

    image-20210818132231169

3. 循环链表

在单链表的基础上,将终端结点的指针由空指针改为指向头结点。即可!

  1. 基本思路

    • 操作:
  2. 代码实现

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值