DS_【单链表】

数组和链表都是线性存储结构的基础,栈和队列都是线性存储结构的应用~
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的引用链
接次序实现的
在这里插入图片描述
组成:
由n个节点离散的分配,每个节点由头结点和尾节点组成,头结点存放date,尾节点存放下个头结点的引用,在最后的节点没有下一个节点nulll

无头定义

//不带头的
private Node head;
    class Node{
        private int data;
        private Node next;

        public Node(int data) {
            this.data=data;
            this.next=null;
        }
    }

无头的链表在new的时候不含头,在空间存储上也没有。

01单向链表的接口
public interface ILinked {
    //头插法
    void addFirst(int data);
    //尾插法
    void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    boolean addindex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    boolean contains(int key);
    //删除第一次出现关键字为key的节点
    int remove(int key);
    //删除所有值为key的节点
    void removeAllKey(int key);
    //得到单链表的长度
    int getLength();
    void display();
    void clear();
}

实现单链表

01addFirst【头插法】
//无头
 @Override
    public void addFirst(int data) {
        // TODO Auto-generated method stub
        //第一次插入数据
        Node node = new Node(data);
        if(this.head==null) {
            this.head=node;
        }else {
            node.next=this.head;
            this.head=node;
        }
    }

考虑到没有头结点所以在插入式第一次插入,链表为空,此时插入的数据唯一特殊情况,要考虑。

定义一个新的节点 node 对node进行判定
当进行头部插入时,如果当前链表的头结点 head为空 将这个新的节点定义为头结点,假如不为null 将新建节点的next指向之前头结点的head
在这里插入图片描述

02尾插法
//无头
 @Override
    public void addLast(int data) {
        // TODO Auto-generated method stub
        Node node = new Node(data);
        Node cur = this.head;
        if(cur==null) {
            this.head=node;
        }else {
            while(cur.next!=null) {
                cur=cur.next;
            }
            cur.next=node;
        }
    }

在操作时考虑到链表可能为null

创建一个新的节点node 对所有节点进行遍历找出尾节点然后将尾节点指向新节点
在这里插入图片描述

03addindex【添加指定位置的节点】
01核对下标是否合法

下标的取值范围

//无头与带头相同
 private void checkIndex(int index) {
        if(index<0||index>getLength()) {
            throw new IndexOutOfBoundsException("下标不合法");
        }
    }
02寻找index-1 未知的节点
//无头寻找index前一个节点
private Node serchIndex(int index) {
        checkIndex(index);
        //记录从头到尾的步数
        int count=0;
        if(index==0){
        return null;
     }
    Node cur = this.head;
        while(cur.next!=null&&count<index-1){
            cur=cur.next;
            count++;
        }
        return cur;
    }

或者

 private Node searchPre(int key){
        //判断是不是第一个节点
        Node cur = this.head;           //创建头结点
        if(head.data==key){            //如果头结点符合情况 返回头结点
            return cur;
        }
            //找到符合key节点的前一个节点
            while(cur.next!=null){          //将整个链表从头到尾遍历 结束是最后一个next=null
                if(cur.next.data==key){
                    return cur;
                }else{
                    cur=cur.next;               //不符合情况则下一个
                }
            }
        return null;
    }

通过遍历头结点到index-1的每经过依次next 计数器加一cur.next!=null&&count<index-1

//无头
	 @Override
    public boolean addindex(int index, int data) {
        // TODO Auto-generated method stub
        Node node = new Node(data);
        Node cur = serchIndex(index);
        if(cur==null){
            node.next=this.head;
            this.head=node;
        }else{
            node.next=cur.next;
            cur.next=node;
        }
        return true;
    }

因为本身链表无头所以不能直接将index拿来做便利条件,必须判断index合法情况,在此基础上使用serachIndex函数去找前一个位置

思路 :
1.通过serchIndex找出index-1上一个节点的next节点
2.然后打断index-1和index节点的连接现将node的next节点指向index的头结点,然后将index-1的next指向node的头节点

03判断是否有元素
	 //判断是否有元素
    @Override
    public boolean contains(int key) {
        // TODO Auto-generated method stub
       Node node  = this.head;
       while(node!=null){
           if(node.data==key){
               return true;
           }else{
               node=node.next;
           }
       }
       return false;
    }

