数据结构:链表

链表的概念及结构:

链表的概念:

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

那物理存储结构连续是什么意思?

之前我们讲过顺序表,顺序表的底层是数组,如下:

因为数组开辟的空间,是连续的,这里所储存的数据,它们在物理储存结构上是连续的

链表的结构:

链表是一个节点链接另一个节点的,一个节点都是一个对象。

如果我用链表储存11,22,33,44,55这几个数据,首先我们有五个节点,因为节点是对象,所以会分配地址。

并把这些数据储存到对应的节点里面,然后再将他们连接起来。

这个链表叫做单向不带头非循环链表

这是单向带头非循环链表,两个的区别是,带头链表 多了一个头(也叫哨兵位),头插一个元素时,带头链表会将新的元素放在头(哨兵位)的后面。带头链表的值没有意义,所以也可以不用放数据。但是不带头链表,头插的时候,头插在第一个元素的前面即可。

链表分类:

这就是循环链表,最后一个节点又指向第一个节点,形成一个圈。

单向不带头非循环链表的实现:

我先创建一个内部类(节点):

java 复制代码
static class ListNode(){
    int val;
    Listnode next;
//构造方法
    public ListNode(int val){
        this.val=val;
    }
}
//还要有一个头结点
ListNode head=null;

在ListNode类中,val用来存储数据,而next用来指向下一个节点,再写一个构造方法用来初始化val值。

定义一个头节点可以利于遍历链表。

实现链表dispaly(打印链表):

如何打印上面链表?我们要打印出每个节点的val值,并利用每个节点的next找到下一个节点。

一直这样循环,一直到最后一个节点的next值为null停止。

java 复制代码
public void dispaly(){
    ListNode cur=head;
    while(next!=null){
      System.out.print(cur.val+" ");
      cur=cur.next;
    }
}

这里要注意一点:不要直接拿头结点head去遍历链表,会导致最后head指向链表的尾部,后续操作就不方便了。所以重新定义一个cur节点等于头结点。用cur来遍历数组。

实现链表addFirst(头插):

在实现链表头插时要分情况:

1.如果链表为空:

这种情况,只用定义新的节点,让头结点指向新节点

java 复制代码
public void addFirst(int data){
//链表为空的情况
    if(head==null){
   ListNode node=new ListNode(data);
   head=node;
 }
}

2、如果链表不为空:

这种情况怎么头插呢?

首先:我们还是要第一个新的节点,让这个新的节点的next值指向head头节点,最后将head头节点指向新的头结点。

java 复制代码
public void addFirst(int data){
//链表为空的情况
    if(head==null){
   ListNode node=new ListNode(data);
   head=node;
//链表不为空的情况
 }else{
    ListNode node=new ListNode(data);
    node.next=head;
    head=node;
  }
}

最后发现,这两种情况可以合并为:

java 复制代码
public void addFirst(int data){
    ListNode node=new ListNode(data);
    node.next=head;
    head=node;
  }

实现链表addLast(尾插):

实现尾插时也要分情况

1.如果链表不为空:

如果对这个链表进行尾插,应该怎么做呢?

首先我们要遍历链表到这个链表的尾部,然后将要插入的节点与尾节点连接,完成尾插。

java 复制代码
public void addLast(int data){
//创建一个新的节点
    ListNode node=new ListNode(data);
    ListNode cur=head;
//遍历到链表尾部
    while(cur.next!=null){
    cur=cur.next;
    }
//插入尾部
    cur.next=node;
}

2.如果链表为空:

如果用上面1的方法,会出现空指针异常。

所以如果为空链表,我们就直接将head头节点指向需要插入的节点,加一个if语句进行判断。

java 复制代码
public void addLast(int data){
    ListNode node=new ListNode(data);
//为空链表的情况
    if(head==null){
    head=node;
    }
//不为空链表的情况
    ListNode cur=head;
    while(cur.next!=null){
    cur=cur.next;
    }
    cur.next=node;
}

实现链表size(求链表长度):

既然要求链表的长度,并定义一个计数器,遍历链表时,只要当前节点不为null,计数器就加1

注意:这里遍历链表时,我们不用head,重新定义一个节点指向head,然后往后遍历。

