基本数据结构之链表(用Java语言实现)

链表

链表是由一系列节点(链表中的每一个元素都叫作一个节点)组成的数据结构,节点可以在运行过程中动态生成。每个节点都包括两部分内容:存储数据的数据域;存储下一个节点地址的指针域。

由于链表是随机存储数据的,因此在链表中插入数据的时间复杂度为O(1),比在线性表和顺序表中插入的效率要高。

但在链表中查找一个节点时需要遍历链表中所有元素,因此时间复杂度为O(n);而在线性表和顺序表中查找一个节点的时间复杂度分别为O(logn)和O(1)。

链表有 3种不同的类型:单向链表、双向链表及循环链表。

链表的特点:链表通过一组存储单元存储线性表中的数据元素,这组存储单元可以是连续的,也可以是不连续的。因此,为了表示每个数据元素与其直接后继数据元素之间的逻辑关系,对数据元素来说,除了存储其本身的信息,还需要存储直接后继数据元素的信息(即直接后继数据元素的存储位置)。由这两部分信息组成一个“节点”。链表数据结构的优点是插入快,缺点是数据查询需要遍历整个链表,效率慢。图示如下:

在这里插入图片描述

其一个节点就是:
在这里插入图片描述

单向链表

单向链表(又称单链表)是链表的一种,其特点是链表的链接方向是单向的,访问链表时要从头部开始顺序读取。单向链表是链表中结构最简单的。一个单向链表的节点(Node)可分为两部分:第1部分为数据区(data),用于保存节点的数据信息;第2部分为指针区,用于存储下一个节点的地址,最后一个节点的指针指向null。具体的数据结构如下图所示:

在这里插入图片描述

操作:

(1)查找:单向链表只能向一个方向遍历。一般查找时,需要从头至尾挨个遍历。

(2)插入:插入,只需将插入节点设置为头结点,将next指向原来的头结点。如下图所示:

在这里插入图片描述

(3)删除:把该节点的上一个节点指针指到下一个节点的头。然后删除该节点即可:

在这里插入图片描述

定义单向链表的数据结构:

public class SingleLinkedList{
    private int length;
    private Node head;
    
    public SingleLinkList{
        length=0;
        head=null;
    }
    
    public class Node{
        private Object data;
        private Node next;
        
        public Node (Object data){
            this.data=data;
        }      
    }   
}

该代码用length表示链表的大小,内部创建了一个Node的内部类,Node包含data 与指针next。

插入单向链表的数据:

public Object addHead(Object obj){
    //新建一个节点
    Node newhead=new Node(obj);
    if(length==0){
        head=newhead;
    }else{
        newhead.next=head;
        head=newhead;
    }
    length+=1;
    return obj;
    
    
}

链表最常用的还是头插法,复杂度为O(1);头插法:只需将新节点的next指向当前的head节点,然后再讲newhead设置为当前的head,最后让length++

删除单向链表的数据:

public boolean delete(Object value){
    if(length==0){
        return false;
    }
    Node current=head;
    Node previous=head;
    while(current.data!=value){
        if(current.next==null){
            return false;
        }else{
            //这样pre指针比cur指针少一步
            previous=current;
            current=current.next;
        }
    }
    //找到了准备的位置
    //若删除的是头结点
    if(current==head){
        head=current.next
    }else{
        previous.next=current.next;
        length--;
    }
    return true;
    
} 

以上代码定义了方法delete()来删除单向链表中的数据,具体的删除操作为:首先判断链表的长度,如果链表长度为 0,则说明链表为空,即不包含任何元素,直接返回false;如果链表不为空,则通过while循环找到要删除的元素;如果要删除的节点是头节点,则需要把要删除的节点的下一个节点指定为头节点,删除该节点,把节点长度减 1;如果删除的节点不是头节点,则将该节点的上一个节点的Next指针指向该节点的下一个节点,删除该节点,并把节点长度减1。

