数据结构与算法---链式存储结构(链表)

链表定义

通俗点来说,链表是由一系列结点组成的,而结点是由数据域和指针域组成的,数据域主要存储数据元素信息,而指针域中存储其直接后继的地址

头结点

真实头结点:其第一个结点用于存储数据
在这里插入图片描述
虚拟头结点:其第一个结点不许存储数据,即数据域为空
在这里插入图片描述
头指针:一个引用变量,存储头结点地址的指针而已

链表的实现
对链表的某些操作功能也是基于线性表实现的

package Ds02.动态链表;
import DS01.动态数组.List;
import java.util.Iterator;

/*步骤:
  1.先定义一个Node结点内部类,用于描述和定义结点
  2.定义head和rear指针,但实质就是虚拟结点
  3.实现各种操作
* */
//虚拟头结点实现的
//用动态链表实现的线性表 链表
public class LinkList<E> implements List<E> {
    private Node head;//链表头指针(虚头结点)
    private Node rear;//链表尾指针(最后一个结点)
    private int size;//链表的元素个数(结点个数)不包括头节点,头节点为虚结点
    public LinkList(){//不需要最大容量
        head=new Node();//head是一个对象的名称,创建一个结点对象  虚拟头结点
        rear=head;//当前两个指向同一个
    }
    private class Node{//内部类(结点的属性与行为)
        E data;//数据域
        Node next;//指针域,因为存下一个节点,节点类型为Node
        Node(){
            this(null,null);
        }
        Node(E data,Node next){
            this.data=data;
            this.next=next;
        }
        @Override
        public String toString(){
            return data.toString();
        }
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size==0&&head==rear;
    }

    @Override
    public void add(int index, E e) {
        if(index<0||index>size){//在尾部添加元素
            throw new IllegalArgumentException("角标越界");
        }
        Node n=new Node();
        n.data=e;//用结点把e存储起来(存到数据域中),然后把结点插入
       if(isEmpty()){//空表状态  特殊处理
            head.next=n;
            rear=n;
        }else if(index==0){//头插法
            n.next=head.next;//把当前头结点存储的下一跳赋给当前要插入的下一跳
            head.next=n;
        }else if(index==size){//尾插法
            rear.next=n;//之前的rear的下一跳指到现在要插入的结点
            rear=n;//当前的尾结点为n;

        }else{//中间插
           Node p=head;
           for(int i=0;i<index;i++){
                p=p.next;//移到要插入位置的前一个
           }
           n.next=p.next;//把当前的next赋给现在要插入的节点的next
           p.next=n;//把要插入的结点赋给当前的next

        }
       size++;
    }

    @Override
    public void addFirst(E e) {
        add(0,e);
    }

    @Override
    public void addLast(E e) {
        add(size,e);
    }

    @Override
    public E get(int index) {
        if(isEmpty()){
            throw new IllegalArgumentException("链表为空");
        }
        if(index<0||index>=size){
            throw new IllegalArgumentException("角标越界");
        }
        if(index==0){
            return head.next.data;
        }else if(index==size-1){
            return rear.data;
        }else{
            Node p=head;
            for(int i=0;i<=index;i++){
                p=p.next;
            }
            return p.data;
        }
    }

    @Override
    public E getFirst() {
        return get(0);
    }

    @Override
    public E getLast() {
        return get(size-1);
    }

    @Override
    public void set(int index, E e) {
        if(isEmpty()){
            throw new IllegalArgumentException("空表");
        }
        if(index<0||index>=size){
            throw new IllegalArgumentException("角标越界");
        }
        Node p=head;
        for(int i=0;i<=index;i++){//也是从虚拟头结点开始,但是虚拟头结点不算进去
            p=p.next;
        }
        p.data=e;
    }

    @Override
    public boolean contains(E e) {
        return find(e)!=-1;
    }

    @Override
    public int find(E e) {
        if(isEmpty()){
            throw new IllegalArgumentException("空表");
        }
        Node p=head;
        for(int i=0;i<size;i++){
            p=p.next;
            if(p.data.equals(e)){
                return i;
            }
        }
        return -1;
    }

