java 数据结构] 链表
最编程
2024-10-16 08:44:06
...
【java数据结构】链表
- 一、引出链表
- 1.1 ArrayLIst存在的缺陷
- 1.2 链表
- 1.2.1 链表的概念以及结构
- 1.2.2 链表分类
- 二、单向,不带头,非循环链表
- 2.1 单链表的实现
- 创建节点
- 打印链表
- 得到单链表长度
- 头插法
- 尾插法
- 任意位置插入
- 查找是否包含关键字Key是否在单链表中
- 删除第一次出现关键字Key的节点
- 删除所有key的节点
- 清空单链表
- 主函数
- 2.2 单链表的优缺点
- 三、双向,不带头,非循环链表
- 3.1 双链表的实现
- 创建节点
- 打印双链表
- 得到双链表的长度
- 头插法
- 尾插法
- 任意位置插入
- 查找是否包含关键字Key是否在双链表中
- 删除第一次出现关键字Key的节点
- 删除所有key的节点
- 清空双链表
- 3.2 双链表的优缺点
- 四、LinkedList类
- 4.1 LinkedList实现的接口
- 4.2 LinkedList的使用
- 4.2.1 LinkedList的构造
- 4.2.2 LinkedList的其他方法
- 4.2.3 LinkedList的遍历
- 1.使用for-each遍历
- 2.使用for循环遍历
- 3.使用迭代器正向遍历
- 4.使用迭代器反向遍历
- mian函数
- 4.3 LinkedList类和ArrayList类的区别
此篇博客希望对你有所帮助(帮助里了解链表(单链表和双向链表),java集合框架中,实现List接口,LinkedList类),不懂的或有错误的也可在评论区留言,错误必改评论必回!!!持续关注,下一篇博客是链表的例题,相信例题可以帮你更加清晰准确理解链表!!!
一、引出链表
1.1 ArrayLIst存在的缺陷
1.ArrayLIst 底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或往后挪动,故时间复杂度为O(n);
2.增容需要申请空间,拷贝数据,释放旧空间,会有不小的消耗;
3.增容一般呈2倍增长,势必会有一定的空间浪费。例如当容量为100,瞒不了以后增容到200,我们再继续插入5个数据,后面没有数据插入了,那么就浪费了95个空间。
因此Java集合框架有引入了LinkedList,即链表结构。
1.2 链表
1.2.1 链表的概念以及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链表次序实现的。
链表由一系列节点(链表中每一个元素称为节点)组成,节点可以在运行时动态生成。每个节点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域。
实例:
最后一个节点的next没有指向下一个节点的地址,所以最后一个节点的next值为null。
1.2.2 链表分类
- 单向链表,双向链表
- 带头链表,不带头链表
- 循环的,非循环的
排列组合后一共8种链表,其中单向、不带头、非循环以及双向、不带头、非循环的链表最为重要,也是本文主要介绍的链表类型。
二、单向,不带头,非循环链表
2.1 单链表的实现
public class MySingleLinkedList {
//头插法
public void addHead(int data){
}
//尾插法
public void addLast(int data){
}
//任意位置插入
public void addindex(int index,int data){
}
//查找是否包含关键字Key是否在单链表中
public boolean contains(int key){
}
//删除第一次出现关键字Key的节点
public void remove(int key){
}
//删除所有key的节点
public void removeAllKey(int key){
}
//得到单链表的长度
public int size(){
return -1;
}
//清空单链表
public void clear(){
}
//打印单链表
public void display(){
}
}
创建节点
static class ListNode{
public int val;
public ListNode next;
public ListNode(int val){
this.val=val;
}
}
打印链表
public void display() {
LinkedNode cur = head;
while (cur != null) {
System.out.println(cur.val + " ");
cur = cur.next;
}
}
得到单链表长度
public int size(){
ListNode cur=head;
int len=0;
while(cur!=null){
cur=cur.next;
len++;
}
return len;
}
头插法
public void addHead(int data) {
ListNode node = new ListNode(data);
node.next = head;
head = node;
}
尾插法
public void addLast(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head =last= node;
return;
}
ListNode cur=head;
while (cur.next != null) {
cur = cur.next;
}
cur.next=node;
}
任意位置插入
//任意位置插入
public void addindex(int index, int data) {
int len = size();
if (index < 0 || index > len) {
return;
}
if (index == 0) {
addHead(data);
return;
}
if (index == len) {
addLast(data);
return;
}
ListNode node =new ListNode(data);
ListNode cur=head;
while(index!=0){
cur=cur.next;
index--;
}
node.next=cur.next;
cur.next=node;
}
查找是否包含关键字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) {
//先判断头节点是不是null值和key值
if(head==null){
return;
}else if(head.val==key){
head=head.next;
return;
}
ListNode cur=head.next;
ListNode prev=head;
while(cur!=null){
if(cur.val==key){
prev.next=cur.next;
return;
}
cur=cur.next;
prev= prev.next;
}
}
删除所有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 void clear() {
while(head!=null){
head=head.next;
}
System.out.println("==========");
// while(head!=null){
// ListNode cur=head.next;
// head=null;
// head=cur;
// }
System.out.println("=========");
ListNode cur = head;
while (cur != null) {
ListNode curN = cur.next;
cur.next = null;
cur = curN;
}
head=null;
System.out.println("==========");
// ListNode cur = head;
// while (cur != null){
// head = cur.next;
// cur = null;
// cur = head;
// }
}
主函数
可以用这个主函数进行测试
public static void main(String[] args) {
MySingleLinkedList m = new MySingleLinkedList();
m.addHead(1);
m.addHead(3);
m.addHead(4);
m.addHead(299);
m.addHead(6);
m.addHead(299);
m.addLast(8);
m.addindex(2,299);
m.addindex(2,299);
m.addindex(2,299);
m.addindex(2,299);
m.addindex(2,299);
// m.remove(299);
// m.removeAllKey(299);
m.clear();
m.display();
}
2.2 单链表的优缺点
优点:
- 插入和删除操作方便:在单链表中,插入和删除节点时,只需修改相邻节点的指针,而不需要移动大量数据,因此操作效率较高。
- 适合动态存储:单链表可以随时插入和删除节点,因此适合动态存储数据。
- 空间利用率高:单链表不需要连续的存储空间,因此可以更有效地利用内存空间。
缺点:
- 查找效率低:在单链表中,查找某个元素需要从头节点开始遍历整个链表,因此查找效率较低。
- 只能单向遍历:单链表只能从头到尾遍历,无法从尾到头遍历,这限制了其在某些应用场景中的灵活性。
三、双向,不带头,非循环链表
双向链表中每个节点包括三个部分:一个是存储数据元素的数据域,二个是存储下一个节点地址的指针域,三是存储上一个节点地址的指针域。
3.1 双链表的实现
public class MyLinkedList {
//头插法
public void addHead(int data){
}
//尾插法
public void addLast(int data){
}
//任意位置插入
public void addindex(int index,int data){
}
//查找是否包含关键字Key是否在单链表中
public boolean contains(int key){
}
//删除第一次出现关键字Key的节点
public void remove(int key){
}
//删除所有key的节点
public void removeAllKey(int key){
}
//得到双链表的长度
public int size(){
return -1;
}
//清空双链表
public void clear(){
}
//打印双链表
public void display(){
}
}
创建节点
static class LinkedNode{
public int val;
public LinkedNode prev;
public LinkedNode next;
public LinkedNode(int val){
this.val=val;
}
}
public LinkedNode head;
public LinkedNode last;
打印双链表
public void display() {
LinkedNode cur = head;
while (cur != null) {
System.out.println(cur.val + " ");
cur = cur.next;
}
}
得到双链表的长度
public int size() {
int len = 0;
LinkedNode cur = head;
while (cur != null) {
len++;
cur = cur.next;
}
return len;
}
头插法
public void addHead(int data) {
LinkedNode node = new LinkedNode(data);
if (head == null) {
head = last = node;
} else {
head.prev = node;
node.next = head;
head = node;
}
}
尾插法
public void addLast(int data) {
LinkedNode node = new LinkedNode(data);
if (head == null) {
head = last = node;
}
node.prev = last;
last.next = node;
last = node;
}
任意位置插入
public void addindex(int index, int data) {
int len = size();
if (index < 0 || index > len) {
return;
}
if (index == 0) {
addHead(data);
return;
}
if (index == len) {
addLast(data);
return;
}
LinkedNode node=new LinkedNode(data);
LinkedNode cur = head;
while (index != 0) {
cur = cur.next;
index--;
}
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
查找是否包含关键字Key是否在双链表中
public boolean contains(int key) {
if (head == null) {
return false;
}
LinkedNode 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;
} else if (head.val == key) {
head = head.next;
return;
}
LinkedNode cur = head;
while (cur != null) {
if (cur.val == key) {
cur.prev.next = cur.next;
if (cur.next != null) {
cur.next.prev = cur.prev;
} else {
last = last.prev;
}
return;
}
cur = cur.next;
}
}
删除所有key的节点
public void removeAllKey(int key) {
if (head == null) {
return;
}
while (head.val == key) {
head = head.next;
}
LinkedNode cur = head;
while (cur != null) {
if (cur.val == key) {
cur.prev.next = cur.next;
if (cur.next != null) {
cur.next.prev = cur.prev;
} else {
last = last.prev;
}
}
cur = cur.next;
}
}
清空双链表
public void clear() {
LinkedNode cur = head;
while (cur != null) {
LinkedNode curN = cur.next;
cur.prev=null;
cur.next = null;
cur = curN;
}
head=last=null;
}
3.2 双链表的优缺点
优点:
- 双链表允许从任意节点向前或向后遍历,这使得在链表中间插入和删除节点时更加灵活。
- 在已知节点位置的情况下,双链表可以在常数时间(O(1))内完成插入和删除操作,因为可以直接访问前驱和后继节点。
- 与数组不同,双链表在插入和删除操作时不需要移动大量数据或重新分配内存,因此具有更好的动态性能。
缺点:
- 每个节点需要额外的指针来存储前驱节点的引用,这增加了节点的内存占用。与单链表相比,双链表需要更多的内存空间。
- 双链表在插入和删除节点时需要更新更多的指针(前驱和后继),这增加了编程的复杂性,特别是在处理边界条件时(如头节点和尾节
推荐阅读
-
[AcWing]基础算法课程--数据结构
-
信息技术在线教学平台设计与实施的 JAVA 项目(源代码 + 文档)
-
数据结构 - 二叉树堆
-
基于taozige/Java的充电桩平台+充电桩系统+充电桩管理系统+充电桩系统源代码+充电桩管理后台+充电桩小程序
-
Java 二分搜索
-
使用 Selenium WebDriver 抓取数据的 Java 爬虫
-
[Java 并发编程 III] 多线程案例(手撕单件模式、阻塞队列、定时器、线程池)
-
数据结构 - 八种分类(下)
-
Java Spring 中的 @Autowired、@Resource、@Qualifier 和 @Inject 注解:使用细节和注意事项
-
入门-4 人工智能中的数据结构