> 文档中心 > [数据结构] 链表之一(胖黑)毁所有 (无头单向非循环链表实现)

[数据结构] 链表之一(胖黑)毁所有 (无头单向非循环链表实现)

目录

往期回顾,专栏一览

🔴 链表

🔵 链表的概念及结构

🔵 无头单向非循环链表实现

🔵 黑胖毁所有

    

往期回顾,专栏一览

🍉 JavaSE 🍋 AWT 🍑 数据结构
🍅 C1进阶之路 🍒 每日一练 🌽 代码报错
🍈 活动

🍹欢迎各路大佬来到 Nick 主页指点

☀️本期文章将学习 [数据结构] 链表之一(胖黑)毁所有 (无头单向非循环链表实现),我是博主Nick。✨

✨我的博客主页:Nick_Bears 🌹꧔ꦿ

🌹꧔ꦿ博文内容如对您有所帮助,还请给个点赞 + 关注 + 收藏

   

🔴 链表

🔵 链表的概念及结构

🔶 链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。

  

    

  

    

🔶 实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  • 单向、双向
  • 带头、不带头
  • 循环、非循环

    

    

虽然有这么多的链表的结构,但是我们重点掌握两种:

   

🔷 无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希、桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

     

        

🔷 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

     

        

🔵 无头单向非循环链表实现

💠 提供以下方法

// 1、无头单向非循环链表实现public class SingleLinkedList {     //头插法     public void addFirst(int data);     //尾插法     public void addLast(int data);     //任意位置插入,第一个数据节点为0号下标     public boolean addIndex(int index,int data);     //查找是否包含关键字key是否在单链表当中     public boolean contains(int key);     //删除第一次出现关键字为key的节点     public void remove(int key);     //删除所有值为key的节点     public void removeAllKey(int key);     //得到单链表的长度     public int size();     public void display();     public void clear(); }

      

💠 方法实现

package 链表.头插尾插;// ListNode 代表一个节点class ListNode {    public int val;    public ListNode next;    public ListNode(int val) { this.val = val;    }}public class MyLinkedList {    public ListNode head;//链表的头    //用于帮助新手理解    public void creatList() { ListNode listNode1 = new ListNode(1); ListNode listNode2 = new ListNode(13); ListNode listNode3 = new ListNode(11); ListNode listNode4 = new ListNode(17); ListNode listNode5 = new ListNode(54); listNode1.next = listNode2; listNode2.next = listNode3; listNode3.next = listNode4; listNode4.next = listNode5; //listNode5.next = null; this.head = listNode1;    }    //这样做不可行,我的头没了//    public void display(){// //this.head.next!=null少打印一个// while(this.head!=null){//     System.out.println(this.head.val+" ");//     this.head = this.head.next;// }//    }    //遍历    public void display() { ListNode cur = this.head; while (cur != null) {     System.out.print(cur.val + " ");     cur = cur.next; } System.out.println();    }    // 1、无头单向非循环链表实现    //头插法    public void addFirst(int data) {//一定记住,绑定的时候,一定要先绑定后面 ListNode node = new ListNode(data); node.next = head; this.head = node;// if(this.head == null){//     this.head = node;// }else{//     node.next = head;//     this.head = node;// }    }    //尾插法    public void addLast(int data) {//尾插第一次必须要判断 ListNode node = new ListNode(data); if (head==null) {     this.head = node; }else{     ListNode cur = this.head;     while (cur.next!= null) {  cur = cur.next;     }     cur.next = node; }    }    //任意位置插入,第一个数据节点为0号下标    public void addIndex(int index, int data) { if(indexsize()){     System.out.println("index位置不合法");     return; } if(index==0){     addFirst(data);     return; } if(index==size()){     addLast(data);     return; } ListNode cur = findIndex(index); ListNode node = new ListNode(data); node.next = cur.next; cur.next = node;    }    //找到插入前驱index-1    public ListNode findIndex(int index){ ListNode cur = this.head; while(index-1!=0){     cur = cur.next;     index--; } return cur;    }    //查找是否包含关键字key是否在单链表当中    public boolean contains(int key) { ListNode cur = this.head; while (cur != null) {     if (cur.val == key) {  return true;     }     cur = cur.next;//先判断再走,不然会错过第一个 } return false;    }    //找到删除的关键字的前驱    public ListNode searchPerv(int key){ ListNode cur = this.head; while(cur.next!=null){     if(cur.next.val==key){  return cur;     }     cur = cur.next; } return null;    }    //删除第一次出现关键字为key的节点    public void remove(int key) { if(this.head == null){     System.out.println("单链表为空,不能删除");     return; } if(this.head.val==key){     this.head=this.head.next;     return; } ListNode cur = searchPerv(key); if(cur == null){     System.out.println("没有你要删除的节点");     return; } ListNode del = cur.next; cur.next = del.next;    }    //遍历链表一遍,删除所有值为key的节点(一个思路,代码后面要加最后位的判断)//    public void removeAllKey(int key){// if(this.head == null){//     System.out.println("单链表为空,不能删除");//     return;// }// if(this.head.val==key){//     this.head=this.head.next;//     return;// }// ListNode last_cur = this.head;// ListNode cur =this.head.next;// int count = 0;// int size = this.size();// while(cur.next!=null && count < size){//     ListNode cur_next = cur.next;//     if(cur.val==key){//  last_cur.next = cur_next;//     }else{//  last_cur = cur;//     }//     cur = cur.next;//     count++;// }//    }    //推荐    public ListNode removeAllKey(int key) { if(this.head==null){     return null; } ListNode prev = this.head; ListNode cur = this.head.next; while(cur !=null){     if(cur.val==key){  prev.next = cur.next;  cur = cur.next;     }else{  prev = cur;  cur = cur.next;     } } if(this.head.val == key){     this.head = this.head.next; } return this.head;    }    //得到单链表的长度    public int size() { int count = 0; ListNode cur = this.head; while (cur != null) {     count++;     cur = cur.next; } return count;    }    //温柔的清空链表    public void clear() { while(head!=null){     ListNode curNext = head.next;     head.next = null;     head = curNext; }    }}

   

