目录
一、什么是LinkedList?
二、LinkedList的模拟实现
1、display()方法
2、addFirst(int data)方法
3、addLast(int data)方法
4、addIndex(int index,int data)方法
5、contains(int key)方法
6、remove(int key)方法
7、removeAllKey(int key)方法
8、size()方法
9、clear()方法
10、完整代码
三、LinkedList的使用
1、LinkedList的构造
(1)LinkedList()无参构造
(2)LinkedList(Collection c)利用Collection进行构造
2、LinkedList的常用方法
3、LinkedList的遍历
(1)for each 遍历
(2)使用迭代器遍历-- 正向遍历
(3)使用反向迭代器-- 反向遍历
四、ArrayList和LinkedList的区别
一、什么是LinkedList?
LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。同时,LinkedList也实现了List接口。
二、LinkedList的模拟实现
1、display()方法
打印单链表
2、addFirst(int data)方法
模拟实现一个Ilist接口
依旧是创建一个MyLinkedList类实现IList接口,在类中创建一个结点,结点当中设置一个变量存入数据一个前驱和一个后继。然后在创建一个头结点和一个尾结点(当然我们这里的结点依然只是一个引用,一个用来存储第一数据,一个用来存储最后一个数据,并不是所谓固定的头结点)
头插法
我们需要将要插入元素的后继改成原先的头节点,然后将原先头结点的前驱改成要插入的结点,最后将头节点的位置变为要插入的结点
3、addLast(int data)方法
尾插法
我们将要插入元素的前驱改成尾结点,将为结点的后继改成要插入的结点,最后将尾结点变成要插入的结点
4、addIndex(int index,int data)方法
任意位置插入 , 第一个数据节点为 0 号下标
在这我们需要找到要插入位置的原本节点,令原本节点的下一个结点的前驱设置为要插入的结点,将要插入的结点的后继设置为原本结点的下一个结点,之后再将原本结点的后继设置为要插入的结点,将要插入结点的前驱设置为原本结点
5、contains(int key)方法
查找是否包含关键字 key 是否在单链表当中
6、remove(int key)方法
删除第⼀次出现关键字为 key 的结点
我们先找到key的结点将这个删除结点的前一个结点的后继改为这个删除结点的后继结点,再将这个删除结点的后一个结点的前驱改为要删除结点的前驱结点
当我们要删除的结点为头结点时,我们只需要将头结点变为下一个结点,再将这个新的头结点的前驱置为空
当要删除的结点为尾结点时,我们只需要将尾结点向前移动,同时将新尾结点的后继置为空(但其实我们可以不用写出这步置为空,一会我们来看我们的代码就明白了)
7、removeAllKey(int key)方法
删除所有值为 key 的节点
对与这一方法如果已经明白了上面的操作了就非常简单了,因为这时要我们删除所有的key结点,在上面我们是只删除一次就结束了,既然要删除所有的我们只需要将return删除即可
8、size()方法
得到单链表的长度
9、clear()方法
清空链表
10、完整代码
IList接口
public interface IList {
//头插法
void addFirst(int data);
//尾插法
void addLast(int data);
//任意位置插⼊,第⼀个数据节点为0号下标
void addIndex(int index,int data);
//查找是否包含关键字key是否在单链表当中
boolean contains(int key);
//删除第⼀次出现关键字为key的节点
void remove(int key);
//删除所有值为key的节点
void removeAllKey(int key);
//得到单链表的⻓度
int size();
//打印单链表
void display();
//清空链表
void clear();
}
MyLinkedList
public class MyLinkedList implements IList {
static class ListNode {
private int val;
private ListNode prev;
private ListNode next;
public ListNode(int val){
this.val = val;
}
}
public ListNode head;
public ListNode last;
@Override
public void display() {
ListNode cur = this.head;
while(cur != null){
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
@Override
public void addFirst(int data) {
ListNode node = new ListNode(data);
if(head == null){
this.head = node;
this.last = node;
}
node.next = this.head;
this.head.prev = node;
this.head = node;
}
@Override
public void addLast(int data) {
ListNode node = new ListNode(data);
if(head == null){
this.head = node;
this.last = node;
}
this.last.next = node;
node.prev = this.last;
this.last = node;
}
@Override
public void addIndex(int index, int data) {
try{
check1(index);
if(index == 0){
addFirst(data);
return;
}
if(index == size()){
addLast(data);
return;
}
ListNode cur = findNode(index);
ListNode node = new ListNode(data);
cur.next.prev = node;
node.next = cur.next;
cur.next = node;
node.prev = cur;
}catch (Illegality e){
e.printStackTrace();
}
}
public void check1(int index) throws Illegality{
if( index < 0 || index > size()){
throw new Illegality("index不合法");
}
}
public ListNode findNode(int index){
ListNode cur = this.head;
while(index != 0){
cur = cur.next;
index--;
}
return cur;
}
@Override
public boolean contains(int key) {
ListNode cur = this.head;
while(cur != null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
@Override
public void remove(int key) {
ListNode cur = this.head;
while(cur != null ){
if(cur.val == key){
if( cur == this.head){
this.head = cur.next;
if(this.head != null){
this.head.prev = null;
}
}else{
cur.prev.next = cur.next;
if(cur == this.last){
this.last = cur.prev;
}else{
cur.next.prev = cur.prev;
}
}
return;
}
cur = cur.next;
}
}
@Override
public void removeAllKey(int key) {
ListNode cur = this.head;
while(cur != null ){
if(cur.val == key){
if( cur == this.head){
this.head = cur.next;
if(this.head != null){
this.head.prev = null;
}
}else{
cur.prev.next = cur.next;
if(cur == this.last){
this.last = cur.prev;
}else{
cur.next.prev = cur.prev;
}
}
}
cur = cur.next;
}
}
@Override
public int size() {
ListNode cur = head;
int count =0;
while(cur != null){
cur = cur.next;
count++;
}
return count;
}
@Override
public void clear() {
ListNode cur = head;
while(cur != null){
ListNode curN = cur.next;
cur.prev = null;
cur.next = null;
cur = curN;
}
this.head = null;
this.last = null;
}
}
异常
public class Illegality extends RuntimeException {
public Illegality(String message){
super(message);
}
}
主函数
public class Test {
public static void main(String[] args) {
MyLinkedList myLinkedList = new MyLinkedList();
}
}
三、LinkedList的使用
- LinkedList实现了List接口
- LinkedList的底层使用了双向链表
- LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问
- LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)
- LinkedList比较适合任意位置插入的场景
1、LinkedList的构造
(1)LinkedList()无参构造
(2)LinkedList(Collection<? extends E> c)利用Collection进行构造
2、LinkedList的常用方法
方法 | 解释 |
boolean add(E e) | 尾插e |
void add(int index, E element) | 将e插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插c中的元素 |
E remove(int index) | 删除 index 位置元素 |
boolean remove(object o) | 删除遇到的第一个o |
E get(int index) | 获取下标 index 位置元素 |
E set(int index, E element) | 将下标 index 位置元素设置为 element |
void clear() | 清空 |
boolean contains(Object o) | 判断o是否在线性表中 |
int indexof(object o) | 返回第一个o所在下标 |
int lastIndexof(Object o) | 返回最后一个o的下标 |
List<E> subList(int fromindex, int tolndex) | 截取部分 list |
这里这些常用方法有些是我们上面没有实现的,但是在顺序表那篇文章中我都进行了讲解,大家可以去看这篇文章
数据结构:ArrayList与顺序表
3、LinkedList的遍历
除了我经常使用的sout方法外还有三种方法
(1)for each 遍历
(2)使用迭代器遍历-- 正向遍历
(3)使用反向迭代器-- 反向遍历
四、ArrayList和LinkedList的区别
好了到目前为止我们已经学完了顺序表和链表了,那么他们之间有什么区别呢?
不同点 | ArrayList | LinkedList |
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持0(1) | 不支持:O(N) |
头插 | 需要搬移元素,效率低O(N) | 只需修改引用的指向,时间复杂度为0(1) |
插入 | 空间不够时需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |
好了,今天的讲解就到这里了,还请大家多多关注,我们下一篇再见!