    @Override
    public E remove(int index) {
        if(isEmpty()){
            throw new IllegalArgumentException("空表");
        }
        if(index<0||index>=size){
            throw new IllegalArgumentException("角标越界");
        }
        E ret=null;
        if(size==1){//只有一个元素,无论删头删尾都一样
            ret=rear.data;//需要返回要删除位置的元素,需要先保存一下,不然就会修改
            head.next=null;
            rear=head;
        }else if(index==0){//删头
            Node del=head.next;//第一个结点
            head.next=del.next;
            del.next=null;//垃圾回收
            del=null;//让删除的那个任何都找不到
        }else if(index==size-1){//删尾
            Node p=head;
            while (true){
                if(p.next!=rear){
                    p=p.next;
                }else{
                    break;
                }
            }
            ret=p.next.data;//最后一个存的数据(倒数第二个结点的下一跳中所存的数据)
            p.next=null;
            rear=p;
        }else{
            Node p=head;
            for(int i=0;i<index;i++){
                p=p.next;//移到删除位置的前一个
            }
            Node del=p.next;
            ret=del.data;
            p.next=del.next;
            del.next=null;//释放空间
        }
        size--;
        return ret;
    }

    @Override
    public E removeFirst() {
        return remove(0);
    }

    @Override
    public E removeLast() {
        return remove(size-1);
    }

    @Override
    public void removeElement(E e) {
        int index=find(e);
        if(index!=-1){
            remove(index);
        }else{
            throw new IllegalArgumentException("未找到");
        }
    }

    @Override
    public void clear() {
        head.next=null;
        rear=head;
        size=0;
    }

    @Override
    public int getCapacity() {
        return 0;
    }
    public String toString(){
        StringBuilder sb=new StringBuilder();
        sb.append("LinkList: "+size+"\n");
        sb.append('[');
        if(isEmpty()){
            sb.append(']');
        }else{
            Node p=head;
            while (true){
                if(p.next!=rear){
                    p=p.next;
                    sb.append(p.data);
                    sb.append(",");
                }else{
                    p=p.next;
                    sb.append(rear.data);
                    sb.append("]");
                    break;
                }
            }
        }
        return sb.toString();
    }
    @Override
    public Iterator<E> iterator() {
        return new LinkListIterator();
    }
    public class LinkListIterator implements Iterator<E>{
        Node p=head;
        @Override
        public boolean hasNext() {
            return p.next!=null;
        }

        @Override
        public E next() {
            p=p.next;
            return p.data;
        }
    }
}

链表的头插法
功能分析:
利用头插法添加结点,简单来说,就是将头结点的后继赋给当前要插入结点的后继,然后将此时头结点的后继定义为此时要插入的结点.

if(index==0){//头插法
            n.next=head.next;//把当前头结点存储的下一跳赋给当前要插入的下一跳
            head.next=n;
        }

链表的尾插法
功能分析:
将结点插入此链表的最后,就是将要插入的结点赋给当前尾结点rear的后继,然后把rear尾结点后移。

if(index==size){//尾插法
            rear.next=n;//之前的rear的下一跳指到现在要插入的结点
            rear=n;//当前的尾结点为n;
        }

链表的一般插入
功能分析:
找到要插入位置的前一个,将当前位置的后继赋给要插入结点的后继,然后此时位置的后继为当前要插入的结点

else{//中间插
           Node p=head;
           for(int i=0;i<index;i++){
                p=p.next;//移到要插入位置的前一个
           }
           n.next=p.next;//把当前的next赋给现在要插入的节点的next
           p.next=n;//把要插入的结点赋给当前的next
        }

链表的删除(只有一个元素)
功能分析:
只剩一个元素时删头删尾都一样,先将要删除的结点存储一下,避免最后返回时出错,然后将头结点的后继置空,尾结点等于头结点

if(size==1){//只有一个元素,无论删头删尾都一样
            ret=rear.data;//需要返回要删除位置的元素,需要先保存一下,不然就会修改
            head.next=null;
            rear=head;
        }

注意:在删除和添加元素是,需从虚拟头结点开始,到要删除或要添加位置的前一个,所以走过的次数是要进行操作的下标次,即index.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值