链表

一:单向链表

 链表是一个一个元素的添加的

 

每一个节点都是用到时 才会进行分配内存空间

 由于动态数组和链表中  接口方法是相同的但是实现的代码是不一样的

因此我们定义一个接口List

实现这个接口的每一个类都应该实现这个接口中的所有方法

 插播一条Java语法的问题:

我们的动态数组ArrayList和链表LinkedList都是实现于List的

因此我们可以用类似于多态的形式进行new

 接口设计:

 

clear:

next不需要设置为null

因为当size==0时  第一个节点消失死亡 跟着的后面节点一个一个也跟着死亡消失 

add:

步骤:找到要添加到位置的前一个元素

让添加的元素的next指向1处的22

让0处的next指向添加元素的44 

1. 找到对应索引下标的节点node

 2.获取某个节点处的元素值

 3.add

先找到要添加位置的前一个位置

之后再进行new出来一个新的节点

但是如果增加到0下标处时

我们需要改正  改正如下:

我们需要让头节点的指向也就是这里的first指向重新定义

 

如果增加到最后一个位置时  但是这种情况可以被第二种添加方式所包含

 

 删除:

 获取前面一个元素  

让前面一个next的指向  指向下一个位置

 这里的first也就是链表头节点的first

 练习题1:

 

思路:

1.首先用要被删除元素的元素的值进行覆盖 node.val=node.next.val;

2.再用要被删除元素的前一个结点的next指向node.next.next

node.next=node.next.next;

 

练习题2:

反转:

方法一:递归方法

举个例子:
当我们函数传参传递的是4时 那么反转之后的链表形式如图 :

 

 

 

 

 习题3:

package 链表动态数组.链表.单链表;

import 链表动态数组.链表.双向链表.双向链表;

public class LinkedList<E> extends 双向链表<E>{
    /**
     * 清除所有元素
     */
    public void clear(){
        size=0;         
        first=null;
    }
    /**
     * 元素的数量
     */
    public int size() {
        return size;
    }
    /**
     * 判断是否为空
     */
    public boolean isEmpty(){
        return size==0;
    }
    /**
     * 判断是否包含某个元素
     */
    public boolean contains(E elements){
        双向链表.Node node=first;
        for (int i = 0; i < size; i++) {
            if(node.element==elements){
                return true;
            }
            node=node.next;
        }
        return false;
    }
    /**
     * 添加元素到尾部
     */
    public void add(int index,E element) {
        if(index==0){
            first=new 双向链表.Node<>(element,first);
        } else {
            双向链表.Node<E> prev=node(index-1);
            prev.next=new 双向链表.Node<>(element,prev.next);
        }
        size++;
    }

    /**
     *找到某一位置的节点并且返回
     */
    private 双向链表.Node<E> node(int index){
        双向链表.Node<E> node=first;
        for (int i = 0; i <index; i++) {
            node=node.next;
        }
        return node;
    }
    /**
     * 获取index位置的元素
     */
    public E get(int index) {
        return node(index).element;
    }
    /**
     * 设置index位置的元素
     */
    public E set(int index,E element){
        双向链表.Node<E> node=node(index);
        E old=node.element;
        node.element=element;
        return old;//返回原来的元素值
    }
 /*   *//**
     * 在index插入一个元素
     *//*
    public void add(int index,E elememt) {

    }*/
    /**
     * 删除index位置的元素
     */
    public E remove(int index){
        双向链表.Node<E> node=first;
        if(index==0){
            first=first.next;
        } else {
            双向链表.Node<E> prev=node(index-1);//找到前驱
            node=prev.next;//对node重新赋值哦
            prev.next=node.next;//进行删除
        }
        size--;
        return node.element;//把这个被删除的节点的元素值进行返回
    }
    /**
     * 查看元素的索引
     */
    public int indexOf(E element){
        if(element==null){
            双向链表.Node<E> node=first;
            for (int i = 0; i <size; i++) {
                if(node.element==null){
                    return i;
                }
                node= ((Node<E>) node).next;
            }
        } else {
            双向链表.Node<E> node=first;
            for (int i = 0; i <size; i++) {
                if(node.element.equals(element)){
                    return i;
                }
                node=node.next;
            }
        }
        return ELEMENT_NOT_FOUND;
    }
}

 注意:

环形链表用快慢指针  快慢指针初始指向位置是不同的

动态数组缩容操作:

 缩容和扩容差不多思想:把原来的元素移到新搞出来的数组内存

 二:双向链表

 找到对应index下标的节点node

因为这个节点node具有双指向性

next和prev指向 因此利用对称来找出对应索引处的节点

这样分一半一半的找

提高了效率

 

 clear:

当我们把LinkedList对象的两条线去掉以后

整个节点数组都会死掉

虽然说仍然有引用互相进行指向

但这些节点已经丧失了gc root对象的指向

JVM宣布他们这些节点全部死掉

那么作用之后只剩下:

 

 双向链表add:

first只关注第一个节点元素

last只关注最后一个节点元素 

 

 但如果最开始什么都没有:

last==null

 只有一个

  /**
     * 在index插入一个元素
     */
    public void addindex(int index,E elememt) {
        if (index < 0 || index > size) {
            return;
        }
        if (index == size) {
            Node oldlast=last;
            Node leo=new Node(element,oldlast,null);
            last=leo;
            if(oldlast==null){//当只有一个元素的时候first last都指向这个节点
                last=leo;
             first=leo;
            } else {
                oldlast.next=last;
            }
        } else {
            Node next = node(index);//找到要添加位置的节点
            Node prev = next.prev;//要添加的节点的上一个
            Node node = new Node(element, prev, next);
            next.prev = node;
            if (next.prev == null) {//index==0
                first = node;
            } else {
                prev.next = node;
            }
        }

 删除:

  /**
     * 删除index位置的元素
     */
    public void remove(int index){
        Node<E> node=node(index);
        Node<E> prev=node.prev;//这里的prev next就相当于一个节点了吧
        Node<E> next=node.next;
        if(prev==null){
            first=next;
        } else {
            prev.next=next;
        }
        if(next==null){
            last=prev;
        } else {
            next.prev=prev;
        }
    }

双向链表总代码:

package 链表动态数组.链表.双向链表;

import 链表动态数组.链表.单链表.leo;

public class 双向链表<E> extends leo<E> {
    //元素的数量
    protected int size;
    protected static final int ELEMENT_NOT_FOUND=-1;
    protected Node<E> first;
    private   Node<E> last;
    /**
     *内部类定义一个Node类
     */
 public static class Node<E>{
     public E element;
     Node<E> prev;
     public Node<E> next;
     public Node(E element, Node<E> prev, Node<E> next) {
         this.element = element;
         this.prev = prev;
         this.next = next;
     }
 }
    /**
     * 清除所有元素
     */
    public void clear(){
        size=0;
        first=null;
        last=null;
    }
    /**
     * 元素的数量
     */
    public int size() {
        return size;
    }
    /**
     * 判断是否为空
     */
    public boolean isEmpty(){
        return size==0;
    }
    /**
     * 判断是否包含某个元素
     */
    public boolean contains(E elements){
        Node node=first;
        for (int i = 0; i < size; i++) {
            if(node.element==elements){
                return true;
            }
            node=node.next;
        }
        return false;
    }
    /**
     *找到某一位置的节点并且返回
     * 双向链表利用对称性 大大提高了查找速率
     */
    private Node<E> node(int index){
        if(index<0||index>size){
            return null;
        }
        //当在左半边的时候
        if(index<(size>>1)){
            Node<E> node=first;
            for (int i = 0; i <index; i++) {
                node=node.next;
            }
            return node;
        } else {  //当在右半边的时候
            Node<E> node=last;
            for (int i =size-1; i <index; i++) {
                node=node.prev;
            }
            return node;
        }
    }
    /**
     * 获取index位置的元素
     */
    public E get(int index) {
       return node(index).element;
    }
    /**
     * 设置index位置的元素
     */
    public E set(int index,E element){
      Node<E> node=node(index);
      E old=node.element;
      node.element=element;
      return old;//返回原来的元素值
    }
    /**
     * 在index插入一个元素
     */
    public void addindex(int index,E elememt) {
        if (index < 0 || index > size) {
            return;
        }
        if (index == size) {
            Node oldlast=last;
            Node leo=new Node(element,oldlast,null);
            last=leo;
            if(oldlast==null){//当只有一个元素的时候first last都指向这个节点
                last=leo;
             first=leo;
            } else {
                oldlast.next=last;
            }
        } else {
            Node next = node(index);//找到要添加位置的节点
            Node prev = next.prev;//要添加的节点的上一个
            Node node = new Node(element, prev, next);
            next.prev = node;
            if (next.prev == null) {//index==0
                first = node;
            } else {
                prev.next = node;
            }
        }
    }
    /**
     * 删除index位置的元素
     */
    public void remove(int index){
        Node<E> node=node(index);
        Node<E> prev=node.prev;
        Node<E> next=node.next;
        if(prev==null){
            first=node;
        } else {
           prev.next=next;
        }
        if(next==null){
            last=node;
        } else {
            next.prev=prev;
        }
    }
    /**
     * 查看元素的索引
     */
    public int indexOf(E element){
        if(element==null){
            Node<E> node=first;
            for (int i = 0; i <size; i++) {
                if(node.element==null){
                    return i;
                }
                node=node.next;
            }
        } else {
            Node<E> node=first;
            for (int i = 0; i <size; i++) {
                if(node.element.equals(element)){
                    return i;
                }
                node=node.next;
            }
        }
        return ELEMENT_NOT_FOUND;
    }

}

 单向循环链表:这是单链表

 当什么都没有 第一次添加节点的时候

 

 删除:

 特殊情况:只有一个节点·

package 链表动态数组.链表.单链表;
//循环链表与单链表的不同之处就在于add和remove
public class 单向循环链表<E> extends 单链表<E> {
    @Override
    public void addindex(int index, Object elememt) {
        if(index<0||index>size){
            return;
        }
        if(index==0){
            Node node=new Node(elememt,first);
            first=node;
            //找到最后一个节点
            Node last=node(index-1);
            last.next=first;//循环链表的规定
        } else {
           Node<E> prev=node(index-1);
           Node node=new Node(elememt,prev.next);
           prev.next=node;
        }
    }
    @Override
    public E remove2(int index) {
        if(index<0||index>size){
            return null;
        }
        if(index==0){
            if(size==1){
              first=null;
            } else {
                Node<E> last=node(index-1);
                Node<E> node=new Node<>(last.element,first.next);
                last=node;
            }
        } else {
            Node prev=node(index-1);
            Node node=prev.next;
            prev.next=node.next;
        }
        size--;
        return null;
    }
}

 双向循环链表:

 

 

 双向循环链表remove:

但是当链表原来只有一个节点的时候

上面代码应当修改: 

 

代码:

package 链表动态数组.链表.双向链表;
//循环链表与单链表的不同之处就在于add和remove
public class 双向循环链表<E> extends 双向链表<E> {
    @Override
    public void addindex(int index, Object elememt) {
        if(index==size){
            Node oldlast=last;
            Node<E> node=new Node<E>((E)elememt,oldlast,first);
            if(oldlast==null){
                first=last;
                first.prev=first;
                first.next=first;
            } else {
                oldlast.next=node;
                first.prev=node;
            }
        } else {
            Node<E> next=node(index);
            Node<E> prev=next.prev;
            Node<E> node=new Node<E>((E) elememt,prev,next);
            next.prev=node;
            if(prev==null){//证明index==0
               first=node;
            } else {
                prev.next=node;
            }
        }
        size++;
    }

    @Override
    public void remove(int index) {
        if (index < 0 || index > size) {
            return;
        }
        Node<E> node = first;
        if (size == 1) {
            first = null;
            last = null;
        } else {
            node = node(index);
            Node prev = node.prev;
            Node next = node.next;
            prev.next = next;
            next.prev = prev;
            if(node==first){//index==0
                first=next;
            }
            if(node==last){//index==size-1
                last=prev;
            }
        }
    }
}

 

 1.

2.

 

 

 3.封装一个remove方法直接删除传进来的节点

 4.运行测试:

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值