LinkedList的模拟实现(底层是一个双向链表)
无头双向链表:有两个指针;一个指向前一个节点的地址;一个指向后一个节点的地址。
节点定义和链表初始化
class ListNode{
int val;
ListNode prev;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
ListNode head;
ListNode tail;
public void createList(){//初始化链表
ListNode ListNode1=new ListNode(1);
ListNode ListNode2=new ListNode(2);
ListNode ListNode3=new ListNode(3);
ListNode ListNode4=new ListNode(4);
ListNode ListNode5=new ListNode(5);
ListNode ListNode6=new ListNode(6);
ListNode1.next=ListNode2;
ListNode2.next=ListNode3;
ListNode3.next=ListNode4;
ListNode4.next=ListNode5;
ListNode5.next=ListNode6;
//还要绑定prev
ListNode1.prev=null;
ListNode2.prev=ListNode1;
ListNode3.prev=ListNode2;
ListNode4.prev=ListNode3;
ListNode5.prev=ListNode4;
ListNode6.prev= ListNode5;
tail=ListNode6;//指向尾节点
this.head=ListNode1;
}
打印链表
public void myprintf(){
ListNode cur=head;
while(cur!=null){
System.out.println(cur.val+"");
cur=cur.next;
}
}
插入元素
头插法
分两种情况,1:有多个节点;需要修改3个地方;2:没有节点的时候插入。tail是我们定义的尾巴
1:head指向
2:原来头节点的前驱位置指向
3:插入的这个节点的next指向
//头插法
public void addFirst(int data) {
ListNode listNode=new ListNode(data);
if(head==null){//修改的时候就想象这个节点插入位置情况;有哪些地方需要改(修改无非就几个东西:head、tail、前一个节点的prev、next。后一个节点的prev、next。插入节点的prev、next);注意:空链表、只有一个节点情况
head=listNode;
tail=listNode;
listNode.next=null;//虽然不赋值都是null;为了可读性
listNode.prev=null;
}else {
listNode.next=head;
head.prev=listNode;
head=listNode;
listNode.prev=null;
}
}
尾插法
尾插法:和头插差不多;也是分两种情况;1:空链表;2:有多个节点情况;需要修改3个地方
//尾插法
public void addLast(int data){
ListNode listNode=new ListNode(data);
if(head==null){
head=listNode;
tail=listNode;
listNode.next=null;
listNode.prev=null;
}else {
tail.next=listNode;
listNode.prev=tail;
tail=listNode;
}
}
index位置插入
任意位置插入:分空链表(单独处理)和多个节点(需要先遍历找到位置;需要修改4个指向)。我们需要判断一下插入的位置合法不合法;等于0头插、等于length尾插
中间位置插入ListNode分析:
//index位置插入
public boolean addIndex(int index,int data){
if(index<0||index>size()){
System.out.println("插入的位置不合法");
return false;
}
if(index==0||head==null){//头插
addFirst( data);
return true;
}
if(index==size()) {//尾插
addLast (data);
return true;
}
ListNode listNode=new ListNode(data);//中间插入;先找到index位置;然后插入这个节点的位置
ListNode indexListNode=select(index);//找到index
listNode.prev=indexListNode.prev;
listNode.next=indexListNode;
indexListNode.prev.next=listNode;//前一个节点的后继等于插入节点
indexListNode.prev=listNode;
return true;
}
//链表长度
public int size(){
ListNode cur=head;
int length=0;
while(cur!=null){
length++;
cur=cur.next;
}
System.out.println(length);
return length;
}
//查找index位置
public ListNode select(int index){
ListNode cur=head;
while(index>0){
cur=cur.next;
index--;
}
System.out.println(cur.val);
return cur;
}
删除第一个值为key节点
遍历去找;找到了;判断一下这个节点是不是头、是不是尾;因为删除头、尾、中间都是不同的。虽然代码能少量复用;但是学习阶段;重点在于可读性。
//删除第一次出现key值节点
public void remove(int key){
ListNode cur=head;
while (cur!=null){
if(cur.val==key){
if(cur==head){
head.next.prev=null;
head=head.next;
break;
}else if(cur==tail){
tail.prev.next=null;
tail=tail.prev;
break;
}else {
//我应该要找到删除节点的前一个;然后直接跳过这个节点就是删除了。就把前一个节点的next跳过这个;后一个节点的prev往前跳过这个节点
cur.next.prev=cur.prev;
cur.prev.next=cur.next;
break;
}
}
cur=cur.next;
}
}
清除链表
public void clear(){
ListNode cur=head;
while (cur!=null){
ListNode curNext=cur.next;
cur.next=null;
cur.prev=null;
cur=curNext;
}
head=null;
tail=null;
}
为什么单向链表清除能直接head=null;而双向链表不能head=null、tail=null缺不行?
理论上单向链表也是要将所有的节点都置为null;双向链表为了更好1及时释放内存空间;就是正常情况下head=null和tail=null它的中间节点也是会被垃圾回收器回收的。
LinkedList常用方法
带参数构造方法,实现这个接口的都可以传进来;顺序表实现了这个接口,所以把顺序表传过去也行
ArrysList和LinkedList区别
增删改查上、储存上呢: