目录
链表的基本概念
LinkedList
ArrayList和LinkedList的区别
链表的基本概念
当在ArrayList任意位置插入或删除元素时,就需要将后续元素整体往前或者整体往后搬移,时间复杂度O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。java集合中又引入了LinkedList,即链表结构。
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
链表的结构非常多样,以下3种情况组合起来就有8种链表:
1、单向 VS 双向
2、带头 VS 不带头
3、循环 VS 非循环
我们重点掌握两种:
无头单向非循环链表:结构简单。一般不会单独用来存数据,实际更多是作为其他数据结构的子结构。
无头双向非循环链表:在java的集合框架库中LinkedList底层实现就是无头双向非循环链表。
无头单向非循环链表结构如下:
LinkedList
Java中提供了LinkedList类来作为双向链表的实现类。 LinkedList的底层是双向链表结构,由于链表中没有将元素存储在连续的空间中,元素存储在单独的结点中,然后通过引用将结点连接起来了,因此在任意位置插入或删除元素时,不需要搬移元素,效率比较高。
以下是LinkedList的模拟实现:
package MyLinkedList;
public class MyLinkedList implements IList {
static class ListNode{
public int val;
public ListNode next;
public ListNode prev;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
@Override
public void addFirst(int data) {
ListNode node = new ListNode(data);
if(head == null){
head = node;
last = node;
}else {
node.next = head;
head.prev = node;
head = node;
}
}
@Override
public void addLast(int data) {
ListNode node = new ListNode(data);
if(head == null){
head = node;
last = node;
}else {
last.next = node;
node.prev = last;
last = node;
}
}
@Override
public void addIndex(int index, int data) {
if(index < 0 || index > size()){
throw new IndexException("双向链表插入,index不合法:" + index);
}
if(index == 0){
addFirst(data);
return;
}
if(index == size()){
addLast(data);
return;
}
ListNode node = new ListNode(data);
ListNode cur = findIndex(index);
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
public ListNode findIndex(int index){
ListNode cur = head;
while (index != 0){
cur = cur.next;
index--;
}
return cur;
}
@Override
public boolean contains(int key) {
ListNode cur = head;
while (cur!= null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
@Override
public void remove(int key) {
ListNode cur = head;
while (cur != null){
if(cur.val == key){
if(cur == head) {
head = head.next;
if(head != null){
head.prev = null;
}else {
//只有一个结点,且是需要删除的结点
last = null;
}
}else {
cur.prev.next = cur.next;
//删除中间结点
if(cur.next != null){
cur.next.prev = cur.prev;
// cur.prev.next = cur.next;
}else {
//删除尾巴结点
// cur.prev.next = cur.next;
last = last.prev;
}
}
return;
}
cur = cur.next;
}
}
@Override
public void removeAllKey(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val == key) {
if (cur == head) {
head = head.next;
if (head != null) {
head.prev = null;
} else {
//只有一个结点,且是需要删除的结点
last = null;
}
} else {
cur.prev.next = cur.next;
//删除中间结点
if (cur.next != null) {
cur.next.prev = cur.prev;
// cur.prev.next = cur.next;
} else {
//删除尾巴结点
// cur.prev.next = cur.next;
last = last.prev;
}
}
}
cur = cur.next;
}
}
@Override
public int size() {
int count = 0;
ListNode cur = head;
while (cur != null){
count++;
cur = cur.next;
}
return count;
}
@Override
public void clear() {
this.head = null;
this.last = null;
}
@Override
public void display() {
ListNode cur = head;
while (cur != null){
System.out.print(cur.val +" ");
cur = cur.next;
}
System.out.println();
}
}
ArrayList和LinkedList的区别
LinkedList插入,删除操作效率更高。ArrayList查找,修改操作效率更高。
练习一题:合并两个有序链表 ( 力扣链接:. - 力扣(LeetCode))
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
解:
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode newHead = new ListNode(-1);
ListNode tmp = newHead;
while(list1 != null && list2 != null){
if(list1.val < list2.val){
tmp.next = list1;
list1 = list1.next;
tmp = tmp.next;
}else{
tmp.next = list2;
list2 = list2.next;
tmp = tmp.next;
}
}
if(list1 != null){
tmp.next = list1;
}
if(list2 != null){
tmp.next = list2;
}
return newHead.next;
}
}
以上,关于链表,希望对你有所帮助。