🍉链表
🌵链表概念及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
链表结构非常多样,以下情况组合起来有8种链表结构:
- 单向、双向
- 带头、不带头
- 循环、非循环
我们重点学习以下两种链表
🍌无头单向非循环链表接口实现(注释非常详细,我👴👴都能看懂)
先写两个类,一个是链表类(包含有一个可变的头结点和实现各种功能的接口,因为是无头链表,所以这个头结点是可变的),一个是节点类(成员属性有value和next)
接下来的接口都是写在链表类里的,因为链表是一个对象,我们要实现的就是一个链表对象所有的功能
🍈打印链表
打印链表其实和打印顺序表类似,遍历链表就好了,不过要注意一个点,这里需要引入一个局部变量cur来代替头节点去遍历,因为头结点在没添加或者删除节点之前是固定的,不要让头结点变来变去
//打印链表 public void display(){ ListNode cur = this.head;//利用创建一个局部变量cur来替代head,这样头结点就不会改变了 while (cur!=null) {//遍历的条件就是下一个节点的引用(地址)不为null System.out.print(cur.value+" "); cur = cur.next;//找到下一个节点 } System.out.println(); }
🍈头插法插入
顾名思义,头插法就是从头部插入节点,使新创建的节点成为新的头结点,这里需要额外考虑一个点,就是头结点是否存在(链表是否为空),不过以下代码能处理头结点为空的情况(注意要先将原头结点的地址先存入新节点,再将新节点引用赋给原头结点成为新头结点)
//头插法 public void addFirst(int data){ ListNode node = new ListNode(data);//创建新节点并初始化节点的data node.next = this.head;//将当前头结点地址存到新节点的next里 this.head = node;//将新创建的节点变为头结点 //这两行代码包含了头结点为null的情况 }
🍈尾插法插入
尾插法不同于头插法,必须先判断链表是否为空(判断头结点是否为null),然后引入局部变量cur遍历链表,直到cur.next为空的时候,说明找到尾节点了,此时的cur就是尾巴结点
//尾插法 public void addLast(int data){ //找尾巴,cur.next为null了,说明这是尾节点了 ListNode node = new ListNode(data);//创建新节点并初始化节点的data if (this.head == null){ //尾插的第一次必须判断头结点是否为空 this.head = node;//如果是第一次插入,则新节点就是头结点 } ListNode cur = this.head; //引入局部变量cur遍历链表 while(cur.next != null){ //next等于null了就跳出while了 cur = cur.next;//找到下一个节点 } cur.next = node; }
🍈查找是否包含关键字key在单链表当中
传入关键字key,依旧引入局部变量cur遍历链表,哪个节点的value等于key了,说明链表里有这个关键字,返回true,否则返回false
//查找是否包含关键字key是否在单链表当中 public boolean contains(int key){ ListNode cur = this.head;//引入局部变量cur遍历 while(cur != null){//循环条件为节点引用不为null if (key == cur.value) return true;//如果找到了,返回true cur = cur.next;//找到下一个节点 } return false; }
🍈得到单链表的长度
依旧采取引用局部变量cur来遍历链表,还要多设置一个局部变量size来计数,只要节点不为null,size就+1,最后返回size的值就是链表长度了
//得到单链表的长度 public int size(){ int size=0;//引入局部变量来计数 ListNode cur = this.head; while(cur != null){//遍历并计数 size++;//节点不为null,计数器+1 cur = cur.next;//找到下一个节点 } return size;//返回链表长度 }
🍈任意位置插入,第一个数据节点为0号下标
首先得判断你要插入的这个位置是否合法,然后这里需要额外写一个查找插入位置前一个节点的方法findIndex()用于插入节点,插入原理
//根据传入的index查找前一个节点并返回地址 public ListNode findIndex(int index){ ListNode cur = this.head;//引入局部变量遍历至index前一节点 while (index-1 != 0){//停止条件就是index-1等于0了 //也就是说遍历到index位置上一个节点了 cur = cur.next;//向后遍历 index--;//每向后找一个节点index减1 } return cur;//返回index上一个节点引用 } //任意位置插入,第一个数据节点为0号下标 public void addIndex(int index,int data){//需要创建一个查找index位置前一节点的函数 if (index > 0 && index < size()) {//判断插入位置是否合法 ListNode node = new ListNode(data);//创建新节点并初始化节点的data node.next = findIndex(index).next;//通过上边写的查找方法将index位置前一节点的下一节点引用赋给新插入节点的next findIndex(index).next = node;//将新节点的引用存到查找到的节点的next达到链接效果 } else if (index==0) {//如果插入位置是0,则直接使用头插法插入 addFirst(data); return; } else if(index==size()) {//如果插入位置是链表长度值,则直接使用尾插法插入 addLast(data); return; } else System.out.println("位置不合法!"); return; }
🍈删除第一次出现关键字为key的节点
首先判断头结点是否为null(链表是否为空),然后有两种情况
①关键字在头结点:将头结点下一个节点设置为新头结点
②关键字不在头结点:将有关键字的节点的下一节点引用赋给有关键字节点的上一节点的next
//删除第一次出现关键字为key的节点 public void remove(int key){ if (this.head == null){//判断链表是否为空 System.out.println("链表为空,不能删除"); return; } ListNode cur = this.head; while(cur.next != null){//遍历链表 if(cur.value == key) {//①关键字在头结点:将头结点下一个节点设置为新头结点 head = cur.next; return; } else if(cur.next.value == key) {//②关键字不在头结点:将有关键字的节点的下一节点引用赋给有关键字节点的上一节点的next cur.next = cur.next.next; return; } cur = cur.next; } System.out.println("没有你要删除的节点"); }
🍈删除所有值为key的节点
和上边删除第一次出现的key类似,只不过return换成了continue,再有就是需要设置一个局部变量size,删除过程进行完之后若删除前设置的size值没发生改变,则说明没有删除节点
//删除所有值为key的节点 public void removeAllKey(int key){ int size = size(); if (this.head == null){ System.out.println("链表为空,不能删除"); return; } ListNode cur = this.head; while(cur.next != null){ if(cur.value == key) { head = cur.next; continue;//删除之后不要return,继续遍历 } else if(cur.next.value == key) { cur.next = cur.next.next; continue;//删除之后不要return,继续遍历 } cur = cur.next; } if(size()==size) {//如果进行删除前设置的size等于进行删除后size()方法返回的值,说明没有进行删除操作 System.out.println("没有你要删除的节点"); } }
🍈清空链表
暴力清空,直接将头结点置空,这样整个链表就无法找到了
//清空链表 public void clear(){ this.head = null; }
顺序表和链表的区别与联系
顺序表
顺序表:一白遮百丑
白:空间连续、支持随机访问
丑:1.中间或前面部分的插入删除时间复杂度O(N) 2.增容的代价比较大。
链表
链表:一(胖黑)毁所有
胖黑:以节点为单位存储,不支持随机访问
所有:1.任意位置插入删除时间复杂度为O(1) 2.没有增容问题,插入一个开辟一个空间