数据结构Java实现:单链表

什么是链表?

链表是一种最常见的数据结构,其内部数据呈线性排列,属于线性表结构,什么是线性表?表中的数据按顺序依次排列,就像用一条线把数据串联起来一样。
在这里插入图片描述

链表就是这种排布方式,特点是添加数据和删除数据速度快,但是查询数据会比较耗时,这是因为链表在内存中的存储结构造成的。

这里我们可以将数组与链表进行对比,数组大家应该都很熟悉,学过 Java 的都会用,但是你真的了解它在内存中的存储结构吗?数组的特点是查询数据很快,添加数据和删除数据效率低,这一特征与链表恰好相反,数组的缺陷正是链表的优势,数组的优势则是链表的缺陷,所以二者对比着来记,效果会更好。

来说说为什么数组和链表的特点恰好相反,首先来看看二者在内存中的存储结构。

数组和链表都是线性表结构,数组在内存中是一串连续的内存空间,比如定义一个 int 类型数组,int[] array = new int[6],计算机会为 array 分配一块连续的空间,如下图所示。

在这里插入图片描述
1000-1003 这段空间用来存储数组中的第一个元素 array[0],1004-1007 的空间用来存储 array[1],以此类推数组中的每个元素都对应一块大小为 4 byte 的空间,这种结构就决定了数组查询数据速度很快,只需要知道首地址(在栈内存中记录的就是数组的首地址,可以直接获取),再结合寻址公式就可以很快找到对应元素的地址,从而取出数据。

数组的寻址公式:i_address = first_address + data_size*i

带入上述案例中,比如要找到数组中第 3 个元素,也就是下标为 2 ,该元素的首地址即 2_address = 1000 + 2*4 = 1008,计算机只需要执行一个简单的数学运算就可以找到元素的首地址,进而取出对应的值,对于计算机来讲,简单数学运算的耗时几乎可以忽略不计,所以数组查询数据速度非常快。

也正是因为这种结构导致数组添加和删除数据效率很低,因为这两种操作不仅仅是在数组中添加或者移除一个元素那么简单,同时还需要移动其他已存在的元素。

数组中各个元素的内存地址都是连续,不间断的,删除某个元素之后需要保证数组仍然是连续的,所以就需要移动数据,比如要删除 array[2],删除之后需要依次将 array[3]、array[4]、array[5] 向前移动一位,如下图所示。
在这里插入图片描述

同理,如果此时将 0 添加到数组中的第 2 位,即 array[1] 的位置,同样需要先将 array[1] 及其之后的各个元素依次向后移动 1 位,给新数据腾出位置才能添加,如下图所示。

在这里插入图片描述
因为要移动元素,所以无论是添加数据还是删除数据,效率都不高。

搞清楚数组的存储结构之后,我们再来看看链表的存储结构,在内存中,链表中的数据是分散的,无须存储在一块连续的内存空间中,如下图所示。

在这里插入图片描述
链表中存储了 3 个元素分别是 1、2、3,每个元素都有一个指针,指向下一个元素的内存地址,1 的指针就指向 2 的内存地址 1008,2 的指针就指向 3 的内存地址 1020,依次类推。

不同元素之间的物理空间间隔也是不确定的,所以这样的结构就无法通过一个固定的公式来求出某个元素的内存地址,只能从首元素开始依次向后查找,直到找到目标元素。如果目标元素位于链表的最后一位,则需要遍历整个链表才能找到它,效率很低。

同样,正是因为这样的结构,使得链表添加和删除元素效率很高,无须移动其他已存在的元素,只需要修改元素指针即可。比如,删除 2,则只需要将 1 的指针指向 3 即可,如下图所示。
在这里插入图片描述

添加元素也是一样,要在 2 和 3 之间添加元素 0 ,只需要随机分配一块空间存储 0,然后将 2 的指针指向 0,0 的指针指向 3 即可,如下图所示。

在这里插入图片描述
所以在链表中,无论是添加还是删除元素,都只需要修改相关节点的指针即可,效率很高。

搞清楚链表的结构之后,我们使用 Java 语言来实现一个单链表的结构。
package com.southwind.link;