java 复制代码
public int size(){
int count=0;//计数器
ListNode node=head;
//开始遍历数组
    while(node!=null){
     count++;
     node=node.next;
    }
  return count;
}

实现链表addIndex(指定位置下插入数据):

既然要在指定位置下插入数据,我们首先就要考虑所提供的下标是否越界:

所以Index(下标)不能小于0,也不能大于链表的长度。

然后如果Index==0,则直接调用头插的方法,如果index==链表的长度,就直接调用尾插的方法。

这里有一个新节点ListNode node =new ListNode(25),要把新节点25插入到22和33之间,应该怎么做呢?

这里我们重新定义一个cur=head;

将cur遍历到22节点的位置

因为我们要将25这个节点插入2下标的位置,所以要将cur从head移动2-1个位置到22这个位置(这样才方便插入)

node.next=cur.next;

cur.next=node;

java 复制代码
public void addIndex(int Index,int data){
int len=size();//求链表的长度 
//判断Index下标是否异常 
 if(Index<0||Index>len){
  System.out.print("下标异常");
  return ;
 }
//特殊处理Index==0或者Index==len
if(Index==0){
    addFirst(data);
 }
if(Index==len){
    addLast(data);
 }
//插入到链表的中间下标
ListNode cur=head;
ListNode node=new ListNode(data);
//找到插入下标的前一个位置
while(Index-1!=0){
 cur=cur.next;
Index--;
 }
//开始插入
node.next=cur.next;
cur.next=node;
}

`实现链表remove(删除指定节点):

给一个链表,我们如果要删除33这个节点,应该怎么删除呢?

首先,如果这个节点为空,我们直接提前返回。

我们如果要删除33这个节点,那我们要重新定义一个节点,让这个节点指向被删除的节点的前一个节点, 也就是22这个节点。怎么找呢?

先有一个大循环,遍历链表,直到链表为空,然后嵌套一个if,找到被删除节点的前一个节点。

java 复制代码
//这个函数用来删除节点
public void remove(int key){
    if(head==null){
    return ;
    }
//如果第一个节点是要删除的节点
    if(head.val==key){
    head=head.next;
    }
//第三种情况
ListNode node=findKey(key);//这时node节点就是要删除的节点的前一个节点
node.next=node.next.next;//删除key节点
}
//这个函数用来找到被删除节点的前一个节点
 public ListNode findKey(int key){
    ListNode cur=head;
//遍历链表
while(cur!=null){
     if(cur.next.val==key){
         return cur;
      }
        cur=cur.next;
        }
return null;
}

实现链表clear(清空链表):

清空链表有两种方法:

1.最简单的方法:将链表的头节点置为空。

java 复制代码
public void clear(ListNode head){
    head=null;
}

2.第二种方法:遍历链表,将链表的所有的节点全部置为空。

java 复制代码
public void clear(ListNode head){
ListNode cur=head;
 while(cur!=null){
    ListNode curN=cur.next;//curN指向后一个节点
    cur.next=null;//将当前的cur节点的next域置为null
    cur=curN;//将cur移到curN的位置
 }
}
相关推荐
Brookty1 小时前
【数据结构】哈希表
数据结构·算法·哈希算法·散列表
Dovis(誓平步青云)2 小时前
【数据结构】·励志大厂版(复习+刷题):二叉树
c语言·数据结构·经验分享·笔记·学习·算法·学习方法
越城2 小时前
算法效率的钥匙:从大O看复杂度计算 —— C语言数据结构第一讲
c语言·开发语言·数据结构·算法
姜行运2 小时前
数据结构【堆和链式结构】
数据结构·算法·c#
chuhx4 小时前
Stream API 对两个 List 进行去重操作
数据结构·windows·list
元亓亓亓7 小时前
Java后端开发day36--源码解析:HashMap
java·开发语言·数据结构
何其有幸.9 小时前
实验3-3 比较大小(PTA|C语言)
c语言·数据结构·算法
丶Darling.10 小时前
26考研 | 王道 | 数据结构笔记博客总结
数据结构·笔记·考研
何其有幸.11 小时前
实验6-3 使用函数求特殊a串数列和(PTA|C语言)
c语言·数据结构·算法