数据结构--无头单向非循环链表模拟实现

       前言 

        逆水行舟,不进则退!!!     


       目录

       尾插法的实现

       任意位置插入的方法的实现

       删除所有关键字key方法的实现

       完整代码 


       尾插法的实现

        实现尾插法主要有两个关键点:1,判断链表是否为空;2,如何找到链表的最后一个结点;

        1,如果链表为空,也就是head为空,当head为空,那就令head指向需要尾插的结点;

        2,找到链表的最后一个结点也简单,通过循环来寻找就可以了,只是有一个地方需要注意:在我们找到最后一个结点后,是要将新的结点插入到找到的结点之后,而单向链表的性质决定了循环的判断条件。                  

        如图所示,需要将遍历链表的指针指在最后一个结点上,然后令cur.next = node;就可以了,

所以,循环的结束条件就是(cur.next != null) 而不是 (cur != null);  

      

//尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);
        if(head == null) {
            head = node;
        }else {
            ListNode cur = head;       //需要一个额外的指针去遍历链表
            while(cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    }


       任意位置插入的方法的实现

·        实现任意位置的插入,也是两个关键点:1,链表为空时要怎么办;2,index的值大于链表的长度时怎么处理;

        1,我的想法是,不单独考虑链表为空时,只考虑index的数值,当index == 0时,调用头插法,头插法内部就有判空程序,并且也简洁明了。

        2,第二个问题吗就是看判断条件怎么写了。中间位置也好,链尾位置也罢,都是要找到插入位置的前一个结点,不过这里吗,就和上面的尾插法的处理不一样了,因为还要考虑到链表为空时的情况。所以这里再定义一个计数器,直接找要插入位置的前一个位置,并且这个位置上的结点不为空。 条件这么写 : (cur != null && count != index - 1)   这么处理,即使链表为空,也不会报错,并且在index超出链表的长度时,也可以处理,只需要在循环后边加上一个判断语句 if(cur == null) { return false;}。 一举两得的写法。

        

//任意位置插入,第一个数据节点为0号下标
    public boolean addIndex(int index,int data) {
        ListNode node = new ListNode(data);
        if(index == 0) {         //如果index == 0,那就直接调用头插法           
           addFirst(data);
           return true;
        }else {
            ListNode cur = head;
            int count = 0;
            while(cur != null && count != index - 1) { //即使插入到链尾,找到的也是倒数第二个结点的位置,
                // 所以cur不能等于null
                cur = cur.next;
                count++;
            }
            if (cur == null) {  // cur等于null,说明index的下标大于链表的长度了
                return false;
            }
            node.next = cur.next;
            cur.next = node;
            return true;
        }
    }

       删除所有关键字key方法的实现

        这一部分的代码,我认为需要注意的地方有三点:1,如何寻找到所有的关键字key;2,循环的结束条件如何写;3,头结点为目标结点时的处理细节;

        1,因为单向链表的性质,删除一个结点同样需要前一个结点。如果只有一个指针的话,也可以处理,不过我这里用到的是双指针,更容易理解。

        2,因为用的是双指针,这里的判断条件是,快的那个指针不等于空 即: (curNext != null);

        3,上述的处理方法,是从第二个结点开始遍历数组查找关键字key,当处理完后,就只剩头结点还没有遍历,所以最后再来一个判断语句来处理头结点。这里也是一个细节。不能先处理头结点,万一头几个结点都是目标结点,就不好处理了。

//删除所有值为key的节点
    public void removeAllKey(int key) {
        ListNode cur = head;
        ListNode curNext = head.next;   //同样使用的是双指针
        while(curNext != null) {        //这里的判断条件如果是(curNext.next != null),就拿不到最后一个结点的val值。
            if(curNext.val == key) {
                cur.next = curNext.next;
                curNext = curNext.next;
            }else {
                curNext = curNext.next;
                cur = cur.next;
            }
        }
        if(head.val == key) {     //最后在处理头结点,是为了避免前两个结点都是目标结点。
            head = head.next;     //因为上面的代码已经将头结点后面的所有的目标节点都处理完了。就只剩下头结点没有处理。 这是这样写代码的巧妙。
        }
    }


       完整代码 
// 无头单向非循环链表实现
public class SingleLinkedList {
    static class ListNode {
        public int val;
        public ListNode next;
        public ListNode(int val) {
            this.val = val;
        }

    }

    public ListNode head;  //头指针
    //头插法
    public void addFirst(int data){
        ListNode node = new ListNode(data);
        if(head == null) {
            head = node;
        }else {
            node.next = head;
            head = node;
        }
    }
    //尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);
        if(head == null) {
            head = node;
        }else {
            ListNode cur = head;       //需要一个额外的指针去遍历链表
            while(cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    }
    //任意位置插入,第一个数据节点为0号下标
    public boolean addIndex(int index,int data) {
        ListNode node = new ListNode(data);
        if(index == 0) {         //如果index == 0,那就直接调用头插法           
           addFirst(data);
           return true;
        }else {
            ListNode cur = head;
            int count = 0;
            while(cur != null && count != index - 1) { //即使插入到链尾,找到的也是倒数第二个结点的位置,
                // 所以cur不能等于null,
                cur = cur.next;
                count++;
            }
            if (cur == null) {  // cur等于null,说明index的下标大于链表的长度了
                return false;
            }
            node.next = cur.next;
            cur.next = node;
            return true;
        }
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur = head;
        while(cur != null) {
            if(cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        if(head.val == key) {   // 若头结点为目标节点
            head = head.next;   // 将head往后移
            return;             // 结束
        }
        ListNode cur = head;
        ListNode curNext = head.next;   //如果删掉一个中间节点,还要用到该节点的前驱结点,
        while(curNext.val != key) {     // 所以用双指针来遍历链表  也叫快慢指针。
            curNext = curNext.next;
            cur = cur.next;
        }
        cur.next = curNext.next;
        curNext.next = null;
    }
    //删除所有值为key的节点
    public void removeAllKey(int key) {
        ListNode cur = head;
        ListNode curNext = head.next;   //同样使用的是双指针
        while(curNext != null) {        //这里的判断条件如果是(curNext.next != null),就拿不到最后一个结点的val值。
            if(curNext.val == key) {
                cur.next = curNext.next;
                curNext = curNext.next;
            }else {
                curNext = curNext.next;
                cur = cur.next;
            }
        }
        if(head.val == key) {     //最后在处理头结点,是为了避免前两个结点都是目标结点。
            head = head.next;     //因为上面的代码已经将头结点后面的所有的目标节点都处理完了。就只剩下头结点没有处理。 这是这样写代码的巧妙。
        }
    }
    //得到单链表的长度
    public int size() {
        ListNode cur = head;
        int count = 0;
        while(cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }
    public void display() {
        ListNode cur = head;
        while(cur != null) {  这里的判断条件如果是(cur.next != null),就拿不到最后一个结点的val值。
            System.out.println(cur.val);
            cur = cur.next;
        }
    }
    public void clear() {
        head = null;
    }
}


        我是专注学习的章鱼哥~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值