单向链表的一些基本操作(Java)

发布于:2025-09-05 ⋅ 阅读:(29) ⋅ 点赞:(0)

上次我们讲了顺序表,这次我们来讲链表

一.链表定义

        链表是一种常见的数据结构,用于存储一系列元素。与数组不同,链表中的元素(节点)在内存中不必是连续的。每个节点包含数据部分和指向下一个节点的引用(指针)。链表的主要优点是插入和删除操作的时间复杂度为O(1),但访问特定元素的时间复杂度为O(n)。

    链表分类:

        1.单向链表(Singly Linked List):
        a)在单向链表中,每个节点包含一个数据域和一个指向下一个节点的引用(指针)。
        这是最简单的链表形式,只能从头节点遍历到尾节点。
        b)优点:实现简单;节省空间(每个节点只需要一个额外的指针)。
        c)缺点:不能反向遍历;插入和删除操作需要找到前驱节点。
        2.双向链表(Doubly Linked List):
        a)双向链表中的每个节点除了包含指向下一个节点的引用外,还有一个指向前一个节点的引用。
        b)优点:可以从两个方向遍历链表;插入和删除操作更加方便。
        c)缺点:每个节点需要两个额外的指针,因此比单向链表占用更多空间。
        3.循环链表(Circular Linked List):
        a)循环链表可以是单向的也可以是双向的,其特点是最后一个节点的“下一个”指针不是指向null,而是回指到链表的第一个节点,形成一个闭环。
        b)优点:便于实现循环遍历;没有明确的尾节点,处理起来更方便。
        c)缺点:如果不小心处理,可能会陷入无限循环。

        今天我们来讲单向链表

二.单向链表

单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。

在一个链表中,头指针的作用就像是一张地图上的起点标示,它帮助我们知道链表的起始位置在哪里。

头指针总是指向链表的第一个节点(即首节点)。通过这个指针,我们可以访问链表中的所有元素。

1.结点的API设计

//结点类
     class Node<T>
     {
         T data; //存储数据
         Node<T> next;//下一个节点
        //构造方法
         public Node(T date, Node<T> next)
         {
            this.data = date;
            this.next = next;
         }
     }

2.单向链表的API设计

初始化单向链表

class Linklist <T>
{
    //结点类
     class Node<T>
     {
         T data; //存储数据
         Node<T> next;//下一个节点
        //构造方法
         public Node(T date, Node<T> next)
         {
            this.data = date;
            this.next = next;
         }
     }
    //创造头结点
    Node<T> head;
     //链表长度
    int size;
   //构造方法
    public Linklist()
    {
        this.head = new Node(null, null);
        this.size = 0;
    }
}

1.清空链表

思路分析:想清空链表,就让头结点的引用域为null ,就会与后面的自动断开,再令长度为0

//清空方法
    public void clear()
    {
        head.next = null;
        size = 0;
    }

2.判断链表是否为空

思路分析:返回查看长度是否为0,是的话返回true,否返回false

 //判断链表是否为空
    public boolean isEmpty()
    {
        return size == 0;
    }

3.求链表长度

思路分析:直接返回长度即可

 //求链表长度方法
    public int length ()
    {
        return size;
    }

4.找到指定位置的元素

思路分析1.先检查位置是否合规

2.创建一个变量来取代头结点的引用

3.通过循环遍历,找到位置所处的结点

4.最后返回结点的数据

 public T get( int index)
    {
        //检查位置是否合规
        if(index < 0 || index >= size)
        {
            throw  new RuntimeException("输入位置违规");
        }
        Node<T> node = head.next;
        for(int i = 0; i < index; i++)
        {
            node = node.next;
        }
        return node.data;
    }

5.在末尾插入元素

思路分析:

1.创建一个变量代替头结点

2.通过循环找到最后一个结点,根据最后一个结点的引用为null来写循环条件

3.创建新结点,将原来最后一个结点的引用指向新结点

//插入元素(末尾)
    public void add(T data)
    {
        //创建一个变量来代替头结点
        Node newNode = head;
        //遍历找到最后一个结点的位置,通过最后一个结点的指针值为null,用while来寻找
        while(newNode.next != null)
        {
            newNode = newNode.next;
        }
        //创建新结点
        Node newNode1 = new Node(data, null);
        newNode.next = newNode1;
        size++;
    }

6.在对应位置插入元素

思路分析:

根据这个简易流程图我们可知:

1.创建一个新的变量表示头结点

2.通过循环遍历找到位置的前一个结点所指向的值

3.创建新结点(也就是值为4所在的这个结点)将新结点指向原来前一个位置结点所指向的值

(在本图中既让4.next =1.next,1.next在等式右边代表所指向下一个结点的值,左边则是变量接受这个值)

4.再将原来位置的前一个结点指向新结点

注意!!!

在执行第三四步代码时,一定要先把新结点指向原来前一个位置结点所指向的值,再把原来前一个结点指向新结点的值

 //在指定位置插入元素
    public void insert(int index, T data)
    {
        Node neeNode=head;
        //找到指向i位置的前一个值的结点
        for(int i = 0; i < index; i++)
        {
            neeNode = neeNode.next;
        }
        //指向i位置的值的结点
        Node currentNode=neeNode.next;
        //创建新结点,将新结点指向原来i位置的前一个位置所指向的结点
        Node newNode2 = new Node(data, currentNode);
        //将原来位置的前一个结点指向新结点的数据
        neeNode.next = newNode2;
        size++;
    }

