1. ArrayList的缺陷
上节课已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { // ... // 默认容量是10 private static final int DEFAULT_CAPACITY = 10; //... // 数组:用来存储元素 transient Object[] elementData; // non-private to simplify nested class access // 有效元素个数 private int size; public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } // }
由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。
2. 链表
2.1 链表的概念及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
- 单向或者双向
- 带头或者不带头
- 循环或者非循环
虽然有这么多的链表的结构,但是我们重点掌握两种:
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多
无头双向链表:在Java的集合框架
- 库中LinkedList底层实现就是无头双向循环链表
2.2 链表的实现
1.链表的功能
package mysingleList; public interface IList { void addFirst(int data); //尾插法 void addLast(int data); //任意位置插入,第一个数据节点为0号下标 void addIndex(int index,int data); //查找是否包含关键字key是否在单链表当中 boolean contains(int key); //删除第一次出现关键字为key的节点 void remove(int key); //删除所有值为key的节点 void removeAllKey(int key); //得到单链表的长度 int size(); void clear(); void display(); }
2.初始化链表
public class MySingleList implements IList{ 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; }
开辟了内存空间
使每个node的next域指向下一个节点的地址,连接成链表
head指向第一个节点的地址
3.实现功能接口
3.1头插添加元素
public void addFirst(int data) { ListNode node = new ListNode(data); if(this.head == null){ this.head = node; } else { node.next = this.head; this.head = node; } }
对 node.next = this.head;
this.head = node;
进行解释,node的next域指向下一个节点的地址
head继续为头节点
3.2尾插法添加新元素
public void addLast(int data) { ListNode node = new ListNode(data); ListNode cur = head; if (this.head == null){ this.head = node; } else { while(cur.next != null){ cur = cur.next; } cur.next = node; } }
找到最后一个元素cur,cur的next指向要插入元素的地址
3.3找到下标的前驱节点
private ListNode searchPrev(int index){ ListNode cur = this.head; int count = 0; while(count != index-1){ cur = cur.next; count++; } return cur; }
3.4指定位置插入元素
public void addIndex(int index, int data) { //判断index的位置是否合法 if(index < 0 || index >size()){ return; } //插入到第一个节点位置 if(index == 0){ addFirst(data); } //插入到最后一个节点的位置 if (index == size()){ addLast(data); } //中间位置 else { ListNode node = new ListNode(data); ListNode cur = searchPrev(index); node.next = cur.next; cur.next = node; } }
3.5指定元素是否存在
public boolean contains(int key) { ListNode cur = this.head; while(cur != null){ if(cur.val == key){ return true; } } return false; }
遍历一遍链表寻找是否有key元素
3.6找到指定元素的前驱节点
private ListNode findPrev(int key){ ListNode cur = this.head; while(cur.next != null){ if (cur.next.val == key){ return cur; } cur = cur.next; } return null; }
3.7删除指定节点
public void remove(int key) { if (this.head == null){ System.out.println("没有节点,无法删除"); return; } //指定元素在头节点 if (this.head.val == key){ this.head = this.head.next; } else { ListNode cur = findPrev(key); //没有找到指定元素 if (cur == null){ System.out.println("没有找到要删除的节点"); return; } //找到了指定元素 ListNode del = cur.next; cur.next = del.next; } }
3.8删除所有元素为key的节点
public void removeAllKey(int key) { if(this.head == null){ return; } ListNode prev = this.head; ListNode cur = this.head.next; while(cur != null){ if(cur.val == key){ prev.next = cur.next; cur = cur.next; } else { prev = cur; cur = cur.next; } } //删除的节点为头节点 if(this.head.val == key){ this.head = this.head.next; } }
3.9链表的长度
public int size() { ListNode cur = this.head; int count = 0; while(cur != null) { count++; cur = cur.next; } return count; }
3.9清空链表
public void clear() { ListNode cur = this.head; while(cur != null){ ListNode curNext = cur.next; cur.next = null; cur = curNext; } head = null; }
完整代码
package mysingleList; public class MySingleList implements IList{ 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; } @Override public void addFirst(int data) { ListNode node = new ListNode(data); if(this.head == null){ this.head = node; } else { node.next = this.head; this.head = node; } } @Override public void addLast(int data) { ListNode node = new ListNode(data); ListNode cur = head; if (this.head == null){ this.head = node; } else { while(cur.next != null){ cur = cur.next; } cur.next = node; } } @Override public void addIndex(int index, int data) { if(index < 0 || index >size()){ return; } if(index == 0){ addFirst(data); } if (index == size()){ addLast(data); } else { ListNode node = new ListNode(data); ListNode cur = searchPrev(index); node.next = cur.next; cur.next = node; } } private ListNode searchPrev(int index){ ListNode cur = this.head; int count = 0; while(count != index-1){ cur = cur.next; count++; } return cur; } @Override public boolean contains(int key) { ListNode cur = this.head; while(cur != null){ if(cur.val == key){ return true; } } return false; } @Override public void remove(int key) { if (this.head == null){ System.out.println("没有节点,无法删除"); return; } if (this.head.val == key){ this.head = this.head.next; } else { ListNode cur = findPrev(key); if (cur == null){ System.out.println("没有找到要删除的节点"); return; } ListNode del = cur.next; cur.next = del.next; } } private ListNode findPrev(int key){ ListNode cur = this.head; while(cur.next != null){ if (cur.next.val == key){ return cur; } cur = cur.next; } return null; } @Override public void removeAllKey(int key) { if(this.head == null){ return; } ListNode prev = this.head; ListNode cur = this.head.next; while(cur != null){ if(cur.val == key){ prev.next = cur.next; cur = cur.next; } else { prev = cur; cur = cur.next; } } if(this.head.val == key){ this.head = this.head.next; } } @Override public int size() { ListNode cur = this.head; int count = 0; while(cur != null) { count++; cur = cur.next; } return count; } @Override public void clear() { ListNode cur = this.head; while(cur != null){ ListNode curNext = cur.next; cur.next = null; cur = curNext; } head = null; } @Override public void display() { ListNode cur = this.head; while (cur != null){ System.out.print(cur.val+" "); cur = cur.next; } System.out.println(); } }