一、链表
链表是一种物理结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。逻辑上是连续的
2)链表结构(8种)
单向/双向+带头/不带头+循环/非循环(组合)
在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。
二、链表的实现
1.有头单向非循环链表实现
代码如下(示例):
package demo2;
public class MySingleList {
static class ListNode{
public int val;//结点的值
public ListNode next;//下一个节点的地址 (引用类型)
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;//表示当前链表的头节点
//创建一个链表
public void createList() {
//构造方法
ListNode node1=new ListNode(12);
ListNode node2=new ListNode(23);
ListNode node3=new ListNode(34);
ListNode node4=new ListNode(45);
ListNode node5=new ListNode(56);
//以链表的方式连接起来
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
this.head=node1;
}
public void display() {
// while (head!=null){
// System.out.print(head.val+" ");
// head=head.next;
// }
// //换行
// System.out.println();
// }
ListNode cur=head;
while (cur!=null){
System.out.print(cur.val+" ");
//cur=null证明链表遍历完成了
cur=cur.next;
}
//换行
System.out.println();
}
//头插法
public void addFirst(int data){
//在插入的时候先绑定后面节点的信息
ListNode node=new ListNode(data);
node.next=head;
head=node;
}
//尾插法
public void addLast(int data){
ListNode node=new ListNode(data);
ListNode cur=head;
//cur/head==null cur.next空指针异常
if (cur==null){
head=node;
return;
}
//cur.next==null说明cur所指向的节点是尾巴节点
//cur==null代表把链表的每个节点都遍历完了
while (cur.next!=null){
cur=cur.next;
}
cur.next=node;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
if(index<0||index>size()){
System.out.println("index 不合法");
return;
}
if(index==0){
//头插法
addFirst(data);
return;
}
if (index==size()){
//尾插法
addLast(data);
return;
}
//定义cur走index-1步,找到插入位置的前一个插入
ListNode cur=findIndexSubOne(index);
ListNode node=new ListNode(data);
node.next=cur.next;
cur.next=node;
}
//要找节点的前一个节点
private ListNode findIndexSubOne(int index){
ListNode cur=head;
int count=index-1;
while (count!=0){
cur=cur.next;
count--;
}
return cur;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
ListNode cur=head;
while (cur!=null){
if(cur.val==key){
return true;
}
cur=cur.next;
}
return false;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
if(head==null){
return;
}
if(head.val==key){
head=head.next;
return;
}
ListNode cur=searchPrev(key);
if (cur==null){
System.out.println("没有你要删除的数字");
return;
}
ListNode del=cur.next;
cur.next=del.next;
}
//要删除值的节点的前一个节点
private ListNode searchPrev(int key){
ListNode cur=head;
while (cur!=null){
if(cur.next.val==key){
return cur;
}
cur=cur.next;
}
return null;
}
//删除所有值为key的节点
public void removeAllKey(int key){
if (head==null){
return;
}
ListNode cur=head.next;
ListNode prev=head;
while (cur!=null){
if(cur.val==key){
prev.next=cur.next;
cur=cur.next;
}else {
prev=cur;
cur=cur.next;
}
}
if(head.val==key){
head=head.next;
}
}
//得到单链表的长度
public int size(){
int count=0;
ListNode cur=head;
while (cur!=null){
count++;
cur=cur.next;
}
return count;
}
public void clear() {
this.head=null;
}
}
class Test{
public static void main(String[] args) {
MySingleList mySingleList=new MySingleList();
mySingleList.createList();
mySingleList.display();
System.out.println("2.===========");
//链表长度
System.out.println(mySingleList.size());
//判断123是否在单链表中
System.out.println(mySingleList.contains(123));
System.out.println("头插999");
mySingleList.addFirst(999);
mySingleList.display();
System.out.println("尾插888");
mySingleList.addLast(888);
mySingleList.display();
System.out.println("1位置插入7");
mySingleList.addIndex(1,7);
mySingleList.display();
System.out.println("删除999");
mySingleList.remove(999);
mySingleList.display();
System.out.println("删除所有的12");
mySingleList.addFirst(12);
mySingleList.display();
mySingleList.removeAllKey(12);
mySingleList.display();
System.out.println("删除所有节点");
mySingleList.clear();
System.out.println(mySingleList.size());
}
}

2.代码解析
1)打印 :这种方法打印最后头节点为空(打印后找不到头节点) //创建临时节点cur


2)任意位置插入元素
被插入位置的前一个节点cur
node.next=cur.next;
cur.next=node;
3)删除节点
找到要删除节点的前一个节点
删除 cur.next=del.next
4)常用方法
boolean add(E e) 尾插 e
void add(int index, E element) 将 e 插入到 index 位置
boolean addAll(Collection 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 subList(int fromIndex, int toIndex) 截取部分 list
三、ArrayList和LinkedList的区别
不同点 ArrayList LinkedList
存储空间上 物理上一定连续 逻辑上连续,但物理上不一定连续
随机访问 支持O(1) 不支持:O(N)
头插 需要搬移元素,效率低O(N) 只需修改引用的指向,时间复杂度为O(1)
插入 空间不够时需要扩容 没有容量的概念
应用场景 元素高效存储+频繁访问 任意位置插入和删除频繁
LinkedList vs ArrayList
内部实现
LinkedList:基于双向链表实现。每个元素(节点)包含对前一个和后一个节点的引用。这使得LinkedList在插入和删除操作时不需要移动其他元素,因此在列表中间执行这些操作效率更高。ArrayList:基于动态数组实现。它提供了对元素的随机访问能力,因为可以通过索引直接访问元素。然而,在进行插入或删除操作(特别是列表中部的操作)时,可能需要移动元素以保持连续性,这会带来额外的时间开销。
性能特点
LinkedList:- 插入和删除操作(尤其是两端)效率高,时间复杂度为O(1)。
- 随机访问元素效率低,时间复杂度为O(n),因为需要从头或尾遍历到指定位置。
ArrayList:- 支持快速随机访问,时间复杂度为O(1)。
- 插入和删除操作(尤其是在列表中部)效率较低,时间复杂度为O(n),因为需要移动元素。
适用场景
LinkedList:适用于频繁进行插入和删除操作的场景,如实现栈和队列等数据结构。ArrayList:适用于需要频繁访问元素,而插入和删除操作相对较少的场景。
1698

被折叠的 条评论
为什么被折叠?



