数据结构--线性表之链表实现

参考:《数据结构--Java语言描述》

线性表的链式存储结构是用一组任意的存储单元来存放线性表的数据元素,这组存储单位可以是连续的,也可以是不连续的。

那么对于某一个元素如何找到下一个元素存放的位置?对于任意数据元素ai ,存在本身的的信息之外,还需要存储一个指示其直接后继元素存放位置的指针,这两部分组成数据元素ai的存储映像,被称之为结点

存储数据元素信息的称为数据域,存储直接后继存放位置的域称为指针域。如下图,单链表结点结构

 

单链表结点类的泛型定义如下:

    class Node<T>{
         T obj; //数据域
         Node<T> next;  //指针域

        public Node(T obj, Node<T> next) {
            this.obj = obj;
            this.next = next;
        }

        public Node(T obj) {
            this.obj=obj;
            this.next = null;
        }
    }

含有n个元素的线性表通过每个结点的指针域链接成一个链表。又由此链表的每个结点中只有一个指向后继的指针,所以称其为单链表线性链表。单链表结构如下图所示:

 

上图所示,单链表是带有头结点单链表。其中,头指针head指向链表的头结点,各元素结点的指针指向下一个结点。而最后一个结点的指针为“空”(NULL)。从头指针开始便可以沿着链找到链表中的各个数据元素(如何查指定的节点是链表中的难点)

简单的讲:单链表就是由若干个节点组成,节点包含两个部分,一个是数据域,一个是指针域,指针域指向下一个节点,最后一个节点的指针域为空

自定义链表中含有两个成员变量,一个是头指针head,一个是链表长度length。

注:上图所示链表,链表的第一个节点为head.next。并不是head

1、初始化

长度length设置为0 ,头指针指向一个新构建的节点,该节点数据域和指针域均设置为null,说明该链表为空,同时可以通过该头节点去查找链表中的各个节点

public class LinkedSequeceList<T> implements sequenceList<T> {
    private Node<T> head;  //头节点

    private int length;  //链表长度

    public LinkedSequeceList() {
        head=new Node<T>(null);
        length=0;
    }

2、插入

上图是在P节点和q节点之间插入S节点示意图

插入算法比较简单。及p.next=s;s.next=q;即可,链表与顺序表不同的地方就是无法通过索引去获取节点的位置。所以链表查找到需要插入的位置,需要从头指针开始查找,设置一个变量num=1;设置一个指针p指head,一个指针q指向head.next,插入的位置为pos,所以需要循环pos-1次,每次循环,都需要将指针p和指针q指向下一个节点,及p=q;q=q.next;

链表的插入算法:

  • 首先要检查参数pos的合法性,pos的有效值范围为1<=pos<=length+1
  • 在链表中如何找到要插入的位置pos是一个难点?使用链表不能像使用顺序表那样通过下标就可以获取到要插入的位置,只能从头结点开始,一个一个往下查找。
    • 需要设置一个变量nun=1
    • 设置一个指针p指向head,一个指针q指向head.next
    • 插入位置为pos,需要循环pos-1次,所以循环条件为num<pos
    • 每次循环,指针p和指针q都需要指向下一个节点,且num加1
  • 插入位置的前一个结点的next指针指向新插入的结点,而新插入位置的结点的next指针指向插入位置的后一个结点,
  • length加1,返回true
    public boolean add(T obj, int pos) {
        if(pos<1 || pos>length+1){
            System.out.println("pos参数非法");
            return false;
        }
        int num=1;
        Node p=head;
        Node q= head.next;
        while (num<pos){
            p=q;
            q=q.next;
            num++;
        }
//        p.next=new Node(obj,q);
        Node newNode=new Node<T>(obj);
        p.next=newNode;
        newNode.next=q;
        length++;
        return true;
    }

3、删除

上图为删除q节点示意图

删除节点的算法:被删除节点的前驱节点的next指针指向被删除节点的后继节点,即p.next=q.next。查找被删除的结点同插入操作查找结点的算法

链表删除节点的算法:

  • 需要确定链表是否为空,为空提示无法进行删除操作
  • 需要校验参数pos的有效性,有效范围1<=pos<=length
  • 在链表中找到需要删除的位置pos,跟插入的查找方式是一样的,我们不能通过下标直接获取到需要删除的结点的位置,需要从头结点开始,一个一个的往下查找
    • 需要设置一个变量num=1
    • 设置一个指针p指向head,一个指针q指向head.next
    • 删除的位置为pos,需要循环pos-1次,所以循环的条件为num<pos
    • 每次循环,指针p和指针q都指向下一个节点,num加1
  • 被删除结点(q)的前驱节点(p)的next指针指向被删除结点的后继结点,删除的节点的next设置为空(用于垃圾回收)
  • length减1. 返回需要删除的数据
  public T remove(int pos) {
        if(length==0){
            System.out.println("表空,无法进行删除操作");
            return null;
        }

        if(pos<1 || pos>length){
            System.out.println("pos值非法");
            return null;
        }
        int num=1;
        Node<T> p=head,q=head.next;
        while (num<pos){
            p=q;
            q=q.next;
            num++;
        }
        T obj= q.obj;
        p.next=q.next;
        q.next=null;  //方便垃圾回收
        length--;
        return obj;
    }

4、查找

判断表是否为空,表空,返回-1,表示不存在,不为空,从第一个节点开始一次往后查找,查找思路同上。不存在到返回-1,存在返回链表位置。

    public int find(T obj) {
        if(isEmpty()){
            System.out.println("链表为空表,不存在你需要查找的数据");
            return -1;
        }
        int num=1;
        Node p=head.next;
        while (num<=length){
            if(p.obj==obj){
               return num;
            }
            p=p.next;  //每次循环都需要把指针指向指向下一个节点
            num++;
        }
        return -1;
    }

5、获取

获取算法如下:

  • 判断表是否为空
  • 判断pos值是否有效
  • 查找pos位置的节点,查找思路同上,就不在阐述了。
    public T value(int pos) {
        if(isEmpty()){
            System.out.println("链表为空表,无法进行查找操作");
            return null;
        }

        if(pos<1 || pos>length){
            System.out.println("pos值非法");
            return null;
        }
        int num=1;
        Node<T> p=head;
        while(num<=pos){
            p=p.next;
            num++;
        }
        return p.obj;
    }

6、更新

算法如下:

  • 校验表是否为空,
  • 校验pos值是否有效,有效范围1<=pos<=length
  • 查找pos位置的节点,查找思路同上,就不在阐述了。用参数obj替换链表pos位置的值
 public boolean modify(T obj, int pos) {
        if(isEmpty()){
            System.out.println("链表为空表,更新失败");
            return false;
        }

        if(pos<1 || pos>length){
            System.out.println("pos值非法");
            return false;
        }
        int num=1;
        Node<T> p=head;
        while(num<=pos){
            p=p.next;
            num++;
        }
        p.obj=obj;
        return true;
    }

8、判空

当length等于0时,表明该链表为空

    public boolean isEmpty() {
        return length==0;
    }

9、求长度

返回length值即可

    public int size() {
        return length;
    }

10、正序遍历

访问头节点之后的每一个节点,并输出该链表中的值

    public void nextOrder() {
        Node<T> p=head.next;
        for(int i=0;i<length;i++){
            System.out.print(p.obj+"   ");
            p=p.next;
        }
        System.out.println();
    }

11、销毁

length值设置为0,head设置为null。为啥head设置为null链表就会被销毁呢?当head指针设置为null的时候,就没有GC Roots指向链表,当JVM进行垃圾回收的时候,会对其进行回收

    public void clear() {
        length=0;
        head=null;
    }

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值