链表
链表的概念及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
注意:
1. 从上图可看出,链式结构在逻辑上连续的,但在物理上不一定连续
-
现实中的结点一般都是在堆上申请出来的
-
从堆上申请的空间,是按照一定策略来分配的,两次申请的空间可能连续可能不连续
而链表的结构非常的多样:
1.单向 或 双向
2.带头或 不带头
3.循环或非循环
虽然结构众多,但重点掌握两种:
-
**无头单向非循环链表:**结构简单,一般不会单独用来存储数据。实际中更多时作为其他数据结构的子结构,如哈希桶,图的邻接表等。
-
**无头双向链表:**在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。
链表的模拟实现
无头单向非循环链表
// 1、无头单向非循环链表实现
public class SingleLinkedList {
//头插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public boolean 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();
public void toString();
public void clear();
}
package singleLinkedList;
/**
* Created with IntelliJ IDEA
* Description:结点
* User: 16037
* Date: 2022-03-12
* Time:14:25
*/
public class Node {
public int val; //结点值
public Node next; //下一个结点引用
public Node(int val) {
this.val = val;
}
}
/**
* Created with IntelliJ IDEA
* Description:无头单向非循环链表的简单模拟
* User: 16037
* Date: 2022-03-12
* Time:14:28
*/
public class SingleLinkedList {
public Node head; //用成员变量来引用局部生成的Node,局部Node就不会被回收
public int usedSize;
//头插法
public void addFirst(int data){
Node node = new Node(data);
node.next = this.head;
this.head = node;
this.usedSize++;
}
//尾插法
public void addLast(int data) {
Node node = new Node(data);
if (this.head == null){
this.head = node;
}else {
Node cur = this.head;
while(cur.next!=null){
cur = cur.next;
}
cur.next = node;
}
this.usedSize++;
}
/**
* 寻找下标为index的结点
@param index
* @return 存在返回下标为index的结点cur,否则返回null
/
private Node searchIndex(int index){
//对index合法性判断
if(index < 0 || index >= this.usedSize){
return null;
}
Node cur = this.head;
for(int i = 0;i < index;i++){
cur = cur.next;
}
return cur;
}
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data){
//头插
if(index == 0){
addFirst(data);
return true;
}
//寻找index前一个,为头插时无法处理,列出处理
Node cur = searchIndex(index-1);
if(cur == null)
return false;
Node node = new Node(data);
node.next = cur.next;
cur.next = node;
this.usedSize++;
return true;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
Node cur = this.head;
while(cur!=null){
//引用类型 equals
if(cur.val==key)
return true;
cur=cur.next;
}
return false;
}
/**
* 找到关键字key的前驱结点,默认初始prev为head
@param key
* @return 存在返回prev,不存在返回null
/
private Node findPrevKey(int key){
Node prev = this.head;
Node findKey = prev.next;
while(findKey!=null){
if(findKey.val == key){
return prev;
}
prev = findKey;
findKey = findKey.next;
}
return null;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
//链表中无结点
if(this.head == null){
return;
}
//所删结点为头结点
if(this.head.val == key){
this.head = this.head.next;
this.usedSize--;
return;
}
Node prev = findPrevKey(key);
//无关键字为key的前驱
if(prev == null)
return;
Node del = prev.next;
prev.next = del.next;
this.usedSize--;
}
//删除所有值为key的节点
public void removeAllKey(int key){
Node del = this.head;
Node pre = null;
while(del!=null){
//所删结点为头结点
if(del.val==key && pre == null){
this.head = del.next;
del = del.next;
this.usedSize--;
continue;
}else if(del.val==key) {
pre.next = del.next;
del = del.next;
this.usedSize--;
continue;
}
pre = del;
del=del.next;
}
}
//得到单链表的长度
public int size(){
return this.usedSize;
}
@Override
public String toString(){
StringBuffer s = new StringBuffer();
if(this.head == null){
s.append("[]");
return s.toString();
}
s.append('[');
Node cur = this.head;
while(cur!=null){
s.append(cur.val);
if(cur.next != null)
s.append(',');
cur=cur.next;
}
s.append(']');
return s.toString();
}
public void clear(){
this.head = null;
this.usedSize = 0;
}
}
无头双向链表实现
// 2、无头双向链表实现
public class DoubleLinkedList {
//头插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public boolean 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();
public void toString();
public void clear();
}
package doubleLinkedList;
/**
* Created with IntelliJ IDEA
* Description:
* User: 16037
* Date: 2022-03-13
* Time:20:10
*/
public class Node {
public int val;
public Node prev;
public Node next;
public Node(int val) {
this.val = val;
}
}
package doubleLinkedList;
/**
* Created with IntelliJ IDEA
* Description:
* User: 16037
* Date: 2022-03-16
* Time:16:17
*/
public class DoubleLinkedList {
public Node head;
public Node last;
public int usedSize;
public void addFirst(int data){
if(this.head == null){
this.head = new Node(data);
this.last = this.head;
}else{
Node node = new Node(data);
node.next = this.head;
this.head.prev = node;
this.head = node;
this.last = node;
}
this.usedSize++;
}
public void addLast(int data){
if(this.head == null){
addFirst(data);
}else{
Node node = new Node(data);
this.last.next = node;
node.prev = this.last;
this.last = node;
this.usedSize++;
}
}
private Node searchByIndex(int index){
Node cur = this.head;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur;
}
public void addIndex(int index,int data){
if(index < 0 || index > this.usedSize){
throw new RuntimeException("下标越界!");
}
if(index == 0){
addFirst(data);
}
if(index == this.usedSize){
addLast(data);
}
Node next = searchByIndex(index);
Node node = new Node(data);
Node prev = next.prev;
prev.next = node;
node.prev = prev;
node.next = next;
next.prev = node;
this.usedSize++;
}
public boolean contains(int key){
Node cur = this.head;
while(cur!=null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
private Node searchByKey(int key){
Node cur = this.head;
while(cur!=null){
if(cur.val == key){
return cur;
}
cur = cur.next;
}
return null;
}
public void remove(int key){
if(this.head == null){
System.out.println("链表为空");
return ;
}
Node del = searchByKey(key);
if(del == null){
throw new RuntimeException("无效关键字!");
}
if(del == this.head){ //删头结点
this.head = this.head.next;
if(this.head == null){ //只有一个结点
this.last = null;
}else{
this.head.prev = null;
}
}else if(del == this.last){ //删除尾结点
this.last = this.last.prev;
this.last.next = null;
}else{ //删除中间结点
Node prev = del.prev;
Node next = del.next;
prev.next = next;
next.prev = prev;
}
this.usedSize--;
}
public void removeAllKey(int key){
if(this.head == null){
System.out.println("LinkedList is Null!");
return ;
}
if(this.head.val == key && this.head.next == null){
this.head = null;
this.last = null;
this.usedSize--;
return ;
}
Node prev = this.head;
Node cur = this.head.next;
while(cur.next != null){
if(cur.val == key){
prev.next = cur.next;
cur.next.prev = prev;
cur = cur.next;
this.usedSize--;
continue;
}
cur = cur.next;
prev = cur;
}
if(this.last.val == key){
this.last = this.last.prev;
this.last.next = null;
this.usedSize--;
}
if(this.head.val == key){
this.head = this.head.next;
if(this.head == null){
this.last = null;
}else{
this.head.prev = null;
}
this.usedSize--;
}
}
public int size(){
return this.usedSize;
}
public void clear(){
Node cur = this.head;
while(cur!= null){
Node next = cur.next;
cur.next = null;
cur.prev = null;
cur = next;
}
this.head = null;
this.last = null;
this.usedSize = 0;
}
@Override
public String toString() {
StringBuffer str = new StringBuffer();
if(this.head == null){
str.append("[]");
return str.toString();
}
str.append('[');
Node cur = this.head;
while(cur.next != null){
str.append(cur.val);
str.append(',');
cur = cur.next;
}
str.append(cur.val);
str.append(']');
return str.toString();
}
}
LinkedList的使用
什么是LinkedList
LinkedList的底层是无头双向非循环链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高
在集合框架中,LinkedList也实现了List接口,具体如下:
【说明】
- LinkedList实现了List接口
- LinkedList的底层使用了双向链表
- LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问
- LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)
LinkedList的使用
LinkedList的构造
方法 | 解释 |
---|---|
LinkedList() | 无参构造 |
public LinkedList(Collection<? extends E> c) | 使用其他集合容器中元素构造List |
public static void main(String[] args) {
// 构造一个空的LinkedList
List<Integer> list1 = new LinkedList<>();
List<String> list2 = new java.util.ArrayList<>();
list2.add("JavaSE");
list2.add("JavaWeb");
list2.add("JavaEE");
// 使用ArrayList构造LinkedList
List<String> list3 = new LinkedList<>(list2);
}
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 subList(int fromIndex, int toIndex) | 截取部分 list |
LinkedList的遍历
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1); // add(elem): 表示尾插
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
System.out.println(list.size());
// foreach遍历
for (int e:list) {
System.out.print(e + " ");
}
System.out.println();
// 使用迭代器遍历---正向遍历
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()){
System.out.print(it.next()+ " ");
}
System.out.println();
// 使用反向迭代器---反向遍历
ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){
System.out.print(rit.previous() +" ");
}
System.out.println();
}
最难不过坚持!