链表 是一种 线性结构 ,而且存储上属于 链式存储(即内存的物理空间是不连续的),是线性表的一种。链表结构如下图所示:
下面以一个我实现的一个简单的链表类来进一步理解链表。
首先设计一个 结点类 ,这个结点类包含 数据 和 指向下一个结点的指针 。结点类的构造函数可以直接对结点赋值,然后利用结点类对象来创建一个链表。链表类的设计如下:
这里为了避免重复设计就可以兼容更多数据类型,引入了 泛型 ,即 模板 的概念。(模板的关键字是 class 或 typename)
这里的 m_size 表示 链表长度 。为了保护数据,这个变量以及 结点数据 都设置为 private 。
与动态数组不同,动态数组的“动态”含义是可以自动扩容(缩容),但实质还是静态的,而链表则实现了真正意义上的动态。因为只有需要一个结点,才会添加一个结点,不需要就会删除。在这里,为了下面程序代码编写方便、统一,引入了 虚拟头结点(也称 哨兵结点 )的概念。这个结点本身不存放数据,用户也不知道它的存在。
实现了前面的程序之后,接下来就是一个链表的增、删、改、查以及一些其他基本操作,接下来利用代码去实现。
首先,在类体内进行增加操作函数的原型说明。这里包括三个函数:
add(添加到任意位置)
addFirst(添加到头部)
addLast(添加到尾部)
然后分别去实现它们。
由于这些函数在类体外,所以每个函数头部必须添加一行代码:
表示该函数使用模板,下面同理。
如果不使用虚拟头结点,代码编写就要区分第一个结点和其他结点,从这里可以看出引入虚拟头结点的好处,统一了代码编写形式,下面的同理。
同理,在类体内进行删除函数的原型说明。这里包括四个函数:
remove(删除任意位置元素):返回删除元素的值。
removeFirst(删除头部元素):返回删除元素的值。
removeLast(删除尾部元素):返回删除元素的值。
removeElement(删除特定元素):这里删除的是第一个这样的元素,如果想把这样的元素都删掉,可以写一个新的函数来实现。
然后分别去实现它们。
这里删除操作的“删除位置非法”后面返回的 NULL 也可以用 throw 抛异常来实现,这里只是为了方便。
修改操作只有一个函数
set(修改指定位置的值)
同理,在类体内进行修改函数的原型说明,然后在类体外实现。
查找函数有四个:
get(返回特定位置元素)
getFirst(返回第一个元素)
getLast(返回最后一个元素)
contains(返回是否包含特定元素)
这里并没有实现 find 函数,因为即使获得了元素位置索引,也不能像数组一样方便的再次通过位置索引获得数据,依然需要遍历链表来获得数据。
分别对它们进行实现。
同理,这里 get 函数的“访问位置非法”后面返回的 NULL 也可以用 throw 抛异常来实现,这里只是为了方便。
链表还有一些其他的操作,这些函数我在类体内进行了实现。
包括 链表长度 的查询,还有 链表的打印 等操作。
这里的时间复杂度与数组相反,原因在于,引起数组时间复杂度增加的是元素的移动,而引起链表时间复杂度增加的是元素的遍历。
同理,删除操作的时间复杂度与数组也相反。
3.4 查找操作
总体情况:
因为链表需要遍历,所以操作的复杂度都是 O(n) 级别的,但是,如果只针对头结点操作,那么操作时间复杂度就会变成 O(1) 级别。
程序完整代码(这里使用了头文件的形式来实现类)如下:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/24023.html