单向链表数据查找:

public Node find(Object obj){
    Node current =head;
    int tempsize=length;
    
    while(tempsize>0){
        if (obj.equals(current.data)){
            return current;
        }else{
            current=current.next;
        }
        tempsize--;
    }
    return null;
}

以上代码定义了名为find()的单向链表节点查询方法。该方法很简单,定义了一个while循环来查找数据,如果当前数据和要查找的数据相同,则返回该数据;如果不同,则将当前节点的下一个节点设置为当前节点,沿着当前节点向前继续寻找。这里将tempSize减 1的目的是控制while循环的条件,在tempSize为 0时表示遍历完了整个链表还没找到该数据,这时返回null。

双向链表

双向链表每个节点都有两个指针,一个指向前节点,一个指向后节点。

双向链表的发明使得遍历变得更简单,每次只需遍历一半即可,因为可以左右开弓。

在这里插入图片描述

定义双向链表的的数据结构:

public class TwoWayLinkedList{
    private Node head;
    private Node tail;
    private int length;
    private class Node{
        private Object data;
        private Node next;
        private Node pre;
        public Node(Object obj){
            this.data=data;
        }
    }
    //对双向链表初始化
    public TwoWayLinkedList{
        length=0;
        head=null;
        tail=null;
    }
    
}

以上代码定义了一个名为TwoWayLinkedList的双向链表的数据结构,其中定义了:head,表示链表头;tail,表示链表尾;length,表示链表长度;Node,表示链表的节点,链表的节点包含data、prev、next,分别表示节点数据、上一个节点和下一个节点。这样双向链表的数据结构就定义好了。

在链表头部增加节点:

public void addHead(Object obj){
	Node newhNode=new Node(value);
    //如果是空节点
    if(length==0){
        head=newNode;
        tail=newNode;
        length++
    }else{
        //头插法
        head.pre=newNode;
        newNode.next=head;
        head=newNode;
        length++;
        
    }
}

以上代码定义了addHead()来向链表的头部加入数据,具体操作为:首先,新建一个节点;然后,判断链表的长度,如果链表的长度为 0,则说明链表是空链表,将链表的头部和尾部均设置为当前节点并将链表长度加 1即可;如果链表不是空链表,则将原链表头部的上一个节点设置当前节点,将当前节点的下一个节点设置为原链表头的
节点,将链表的头部节点设置为当前节点,这样就完成了双向链表的头部节点的插入;最后,需要将链表的长度加1。

在链表尾部增加节点:

public void addTail(Object obj){
	Node newhNode=new Node(value);
    //如果是空节点
    if(length==0){
        head=newNode;
        tail=newNode;
        length++
    }else{
        //尾插法
        newNode.pre=tail;
        tail.next=newNode;
        tail=newNode;
        length++;
        
    }
}

与头插法相似。

删除双向链表的头部节点

public Node deleteHead(){
    Node temp=head;
    if(length!=0){
        head=head.next;
        head.pre=null;
        length--;
        return temp;
    }else{return null}
}

以上代码定义了一个名为deleteHead()的方法来删除链表的头部节点,具体操作为:首先定义一个临时节点来存储当前头部节点;然后判断节点的长度,如果节点的长度为 0,则直接返回null;如果节点的长度不为 0,则将当前头部节点设置为原头部节点的下一个节点,将头部节点的上一个节点设置为null,然后删除该节点;最后,将节点的长度减 1。

删除双向链表的尾部节点

public Node deleteTail(){
    Node temp=head;
    if(length!=0){
        Tail=Tail.pre;
        Tail.next=null;
        length--;
        return temp;
    }else{return null}
}

同上,方法相似。

循环列表

循环列表与单向列表的唯一不同点是,循环链表的最后一个节点的指针指向头结点。整个链表成了一个闭环。

如下图所示:
在这里插入图片描述

其他均相同

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你们卷的我睡不着QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值