7.删除指定位置的元素

思路分析:

根据简易流程图我们可知:

1.首先创建一个变量代表头结点

2.用循环找到指向指定位置前一个值的结点

3.找到指向i位置的值的结点

4.找到i位置指向下一个位置的值的结点

5.将i位置下一个位置的值指向i位置前一个的结点

//删除指定位置的元素,并返回被删除的元素
    public Object remove(int index)
    {
        Node curr=head;
        //找到指向i位置前一个位置的值的结点
        for(int i = 0; i < index; i++)
        {
            curr = curr.next;
        }
        //找到指向i位置的值的结点
        Node newNode = curr.next;
        //找到i位置指向下一个位置的值的结点
        Node nextNode = newNode.next;
        //将i位置下一个位置的值指向i位置前一个的结点
        curr.next = nextNode;
        size--;
        return newNode.data;
    }

8.找到值对应的位置

思路分析:用一个循环和equal函数找出相对应的位置

// 返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
    public int indexOf(T t)
    {
        //循环遍历找到值所对应的索引
        Node<T> current = head;
        int index = -1;
        while (current != null)
        {
            if (current.data != null && current.data.equals(t))
            {
                return index;
            }
            current = current.next;
            index++;
        }
        return -1;
    }

练习题

添加以下学生姓名到顺序表中:“张三”, “李四”, “王五”,“赵六”。

在“李四”后面插入“李四”(注意:需要先找到“李四”的位置)

删除“李四”并从顺序表中移除

查找“李四”在顺序表中的位置(索引)

打印最终顺序表中的所有学生姓名

清空顺序表并验证它确实为空

完成代码

class Linklist <T>
{
    //结点类
     class Node<T>
     {
         T data; //存储数据
         Node<T> next;//下一个节点
        //构造方法
         public Node(T date, Node<T> next)
         {
            this.data = date;
            this.next = next;
         }
     }
    //创造头结点
    Node<T> head;
     //链表长度
    int size;
   //构造方法
    public Linklist()
    {
        this.head = new Node(null, null);
        this.size = 0;
    }
// ------------------------------------------------------------
    //清空方法
    public void clear()
    {
        head.next = null;
        size = 0;
    }
    //求链表长度方法
    public int length ()
    {
        return size;
    }
    //判断链表是否为空
    public boolean isEmpty()
    {
        return size == 0;
    }
    //找到指定位置的元素
    public T get( int index)
    {
        //检查位置是否合规
        if(index < 0 || index >= size)
        {
            throw  new RuntimeException("输入位置违规");
        }
        Node<T> node = head.next;
        for(int i = 0; i < index; i++)
        {
            node = node.next;
        }
        return node.data;
    }
    //插入元素(末尾)
    public void add(T data)
    {
        //创建一个变量来代替头结点
        Node newNode = head;
        //遍历找到最后一个结点的位置,通过最后一个结点的指针值为null,用while来寻找
        while(newNode.next != null)
        {
            newNode = newNode.next;
        }
        //创建新结点
        Node newNode1 = new Node(data, null);
        newNode.next = newNode1;
        size++;
    }
    //在指定位置插入元素
    public void insert(int index, T data)
    {
        Node neeNode=head;
        //找到指向i位置的前一个值的结点
        for(int i = 0; i < index; i++)
        {
            neeNode = neeNode.next;
        }
        //指向i位置的值的结点
        Node currentNode=neeNode.next;
        //创建新结点,将新结点指向原来i位置的前一个位置所指向的结点
        Node newNode2 = new Node(data, currentNode);
        //将原来位置的前一个结点指向新结点的数据
        neeNode.next = newNode2;
        size++;
    }
    //删除指定位置的元素,并返回被删除的元素
    public Object remove(int index)
    {
        Node curr=head;
        //找到指向i位置前一个位置的值的结点
        for(int i = 0; i < index; i++)
        {
            curr = curr.next;
        }
        //找到指向i位置的值的结点
        Node newNode = curr.next;
        //找到i位置指向下一个位置的值的结点
        Node nextNode = newNode.next;
        //将i位置下一个位置的值指向i位置前一个的结点
        curr.next = nextNode;
        size--;
        return newNode.data;
    }
    // 返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
    public int indexOf(T t)
    {
        //循环遍历找到值所对应的索引
        Node<T> current = head;
        int index = -1;
        while (current != null)
        {
            if (current.data != null && current.data.equals(t))
            {
                return index;
            }
            current = current.next;
            index++;
        }
        return -1;
    }
}
    public class StudentMs2
    {
        public static void main(String[] args)
        {
            Linklist<String> ll=new Linklist<>();
            ll.add("张三");
            ll.add("李四");
            ll.add("王五");
            ll.add("赵六");
            int i=ll.indexOf("李四");
            System.out.println("李四的位置:"+ i);
            ll.insert(1,"李四");
            ll.remove(1);
            System.out.println("最终的学生名单:");
            for(int i1=0;i1<ll.length();i1++)
            {
                System.out.println(ll.get(i1));
            }
            ll.clear();
            System.out.println("清空后的长度 "+ll.length());
            ll.isEmpty();
        }
    }


网站公告

今日签到

点亮在社区的每一天
去签到