码迷,mamicode.com
首页 > 编程语言 > 详细

链表——数组的兄弟

时间:2021-06-13 10:49:00      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:双链表   通过   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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!