数据结构2:简单实现单链表

代码不全只是自己记录学习,借鉴:https://blog.csdn.net/javazejian/article/details/52953190;强烈推荐此大神博客。。。

package com.zejian.structures.LinkedList.MyLinked;
 
/**
 * 单链表的进化版本
 * @param <T>
 */
public class SingleLinkedList2<T> implements ILinkedList<T>{
 
    //设定一个空的头结点
    private Node<T> headnode;
 
    //设定一个尾节点,代表链表的最后的一个数据
    private Node<T> rear;
 
    //构造方法初始化数据
    public SingleLinkedList2(){
 
        this.headnode=this.rear=new Node<T>(null);
    }
 
    //构造方法
    public SingleLinkedList2(Node<T> head){
        this();
        //构造方法,将head对象赋值给头结点headNode的next上,将head的索引赋值rear,将最后的数据可以由 .rear获取
 
        //这是的头结点就是尾节点,这时的headNode与rear是对等的,头结点的下一个节点为初始化的
        this.headnode.next=this.rear.next=head;
 
        //将尾部结构赋值给rear,也就是说rear就是head,只是通过一个公共的变量来控制尾部
        rear=rear.next;
 
 
    }
 
    /**
     * 传入数组,生成单链表
     * @param arr
     */
    public SingleLinkedList2(T[] arr){
        this();
 
        if (arr!=null&&arr.length>0){
            //初始化头结点的下一个节点
            this.headnode.next=new Node<T>(arr[0]);
            //将数组的第一个数据赋值头结点的下一个数据
            rear=this.headnode.next;
 
            int i=1;
 
            while (i<arr.length){
 
                rear.next=new Node<T>(arr[i++]);
 
                //始终将rear作为最后的值得引用
                rear=rear.next;
            }
 
        }
 
    }
 
 
    /**
     * 传入链表生成新的链表
     * @param linkedList2
     */
    public SingleLinkedList2(SingleLinkedList2<T> linkedList2){
        //初始化链表
        this();
 
        if(linkedList2!=null&&linkedList2.headnode.next!=null){
 
            //将头结点传入
            this.headnode.next=new Node<T>(linkedList2.headnode.data);
 
            //获取传入链表的头节点的下一个节点
            Node<T> p = linkedList2.headnode.next;
            //获取新链表的头节点的下一个节点
            rear=this.headnode.next;
 
            //赋值
            while (p!=null){
                //将p中的数据给尾节点数据赋值
                rear.next= new Node<>(p.data);
 
                //跟新末尾指针指向
                rear=rear.next;
 
                p = p.next;
 
            }
 
 
 
        }
 
 
    }
 
 
    /**
     * 判断链表是否为空
     * 需要判断链表是否为空的依据是头结点head是否为null,当head=null时链表即为空链表,因此我们只需判断头结点是
     * @return
     */
    @Override
    public boolean isEmpty() {
        return this.headnode==null;
    }
 
 
    /**
     * 计算链表的长度
     * 遍历链表需要从头结点HeadNode开始,为了不改变头结点的存储单元,
     * 声明变量p指向当前头结点和局部变量length,然后p从头结点开始访问,
     * 沿着next地址链到达后继结点,逐个访问,直到最后一个结点,每经过一个结点length就加一,
     * 最后length的大小就是链表的大小
     * @return
     */
    @Override
    public int length() {
        int length=0;
        Node<T> p=headnode;
        while (p!=null){
            length++;//这个节点不为null,就加1重新赋值
            p=p.next;//指向下一个节点的位置
        }
 
        return length;
    }
 
 
    /**
     * 在单链表中获取某个元素的值是一种比较费时间的操作,需要从头结点开始遍历直至传入值index指向的位置,
     * 其中需要注意的是index是从0开始计算,也就是说传递的index=3时,查找的是链表中第4个位置的值
     * @param index
     * @return
     */
    @Override
    public T get(int index) {
        Node<T> p=this.headnode;
 
        int count=0;
        while (p!=null&&count<index){
            p=p.next;//指向下一个节点的位置,获取下一个节点的对象
            count++;
 
        }
        if (p!=null){
 
            return p.data;
        }
 
        return null;
    }
 
 
    /**
     * 根据传递的index查找某个值并替换其值为data,
     * 其实现过程的原理跟get(int index)是基本一样的,先找到对应值所在的位置然后删除即可
     * @param index
     * @param data
     * @return
     */
    @Override
    public T set(int index, T data) {
 
        if(this.headnode!=null&&data!=null&&index>=0){
            Node<T> pre=this.headnode;//获取头结点
            int count=0;
            //查找需要替换的节点
            while (pre!=null&&count<index){
                count++;
                pre=pre.next;
 
            }
 
 
            if (pre!=null){
                T oldData=pre.data;
                pre.data=data;//设置新值
 
                return oldData;
            }
        }
 
 
        return null;
    }
 
 
    /**
     * 根据下标添加结点
     * 1.头部插入
     * 2.中间插入
     * 3.末尾插入
     * @param index 该值从0开始,如传4就代表插入在第5个位置
     * @param data
     * @return
     */
    @Override
    public boolean add(int index, T data) {
        if (data==null){
            throw new NullPointerException("data can\'t be empty!");
        }
 
        if(index<0)
            throw new NullPointerException("index can\'t less than 0");
 
        //无需区分位置操作,中间/头部/尾部插入
        int j=0;
        Node<T> pre=this.headnode;
        while (pre.next!=null&&j<index){
            pre=pre.next;
            j++;
        }
 
        //将新插入的结点的后继指针指向pre.next
        Node<T> q=new Node<T>(data,pre.next);
        //更改指针指向
        pre.next=q;
 
        //如果是未指针
        if (pre==this.rear)
            this.rear=q;
 
        return true;
    }
 
 
    /**
     * 从代码和图示看来确实只要获取当前的尾部指向的结点rear并把新结点赋值给rear.next,最后更新rear结点的值即可,
     * 完全不用遍历操作,但是如果是根据index来插入的还,遍历部分结点还是少不了的,
     * 下面看看根据index插入的代码实现,由于有了头结点,头部、中间、尾部插入无需区分操作位都视为一种情况处理。
     * @param data
     * @return
     */
    @Override
    public boolean add(T data) {
        //判断
        if (data==null)
            System.out.println("data is empty!");
 
        this.rear.next=new Node<T>(data);
 
        rear=rear.next;
 
        return true;
    }
 