💠 方法测试

package 链表.头插尾插;public class Demo {    public static void main(String[] args) { MyLinkedList myLinkedList=new MyLinkedList(); myLinkedList.addFirst(1); myLinkedList.addFirst(1); myLinkedList.addFirst(1); myLinkedList.addFirst(2); myLinkedList.addFirst(2); myLinkedList.addFirst(2); myLinkedList.addIndex(0,10); myLinkedList.removeAllKey(2); System.out.println(myLinkedList.size()); myLinkedList.clear(); myLinkedList.addFirst(10); myLinkedList.display();    }}

   

💠 难点分析

这是Nick自己写了一遍总结的一些卡点 先理解,后实践 (可能和上面不完全一样)

//心得体悟1.为什么遍历时?是cur!=null,而lastAdd是cur.next!=null;    public void display(){ NodeList cur = head; while(cur!=null){     System.out.print(cur.val+" ");     cur = cur.next; } System.out.println();    }答:因为遍历时目标是确定自身是否为空(为空跳出循环,不为空sout),添加元素时是判断下一个节点是否为空(为空cur.next = node,不为空 cur = cur.next)2.尾插法最后赋值时为什么是cur.next = node;而不能反着写?    public void lastAdd(int date){ NodeList cur = head; NodeList node = new NodeList(date); while(cur.next!=null){     cur = cur.next; } cur.next = node;    }答:node.next指向是空,若node.next=cur意味着,cur的前一个元素是node,但是实际情况是前一个元素已经"另有所指"了,因此不能往前插,只有cur的后面是空滴,所以cur.next = node;3.查询一个元素删除前驱时,为什么while中条件是cur.next!=null,而不是cur!=null;呢?    public NodeList getPrev(int val){ NodeList cur = head; while(cur.next!=null){     if(cur.next.val==val){  return cur;     } } return null;    }答:前驱为cur,删除元素为cur.next,既然你要删cur.next,那么你就是要保证有这个元素,因此判断cur.next不为空4.小坑:通过前驱找删除的,第0个没有前驱,因此要加一个判断。    //删除元素    public void remove(int val){ if(head.val==val){     head=head.next;     return; } if(container(val)){     NodeList cur = getPrev(val);     NodeList del = cur.next;     cur.next = del.next; }else{     System.out.println("该链表没有这个元素"); }    }//删除前驱    public NodeList getPrev(int val){ NodeList cur = head; while(cur.next!=null){     if(cur.next.val==val){  return cur;     } } return null;    }5.为啥清空链表是head.next而不是head?    //清空链表    public void clear(){ while(head!=null){     NodeList curNext = head.next;     head.next = null;     head = curNext; }    }答:不要担心第一个删不掉,这个代码先删除了第一个的后继,通过head = curNext最终会head=null;

   

💠 做题时画了一些图

   

头插法

   

尾插法

     

添加节点

      

删一个元素 

       

删除所有

点我跳转

      

💠 总结

1、你要搞清楚前驱后继

2、一定记住,绑定的时候,一定要先绑定后面(经验)

3、不要把head头跑没了,因为黑胖毁所有

    

🔵 黑胖毁所有

🔶 链表:一(胖黑)毁所有

  

🔶 胖黑:以节点为单位存储,不支持随机访问

   

🔶 所有:

  • 1.任意位置插入删除时间复杂度为O(1)
  • 2.没有增容问题,插入一个开辟一个空间。

   

QQ头像吧