1.ArrayList的缺陷
ArrayList底层使用的是数组来存储元素,因此底层是一段连续的空间,。当在ArrayList任意位置
插入或者删除元素时,就需要将后面的元素全部往前移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置增加删除元素比较多的场景。因此java集合又出现的了,LinkedList结构,就是链表。
2.链表
2.1链表的结构和概念
链表是一个在物理存储结构上不连续,逻辑结构上连续的的结构。
就像小火车一样,通过地址来连接
2.2链表的种类
链表的种类也是很多的,单向(双向)、带头(不带头)、循环(非循环)
6种排列组合共有8种结构
虽然结构很多,但是我们只需要掌握最常用的两种就可以
无头单向非循环链表
:
结构简单
,一般不会单独用来存数据。实际中更多是作为
其他数据结构的子结构
,如 哈希桶、图的邻接表等等。另外这种结构在笔试面试
中出现很多。
无头双向链表
:在
Java
的集合框架库中
LinkedList
底层实现就是无头双向循环链表。
2.3链表的实现
public class MysingleList {
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);
ListNode node6 = new ListNode(67);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
this.head = node1;
}
//遍历单链表
public void show(){
ListNode cur = head;
while(cur != null){
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println( );
}
//计算链表长度
public int size(){
int count = 0;
ListNode cur = head;
while(cur != null){
count++;
cur = cur.next;
}
return count;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
ListNode cur = head;
while(cur != null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
//头插法
public void addFirst(int data){
ListNode node7 = new ListNode(data);//产生一个新的节点
node7.next = head;
head = node7;
}
public void addLast(int data){
ListNode node8 = new ListNode(data);
//判断是否一个节点也没有
if(head == null){
head = node8;
return;
}
ListNode cur = head;
//找到链表的尾巴节点
//当cur.next 等于空时,cur就在尾巴节点
while(cur.next != null){
cur = cur.next;
}
cur.next = node8;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
ListNode cur = head;
ListNode node = new ListNode(data);
//1.判断index位置的合法性
if(index < 0 || index > size()){
throw new IndexOutOfBounds("index的下标不合法!!");
}
//2.如果链表为空就是头插法
if(index == 0){
addFirst(data);
return;
}
//3.如果index等于链表长度,就是尾插法
if(index == size()){
addLast(data);
return;
}
//4.找到index-1的位置
for (int i = 0; i < index - 1; i++) {
cur = cur.next;
}
node.next = cur.next;
cur.next = node;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
//如果链表为空
if(head == null){
return;
}
//key是头结点
if(head.val == key){
head = head.next;
return;
}
//删除节点
ListNode pre = searchNode(key);//前驱节点
if(pre == null){
System.out.println("没有这个数据!!");
}
ListNode del = pre.next;
pre.next = del.next;
}
//找到key的前节点
public ListNode searchNode(int key){
ListNode pre = head;
while (pre.next != null){
if(pre.next.val == key){
//删除节点
return pre;
}else{
pre = pre.next;
}
}
return null;
}
//删除所有值为key的节点
public void removeAllKey(int key){
if(head == null){
return;
}
while(head.val == key){
head = head.next;
}
ListNode pre = head;
ListNode cur = head.next;
while(cur != null){
if(cur.val == key){
pre.next = cur.next;
cur = cur.next;
}else {
pre = cur;
cur = cur.next;
}
}
}
//回收链表
public void clear(){
//方法一: this.head = null;//将头节点置空,其余的就不见了
while(head != null){ //方法二:
ListNode headNext = head.next;
head = null;
head = headNext;
}
}
}
上面的方法是链表中经常用到的,也就是链表增删改查。
3.LinkedList的模拟实现
LinkedList的底层是一个双向链表
现在我们对它进行增删查改
public class MyLinkedList {
static class ListNode{
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head ;
public ListNode last ;
//创建链表
public void createList(){
ListNode node1 = new ListNode(12);
ListNode node2 = new ListNode(23);
ListNode node3 = new ListNode(34);
ListNode node4 = new ListNode(45);
node1.next = node2;
node2.next = node3;
node3.next = node4;
this.head = node1;
this.last = node4;
}
//得到单链表的长度
public int size(){
ListNode cur = head;
int count = 0;
while (cur != null){
count++;
cur = cur.next;
}
return count;
}
//打印双链表
public void display(){
ListNode cur = head;
while(cur != null){
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
ListNode cur = head;
while (cur != null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
//头插法
public void addFirst(int data){
ListNode cur = head;
ListNode node = new ListNode(data);
if(head == null){
head = node;
last = node;
return;
}
node.next = head;
head.prev = node;
head = node;
}
//尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
if(last == null){
last = node;
head = node;
return;
}
last.next = node;
node.prev = last;
last = last.next;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
if(index < 0 || index > size()){
throw new RuntimeException("index下标不合法!");
}
if(index == 0){
//头插法
addFirst(data);
}
if(index == size()){
//尾插法
addLast(data);
}
ListNode node = new ListNode(data);
ListNode cur = head;
//找到index下标!
while(index != 0){
cur = cur.next;
index--;
}
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
//开始删
if(cur == head) {
//删除头节点
head = head.next;
//只要1个节点的时候
if(head != null) {
head.prev = null;
}else {
last = null;
}
}else {
cur.prev.next = cur.next;
if(cur.next != null) {
//cur.prev.next = cur.next;
cur.next.prev = cur.prev;
}else {
//cur.prev.next = cur.next;
last = last.prev;
}
}
return;
}else {
cur = cur.next;
}
}
}
//删除所有值为key的节点
public void removeAllKey(int key){ ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
//开始删
if (cur == head) {
//删除头节点
head = head.next;
//只要1个节点的时候
if (head != null) {
head.prev = null;
}else {
last = null;
}
} else {
cur.prev.next = cur.next;
if (cur.next != null) {
//cur.prev.next = cur.next;
cur.next.prev = cur.prev;
} else {
//cur.prev.next = cur.next;
last = last.prev;
}
}
}
//cur = cur.next;
// }else {
// cur = cur.next;
// }
cur = cur.next;
}
}
public void clear(){
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.prev = null;
cur.next = null;
//cur.val = null;
cur = curNext;
}
head = null;
last = null;
}
}
3.1 LinkedList的其它方法介绍
方法 | 解释 |
boolean
add
(E e)
| 尾插 e |
void
add
(int index, E element)
|
将
e
插入到
index
位置
|
boolean
addAll
(Collection<? extends E> 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<E>
subList
(int fromIndex, int toIndex)
|
截取部分
list
|
4.ArrayList和LinkedList的区别
目录