public class SingleLinkedList {
public int size;
public Node head;

 /**
* 链表头部添加元素
* @param obj
* @return
 */
 public Node addHead(Object obj){
     Node newNode = new Node(obj);
     if(size == 0){
         this.head = newNode;
     }else{
         newNode.next = head;
         head = newNode;
     }
     size++;
     return newNode;
 }

 /**
* 在指定位置插入元素
* @return
 */
 public Node insert(int index,Object obj){
     if(index >= size){
         return null;
     }
     Node newNode = new Node(obj);
     if(index == 0){
         newNode.next = head;
         head = newNode;
     }else{
         Node target = head;
         Node previous = head;
         int pos = 0;
         while(pos != index){
             previous = target;
             target = target.next;
             pos++;
         }
         previous.next = newNode;
         newNode.next = target;
     }
     size++;
     return newNode;
 }

 /**
* 删除链表头部元素
* @return
 */
 public Node removeHead(){
     if(size > 0){
         Node node = head;
         head = head.next;
         size--;
         return node;
     }else{
         return null;
     }
 }

 /**
* 删除指定位置元素
* @return
 */
 public Node remove(int index){
     if(index >= size){
         return null;
     }
     Node result = head;
     if(index == 0){
         head = head.next;
     }else{
         Node target = head;
         Node previous = head;
         int pos = 0;
         while(pos != index){
             previous = target;
             target = target.next;
             pos++;
         }
         previous.next = target.next;
         result = target;
     }
     size--;
     return result;
 }

 /**
 * 删除指定元素
 * @return
 */
 public Node removeNode(Object obj){
     Node target = head;
     Node previous = head;
     if(obj.equals(target.data)){
         head = head.next;
         size--;
     }else{
         while(target.next!=null){
             if(obj.equals(target.next.data)){
                 previous = target;
                 target = target.next;
                 size--;
                 break;
             }else{
                 target = target.next;
                 previous = previous.next;
             }
         }
         previous.next = target.next;
     }
     return target;
 }

 /**
 * 返回指定元素
 * @return
 */
 public Node findNode(Object obj){
     Node target = head;
     while(target.next!=null){
         if(obj.equals(target.data)){
             return target;
         }else{
             target = target.next;
         }
     }
     return null;
 }

 /**
 * 输出链表元素
 */
 public void show(){
     if(size > 0){
         Node node = head;
         int length = size;
         System.out.print("[");
         while(length > 0){
             if(length == 1){
                 System.out.print(node.data);
             }else{
                 System.out.print(node.data+",");
             }
             node = node.next;
             length--;
         }
         System.out.println("]");
     }else{
         System.out.println("[]");
     }
 }

}
对上述代码进行测试。

package com.southwind.link;

public class Test {
public static void main(String[] args) {
SingleLinkedList linkedList = new SingleLinkedList();
linkedList.addHead(6);
linkedList.addHead(5);
linkedList.addHead(4);
linkedList.addHead(3);
linkedList.addHead(2);
linkedList.addHead(1);
linkedList.show();
System.out.println(“在index=3处添加元素11”);
linkedList.insert(3,11);
linkedList.show();
System.out.println(“在index=7处添加元素33”);
linkedList.insert(7,33);
linkedList.show();
System.out.println(“在index=6处添加元素22”);
linkedList.insert(6,22);
linkedList.show();
System.out.println(“删除头部元素”);
linkedList.removeHead();
linkedList.show();
System.out.println(“删除index=7处元素”);
linkedList.remove(7);
linkedList.show();
System.out.println(“删除index=6处元素”);
linkedList.remove(6);
linkedList.show();
System.out.println(“删除index=3处元素”);
linkedList.remove(3);
linkedList.show();
System.out.println(“删除指定元素3”);
linkedList.removeNode(3);
linkedList.show();
System.out.println(“删除指定元素33”);
linkedList.removeNode(33);
linkedList.show();
System.out.println(“返回指定元素2”);
System.out.println(linkedList.findNode(2));
System.out.println(“返回指定元素22”);
System.out.println(linkedList.findNode(22));
}
}

结果如下
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值