通过反复遍历每一个节点通过对比寻找元素

04删除节点
@Override
    public int remove(int key) {
        // TODO Auto-generated method stub
        int oldData = 0;
        Node pre = searchPre(key);          //找到符合情况的前一个节点
        if(pre==null){
            throw new UnsupportedOperationException("无节点");
        }
       if(pre==this.head&&pre.data==key){               //排除当目标节点为第二个节点时 前一节点为head
           oldData=this.head.data;          //拿到数据存放
           this.head=this.head.next;
           return oldData;
       }
       Node delNode=pre.next;               //拿到前一个节点next节点
       oldData = delNode.data;
       pre.next = delNode.next;             //与目标节点下一个节点相连接
       return oldData;
    }
05removeAllKey
 @Override
    public void removeAllKey(int key) {
        // TODO Auto-generated method stub
       Node cur = this.head.next;           //pre cur   curNdext cur
       Node pre = this.head;
       while(cur!=null){
           if(cur.data==key){
                pre.next=cur.next;
                cur=pre.next;
           }else{
               pre=cur;
               cur=pre.next;
           }
       }
       if(this.head.data==key){
           this.head=this.head.next;
       }
     }

总结:
1.单向不循环链表在执行头插法或者尾插时要考虑头结点因素
2.画图更能掌握单向链表

**总代码
public class Linkedimpl implements ILinked {
    private Node head;
    class Node{
        private int data;
        private Node next;

        public Node(int data) {
            this.data=data;
            this.next=null;
        }
    }
   @Override
    public void addFirst(int data) {
        // TODO Auto-generated method stub
        //第一次插入数据
        Node node = new Node(data);
        if(this.head==null) {
            this.head=node;
        }else {
            node.next=this.head;
            this.head=node;
        }
    }
   @Override
    public void addLast(int data) {
        // TODO Auto-generated method stub
        Node node = new Node(data);
        Node cur = this.head;
        if(cur==null) {
            this.head=node;
        }else {
            while(cur.next!=null) {
                cur=cur.next;
            }
            cur.next=node;
        }
    }
    //寻找index -1 位置的节点
    private Node serchIndex(int index) {
        checkIndex(index);
        //记录从头到尾的步数
        int count=0;
        if(index==0){
        return null;
     }
    Node cur = this.head;
        while(cur.next!=null&&count<index-1){
            cur=cur.next;
            count++;
        }
        return cur;
    }
    private void checkIndex(int index) {
        if(index<0||index>getLength()) {
            throw new IndexOutOfBoundsException("下标不合法");
        }
    }
    @Override
    public boolean addindex(int index, int data) {
        // TODO Auto-generated method stub
        Node node = new Node(data);
        Node cur = serchIndex(index);
        if(cur==null){
            node.next=this.head;
            this.head=node;
        }else{
            node.next=cur.next;
            cur.next=node;
        }
        return true;
    }
    //判断是否有元素
    @Override
    public boolean contains(int key) {
        // TODO Auto-generated method stub
       Node node  = this.head;
       while(node!=null){
           if(node.data==key){
               return true;
           }else{
               node=node.next;
           }
       }
       return false;
    }
    private Node searchPre(int key){
        //判断是不是第一个节点
        Node cur = this.head;           //创建头结点
        if(head.data==key){            //如果头结点符合情况 返回头结点
            return cur;
        }
            //找到符合key节点的前一个节点
            while(cur.next!=null){          //将整个链表从头到尾遍历 结束是最后一个next=null
                if(cur.next.data==key){
                    return cur;
                }else{
                    cur=cur.next;               //不符合情况则下一个
                }
            }
        return null;
    }
    @Override
    public int remove(int key) {
        // TODO Auto-generated method stub
        int oldData = 0;
        Node pre = searchPre(key);          //找到符合情况的前一个节点
        if(pre==null){
            throw new UnsupportedOperationException("无节点");
        }
       if(pre==this.head&&pre.data==key){               //排除当目标节点为第二个节点时 前一节点为head
           oldData=this.head.data;          //拿到数据存放
           this.head=this.head.next;
           return oldData;
       }
       Node delNode=pre.next;               //拿到前一个节点next节点
       oldData = delNode.data;
       pre.next = delNode.next;             //与目标节点下一个节点相连接
       return oldData;
    }
    @Override
    public void removeAllKey(int key) {
        // TODO Auto-generated method stub
       Node cur = this.head.next;           //pre cur   curNdext cur
       Node pre = this.head;
       while(cur!=null){
           if(cur.data==key){
                pre.next=cur.next;
                cur=pre.next;
           }else{
               pre=cur;
               cur=pre.next;
           }
       }
       if(this.head.data==key){
           this.head=this.head.next;
       }
     }
    @Override
    public int getLength() {
        // TODO Auto-generated method stub
        int len=0;
        Node node = this.head;
        while(node!=null) {
            len++;
            node=node.next;
        }
        return len;
    }
    @Override
    public void display() {
        // TODO Auto-generated method stub
        Node node = this.head;
        if(node==null){
            System.out.println("wudi");
        }
        while(node!=null) {
            System.out.print(node.data+" ");
            node=node.next;
        }
    }
    @Override
    public void clear() {
        // TODO Auto-generated method stub
       while(this.head.next!=null){
           Node cur = this.head.next;
           this.head= null;
           this.head = cur;
       }
    }

在执行clear操作时,要保证没一个节点得到释放

单向带头循环链表
public class CLinkedimpl implements ICLinked {
    class Node{
        private Node next;
        private int data;
        public Node(){
            this.data=-1;
        }
        public Node(int data){
            this.data=data;
        }
    }
    private Node head;
    public CLinkedimpl(){
        this.head=new Node();
        this.head.next=this.head;
    }
    @Override
    public void addFirst(int data) {
        Node node  = new Node(data);
        node.next = this.head.next;
        this.head.next=node;
    }
    @Override
    public void addLast(int data) {
        Node node = new Node(data);
        Node cur = this.head;
        while(cur.next!=this.head){
            cur = cur.next;
        }
        node.next=this.head;
        cur.next=node;
    }
    private void checkIndex(int index){
        if(index<0||index>getLength()){
            throw new IndexOutOfBoundsException("下标不合法");
        }
    }
    @Override
    public boolean addIndex(int index, int data) {
        Node node  = new Node(data);
        Node cur = this.head;
        checkIndex(index);
        for(int i=0;i<index;i++){
            cur = cur.next;
        }
        node.next = cur.next;
        cur.next=node;
        return true;
    }

