package Demo1;
/**
* Describe:双向链表的简单模拟实现
* User:lenovo
* Date:2023-01-08
* Time:11:20
*/
class Node {
int val;
Node prev;
Node next;
public Node(int val) {
this.val = val;
}
public Node() {
}
}
public class MyLinkedList {
Node first;
Node last;
int size = 0;
public MyLinkedList() {
}
//头插法
public void addFirst(int data){
Node cur = new Node(data);
//链表为空
if(first == null) {
first = cur;
last = cur;
size++;
return;
}
//链表不为空
cur.next = first;
first.prev = cur;
first = cur;
size++;
}
//尾插法
public void addLast(int data){
Node cur = new Node(data);
//链表为空
if(first == null) {
first = cur;
last = cur;
size++;
return;
}
//链表不为空
last.next = cur;
cur.prev = last;
last = last.next;
size++;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
if(index < 0 || index > size) {
return;//这里我们可以直接返回,或者报错
}
Node cur = new Node(data);
Node s1 = first;
Node s2 = first;
//头节点的插入
if(index == 0) {
addFirst(data);
return;
}
//尾节点的插入
if(index == size){
addLast(data);
return;
}
//找这个下表的节点
//如果它小,我们从头找这个元素
int count = 0;
if(index < size / 2) {
while(count != index) {
s2 = s2.next;
count++;
}
}else {
s2 = last;
while(size - 1 - index != count) {
s2 = s2.prev;
count++;
}
}
s1 = s2.prev;
//开始插入
s1.next = cur;
cur.next = s2;
s2.prev = cur;
cur.prev = s1;
size++;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
Node cur = first;
if(first == null ) {
return false;
}
while(cur != null && cur.val != key) {
cur = cur.next;
}
if(cur == null) {
return false;
}else {
return true;
}
}
//删除第一次出现关键字为key的节点
public void remove(int key){
Node cur = first;
if(first == null ) {
return;
}
while(cur != null) {
if(cur.val == key) {
//头节点的判断
if(cur == first) {
//如果只有一个节点
if(cur.next == null) {
first = null;
last = null;
size--;
break;
}
Node s1 = cur.next;
s1.prev = null;
first = s1;
size--;
break;
}else if(cur == last) {//尾节点的判断
Node s1 = cur.prev;
s1.next = null;
last = s1;
size--;
break;
}else {//其他节点的判断
Node s1 = cur.prev;
Node s2 = cur.next;
s1.next = s2;
s2.prev = s1;
size--;
break;
}
}
cur = cur.next;
}
}
//删除所有值为key的节点
public void removeAllKey(int key){
if(first == null ) {
return;
}
Node cur = first;
while(cur != null) {
if(cur.val == key) {
//头节点的判断
if(cur == first) {
//如果只有一个节点
if(cur.next == null) {
first = null;
last = null;
size--;
break;
}
Node s1 = cur.next;
s1.prev = null;
first = s1;
size--;
}else if(cur == last) {//尾节点的判断
Node s1 = cur.prev;
s1.next = null;
last = s1;
size--;
}else {//其他节点的判断
Node s1 = cur.prev;
Node s2 = cur.next;
s1.next = s2;
s2.prev = s1;
size--;
}
}
cur = cur.next;
}
}
//得到单链表的长度
public int size(){
return size;
}
public void display(){
//链表为空
if(this.first ==null) {
return;
}
Node cur = first;
while(cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}
public void clear(){
//最保险的方式是遍历数组每个节点的上下节点设为空
Node cur = first;
while(cur != null) {
Node tmp = cur.next;
cur.prev = null;
cur.next = null;
cur = tmp;
}
first = null;
last = null;
}
//测试
public static void main(String[] args) {
MyLinkedList list = new MyLinkedList();
System.out.println("==================头插法===============");
list.addFirst(1);
list.addFirst(2);
list.addFirst(3);
list.addFirst(4);
list.display();
System.out.println("================尾插法================");
MyLinkedList list2 = new MyLinkedList();
list2.addLast(1);
list2.addLast(2);
list2.addLast(3);
list2.addLast(4);
list2.display();
System.out.println("===============插入任意位置=============");
list2.addIndex(-1,0);//这条不会被加上去
list2.addIndex(4,5);
list2.addIndex(1,11);
list2.display();
System.out.println("===============查找关键字===============");
System.out.println(list2.contains(1));
System.out.println(list2.contains(0));
System.out.println("===============删除第一个关键字===========");
list2.remove(11);
list2.remove(22);
list2.display();
System.out.println("===============删除所有的5==============");
list2.addLast(5);
list2.addLast(5);
list2.addLast(5);
list2.addLast(6);
list2.removeAllKey(5);
list2.display();
System.out.println("==============获得长度================");
System.out.println(list2.size());
System.out.println("==============清空数组================");
list2.clear();
list2.display();
}
}
(如有错误,请批评指针,评论区会不定时查看)
LinkedList的底层是双向链表结构,双向链表的的储存并不需要连续的大块空间,并且链表是通过节点链接起来的,因此在任意位置插入或删除元素并没有很高的复杂度。
LinkedList实现了List接口;
LinkedList的底层是双向链表;
LinkedList没有实现RandomAccess及接口,因此不支持随机访问;
LinkedList的任意位置插入和删除元素时效率比较高;
LinkedList比较适合任意位置插入和删除的场景。
两个构造方法:
无参构造:LinkedList()
使用其他容器中元素构造List:public LinkedList(Collection extends E> c)
/**
* Describe:LinkedList构造方法的介绍
* User:lenovo
* Date:2023-01-09
* Time:14:17
*/
public class Test {
public static void main(String[] args) {
List list1 = new LinkedList<>();
List list2 = new ArrayList<>();
list2.add("javeSE");
list2.add("javaWeb");
List list3 = new LinkedList<>(list2);
}
}
//常用方法的介绍
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList<>();
System.out.println("===========尾插法================");
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
System.out.println(list);
System.out.println("=================在任意位置插入===============");
list.add(0, 0);
System.out.println(list);
System.out.println("=================移除元素===============");
list.remove();//移除第一个元素,内部调用的是removeFirst
System.out.println(list);
list.removeFirst();//移除第一个元素
System.out.println(list);
list.removeLast();//移除最后一个元素
System.out.println(list);
list.remove((Integer) 3);//移除第一个指定的元素
System.out.println(list);
System.out.println("================contains()检测是否包含某一元素=============");
System.out.println(list.contains(2));
System.out.println(list.contains(3));
System.out.println("=================找都某一元素返回下标======================");
System.out.println(list.indexOf(2));
System.out.println(list.lastIndexOf(4));
System.out.println("===================获得某一位置的元素=====================");
System.out.println(list.get(0));
System.out.println("===================修改某一位置的元素=====================");
list.set(0, 1);
System.out.println(list);
System.out.println("================subList()创建一个新的LinkedList==========");
List list1 = list.subList(1, 3);//前闭后开
System.out.println(list1);
list1.set(0, 0);
System.out.println(list);//修改链表1,但是原链表也改变了,说明公用一个链表
System.out.println("====================清空链表===========================");
list.clear();
System.out.println(list);
System.out.println(list.size());
}
}
//双向链表的遍历
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
//foreach遍历
for (int a : list) {
System.out.print(a + " ");
}
System.out.println();
//迭代器,正向遍历
ListIterator it = list.listIterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
//使用迭代器,反向遍历
ListIterator rit = list.listIterator(list.size());
while(rit.hasPrevious()) {
System.out.print(rit.previous() + " ");
}
System.out.println();
}
}
不同点 |
ArrayList |
LinkedList |
储存空间上 |
连续的大段空间 |
逻辑上连续,空间上不需要连续 |
随机访问 |
时间复杂度O(1) |
时间复杂度O(N) |
头插 |
需要移动后面元素,较为复杂 |
不需要移动后面元素,效率较高 |
插入 |
空间不够,需要扩容 |
没有容量的概念 |
应用场景 |
元素高效储存+频繁访问 |
任意位置插入+频繁增删 |