大话数据结构------线性表的链式存储结构(单链表)

上篇说的线性表的线性存储结构,有一个明显的确定,那就是每次插入和删除元素都需要移动大量的元素,能否想个办法解决它。我们可以使用本文提到的链表解决

线性表的链式存储结构每一个结点都拥有一个数据域和一个指针域(指向下一个节点),因此其不需要将所有结点存在一块连续的地址空间中

先贴上kotlin代码

/**
 * 使用单链表实现,区别于java类库中的LinkedList(双向链表)
 */
class LinkedList<T> {

    /**
     * 头结点
     */
    private val mHeader: Node = Node(null, null)
    var size = 0
        private set

    /**
     * 取出对应索引的数据
     */
    fun get(index: Int): T? = node(index)?.data

    /**
     * 设置指定索引的数据
     */
    fun set(index: Int, data: T?) {
        node(index)?.data = data
    }

    /**
     * 在指定位置添加若干个数据
     * 如果链表的数据是Int,那么这个方法有问题
     */
    @JvmOverloads
    fun add(index: Int = size, vararg data: T) {
        checkIndex(index, size)
        var preNode: Node? = null
        for (i in data) {
            val node = Node(i, null)
            if (index == 0) {
                node.next = mHeader.next
                mHeader.next = node
            } else {
                if (preNode == null) {
                    preNode = node(index-1)
                }
                node.next = preNode?.next
                preNode?.next = node
            }
            size++
        }
    }

    /**
     * 移除指定位置的数据
     */
    fun remove(index: Int) {
        checkIndex(index)
        if (index == 0) {
            mHeader.next = node(1)
        } else {
            val preNode = node(index-1)
            preNode?.next = preNode?.next?.next
        }
        size--
    }

    /**
     * 找到对应索引位置的结点
     */
    private fun node(index: Int): Node? {
        checkIndex(index)
        var tempNode: Node? = mHeader
        for (i in 0..index) {
            tempNode = tempNode?.next
        }
        return tempNode
    }

    /**
     * 检查索引
     */
    private fun checkIndex(index: Int, length: Int = size - 1) {
        if (index !in 0..length) {
            throw RuntimeException("out of range totalLength is $size index is $index")
        }
    }

    override fun toString(): String {
        val sb = StringBuilder("[")
        var tempNode: Node? = mHeader
        for (i in 0..(size-1)) {
            tempNode = tempNode?.next
            if (i != 0) {
                sb.append(", ")
            }
            sb.append(tempNode?.data)
        }
        sb.append("]")
        return sb.toString()
    }

    private inner class Node(var data: T?, var next: Node?)
}
  • 链表获取指定位置元素的流程 时间复杂度O(n)
  1. 判断获取的位置是否小于列表大小,不正确就抛出异常
  2. 一个接一个往下查找直到找到对应位置
  3. 返回该位置结点的数据域
  4. 链表设置指定位置元素的流程 时间复杂度O(n)
  • 判断获取的位置是否小于列表大小,不正确就抛出异常
  1. 一个接一个往下查找直到找到对应位置
  2. 设置该位置结点的数据域
  3. 链表在指定位置插入一个数据 时间复杂度O(n)
  • 判断插入的位置是否小于列表大小,不正确就抛出异常
  1. 一个接一个往下查找直到找到插入位置的前一个结点P
  2. 新建一个节点N,并且设置N.next = P.next,P.next = N
  3. 链表删除指定位置的一个数据 时间复杂度O(n)
  • 判断插入的位置是否小于列表大小,不正确就抛出异常
  1. 一个接一个往下查找直到找到插入位置的前一个结点P
  2. 设置P.next = P.next.next
  3. 4个方法时间复杂度都是O(n)还多了一个引用域那么相对于线性结构有什么优点呢?

其实链表元素的插入和删除主要时间花在了找到插入/删除前的一个结点,如果我们已经知道了该结点,那么插入和删除的时间复杂度将是O(1),因此当我们需要在同一个位置连续插入多个元素其优点就显现出来了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值