    /**
     *
     * @param index
     * @return
     */
    @Override
    public T remove(int index) {
        T old=null;
        //通过headnode和rear删除
        if (index>0){
            Node<T> p = this.headnode;
            int j=0;
            while (p.next!=null&&j<index){
                p= p.next;
                j++;
            }
 
            //获取删除节点上一个节点
            Node<T> move = p.next;
            if (move!=null){
            //获取旧值
                old=move.data;
                //判断是否是尾节点、
                if (move==this.rear){//位置的比较
 
                    //将上一个节点赋值给尾节点
                    this.rear=p;
 
                }
 
            }
 
            //删除节点
            p.next=move.next;
 
        }
        return old;
    }
 
    /**
     * 根据data移除所有数据相同的结点
     * @param data
     * @return
     */
    @Override
    public boolean removeAll(T data) {
 
        if (data!=null){
 
            //用于与记录要删除节点前的一个节点
            Node<T> font = this.headnode;
 
            //当前遍历的节点
            Node<T> pre = font.next;
 
            //查询所有节点相同的数据并删除
            while (pre!=null){
                // 判断data数据是否与链表数据相等,
                if (pre.data.equals(data)){
 
                    //如果恰好是尾部结点,则更新rear的指向
                    if(data.equals(rear.data)){
                        this.rear=font;
                    }
                  //更改指针方法
                    font.next=pre.next;
 
                    //font.next重新指向pre,这时pre=pre.next
                    pre=font.next;
 
 
 
                }else{
                    //如果不是相同的节点,就设置指针
                    font=pre;
                    pre=pre.next;
                }
 
 
 
            }
 
 
        }
 
 
 
        return true;
    }
 
    /**
     * 删除所有节点
     */
    @Override
    public void clear() {
        //将头结点的下一个节点赋值为空
        this.headnode.next=null;
 
        //将尾节点赋值为空
        this.rear=this.headnode;
    }
 
 
    /**
     * 判断是否包含某个值
     * @param data
     * @return
     */
    @Override
    public boolean contains(T data) {
        Node<T> headnode = this.headnode;
 
        while (headnode.next!=null){
 
            if (data.equals(headnode.data)){
 
                return true;
            }
            headnode=headnode.next;
        }
 
 
        return false;
    }
 
    /**
     * 从末尾连接两个链表
     * @param list
     */
    public void concat(SingleLinkedList2<T> list) {
        //如果本链表为空
        if (this.headnode.next == null) {
            //直接将外来链表赋值到本链表的头结点的后面一个节点
            this.headnode.next = list.headnode.next;
 
        } else {
 
            Node<T> p = this.headnode.next;
 
            while (p.next != null) {
 
                p = p.next;
            }
 
            p.next = list.headnode.next;
 
            //更新指针
            this.rear = list.rear;
 
        }
    }
 
        /**
         *打印
         */
        public String toString(){
        String str="(";
        Node<T> pre = this.headnode.next;
        while (pre!=null)
        {
            str += pre.data;
            pre = pre.next;
            if (pre!=null)
                str += ", ";
        }
        return str+")";
 
        }
 
 
 
 
    public static void main(String[] args) {
        String[] letters={"A","B","C","D","E","F"};
        SingleLinkedList2<String> list=new SingleLinkedList2<>(letters);
        list.add("aaa");
 
 
        list.removeAll("aaa");
        for (int i = 0; i <list.length() ; i++) {
            System.out.println(list.get(i));
        }
 
        System.out.println(list.contains("a"));
 
        list.set(1,"vv");
        System.out.println(list.toString());//(vv, B, C, D, E, F)
 
    }
 
 
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值