    @Override
    public boolean contains(int key) {
        Node cur = this.head;
        while(cur.next!=this.head){
            if(cur.next.data==key){
                return true;
            }else{
                cur=cur.next;
            }
        }
        return false;
    }

    private Node searchPre(int key){
        Node pre = this.head;
        while(pre.next!=this.head){
            if(pre.next.data==key){
                return pre;
            }else{
                pre=pre.next;
            }
        }
        return null;
    }
    @Override
    public int remove(int key) {
        int oldData=0;
        Node pre = searchPre(key);
        if(pre==null){
            System.out.print("无该节点");
        }

        oldData = pre.next.data;
        pre.next=pre.next.next;
        /**
         * Node delNode = pre.next;
         * oldData = delNode.data;
         * pre.next = delNdoe.next;
         */
        return oldData;
    }

    @Override
    public void removeAllKey(int key) {
        int oldData = 0;
        Node pre = this.head;
        Node cur = this.head.next;
        while(cur!=this.head){
           if(cur.data==key){
               pre.next=cur.next;
               cur=cur.next;
           }else{
               cur=pre;
               cur=cur.next;
           }
        }
    }

    @Override
    public int getLength() {
        Node node = this.head.next;
        int count = 0;
        while(node!=this.head){
            count++;
        }
        return count;
    }

    @Override
    public void display() {
        Node node = this.head.next;
        while(node!=this.head){
            System.out.print(node.data+" ");
            node=node.next;
        }
        System.out.println();
    }

    @Override
    public void clear() {
        while(this.head.next!=this.head){
            Node cur = this.head.next;
            this.head.next=cur.next;
            cur.next=null;
        }
    }
}

带头循环的优点,相比于无头节点,在执行头插法或者尾插法不用考虑头结点。执行遍历时终止条件是转一个圈的头结点
在这里插入图片描述
生命不息!!!奋斗不止!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值