标签:双链表 通过 tom 利用 保存 虚拟 个数 容量 直接
数组并不是一个完美的数据结构,它容量固定,在有序数组中,增删数据麻烦而耗时。如果要记录的数据特点需要频繁的增删数据,又要快速查找数据,而且数据量又特别大的时候,数组就不再合适。当然,链表也不合适。针对这样的数据,可能使用散列表和红黑树是更合适的选择。
我其实只是想引出我要说的链表,但是搞砸了。不管,今天还是要说的是链表这个数据结构。
链表的结构是这样的:一个节点,这个节点储存着两个东西,分别是这个节点的数据,和下一个节点的地址。可以把链表简单的看成是一辆火车,特别是古老的绿皮火车。每一节的车厢相当于链表的节点,车厢装着的货物就是节点的数据,一节车厢连着下一节车厢。火车的车厢其实连接着上一节和下一节车厢,这么来看的话,火车更加的严谨的看,是双链表,也就是一个节点,即存放下一个节点的地址,也存放上一个节点的地址。而且火车让我们感觉和数组一样,所有的车厢都是被放在一起的。看来火车并不是对链表的一个好的比喻。
单链表有一个特点,没有后退可言,就如同时间。那么我们在生活中是否能找到这样的一个链表结构的事物呢?
galgame可能会很像链表,当发现有选项会严重影响结局的时候,玩家常常会在选项前进行存档,相当于保存了一个指向这个节点的指针或者说是地址,然后当要返回的时候,就读档,回到过去的节点,重新进行选择。但是,这个结构更加准确的来说,是一个树结果,因为一个节点其实不止一个地址。链表和数组一样,一个数据后面相邻的只会有一个数据。就如同头发一样,一根正常的头发,是不会分叉的。
如果有玩过RPG手游,是兄弟就来砍我的那种。那么我们可以看到,游戏里给的任务是特别符合单链表这个数据结构的。在我们跑任务的过程中,只需要点下一步,自动导航等,完成一个又一个的连环任务,就可以了。
链表这个结构在现实中的应用,我的确不是很了解。因为感觉现实更加的庞大,复杂。树和图对现实的描写会更加的契合。
class ListNode{
int val;
ListNode next;
node (){}
node (int value) {
node(value, null);
}
node (int value, node next) {
this.val = value;
this.next = next;
}
}
我们从最简单的查开始
boolean search(ListNode node, int key) {
while (node != null) {
if (node.val == val) {
return ture;
}
node = node.next;
}
return false;
}
这个就是最基本的链表的查找元素了。看节点的数据是否等于关键字,如果等于,那就说明找到了,如果不等于,就说明这个节点不是,看看下一个节点。如果整个链表里都没有符合的,说明没有找到。在变化的其实就只有一个节点的引用在变化。而且链表是和符合递归的结构,任意一个节点就是一个链表。所以利用递归可以很舒服的处理链表的问题,有时候。
boolean search(ListNode node, int key) {
if (node == null) {
return false;
}
if (node.val == key) {
return true;
}
return search(node.next, key);
}
如何给链表增加一个节点呢?
node.next = head.next;
head.next = node;
通常我们需要先找到新节点的位置,这个位置通常是新节点的爹。找到爹之后,就可以按照上门的代码所写的那样,先将爹的子节点赋值给新节点,然后重新认新节点当儿子。
我们来看看经常会碰到的特殊情况,比如要将新节点插入到头部,成为所谓的头节点,或者插入到尾部。
如果新节点要成为头节点,其实可以在上面代码的基础上,交换头节点和新节点的值,这样就可以表现出新节点插入到了头部的位置。
但是还有一种更加简单的,就是
node.next = head;
head = node;
将新节点的next指向原来的头节点,然后更新头节点的引用。
尾节点:
if (head.next == null) {
head.next = node;
}
可以发现链表对增加节点的处理是特别方便的。找到位置,更加一下节点的next引用就可以了。
那么删除节点呢?
删除节点的第一步当然还是要找到要删除的节点。但是怎么样才算是将一个节点从链表中删除了呢?不再有其他任何的节点引用指向这个节点。简单的说,其实就是它的父节点不再指向它。所以,我们删除链表中的节点,操作的并不是这个节点,而是这个节点的爹。让这个节点的爹的next引用删除节点的子节点。
if (head.next.val == key) {
head.next = head.next.next;
}
这个节点现在就不再有其他的节点指向它了,那么,其实我们就可以说是将这个节点给删除了。其实后面的工作,将删除节点的内存回收,时候Java虚拟机的垃圾回收器自动进行操作的,并不需要现在的我们来关系。我也不知道它具体是怎么实现的。
头节点的处理。
链表的头节点一直是特殊的存在,不过仍然是好处理的。
if (head.val == key) {
head = head.next;
}
emmm,的确好处理。
最有意思的,对链表的操作就是修改链表了。在算法里中,将一个链表各种反转,将多个链表进行合并等等。一般都是去操作链表的引用,而不是去直接的就该链表的数据。
链表是一个很简单的结构。玩链表基本上就是玩链表的引用。
链表是特别基础的结构。前面没有具体的说过操作链表的速度,因为我在自己写算法的时候,更多的还是计算自己写的算法是否嵌套多个循环,而不是背各种的结构操作的效率。
标签:双链表 通过 tom 利用 保存 虚拟 个数 容量 直接
原文地址:https://www.cnblogs.com/zhujf/p/14879305.html