链表
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。链表的结构非常多样,以下情况组合起来就有8种链表结构:单向(带头/不带头)(循环/非循环),双向(带头/不带头)(循环/非循环),其中无头单向非循环链表和无头双向链表需要重点掌握。
无头单向非循环链表
结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等。
class Node{
public int date;
public Node next;
public Node(int date) {
this.date = date;
this.next = null;
}
}
public class MyLinkedList {
public Node head; //保存单链表的头结点的引用
//头插法
public void addFirst(int data) {
Node node = new Node(data);
if (this.head == null) {
//第一次插入节点
this.head = node;
return;
}
node.next = this.head;
this.head = node;
}
//尾插法
public void addLast(int data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
return;
}
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data) {
if (index == 0) {
addFirst(data);
return;
}
if (index == this.size()) {
addLast(data);
return;
}
Node node = new Node(data);
//先找到index位置的前一个节点的地址
Node cur = searchIndex(index);
//进行插入
node.next = cur.next;
cur.next = node;
}
private Node searchIndex(int index) {
//1,对index进行合法性检查
if (index < 0 || index >this.size()) {
throw new RuntimeException("index位置不合法!");
}
Node cur = this.head; //index-1
while (index - 1 != 0) {
cur = cur.next;
index--;
}
return cur;
}
//查找是否包含关键字key是否在单链表当中
public boolean contain(int key) {
Node cur = this.head;
while (cur != null) {
if (cur.date == key) {
return true;
}
cur = cur.next;
}
return false;
}
//得到单链表长度
public int size() {
int count = 0;
Node cur = this.head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
private Node searchPrev(int key) {
Node prev = this.head;
while (prev.next != null) {
if (prev.next.date != key) {
return prev;
} else {
prev = prev.next;
}
}
return null;
}
//删除第一次出现关键字为key的节点
public void remove(int key) {
if (this.head == null) {
return;
}
//删除的是不是头结点
if (this.head.date == key) {
this.head = this.head.next;
return;
}
//找到删除节点的前驱
Node prev = searchPrev(key);
if (prev == null) {
System.out.println("根本没有这个节点!");
return;
}
//开始删除
Node del = prev.next;
prev.next = del.next;
}
//删除所有值为key的节点
public void removeAllKey(int key) {
Node prev = this.head;
Node cur = this.head.next;//代表要删除的节点
while (cur != null) {
if (cur.date == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
if (this.head.date == key) {
this.head = this.head.next;
}
}
//打印单链表
public void disPlay() {
Node cur = this.head;
while (cur != null) {
System.out.println(cur.date + " ");
cur = cur.next;
}
System.out.println();
}
//释放内存
public void clear() {
this.head = null;
}
无头双向链表
在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。
class Node {
public int data;
public Node next;
public Node prev;
public Node(int data) {
this.data = data;
Node next = null;
Node prev = null;
}
}
public class MyLinklist {
public Node head; //标志双向链表的头
public Node tail; //标志当前双向链表的尾
//头插法
public void addFirst(int data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
this.tail = node;
} else {
node.next = this.head;
this.head.prev = node;
this.head = node;
}
}
//尾插法
public void addLast(int data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
this.tail = node;
} else {
this.tail.next = node;
node.prev = this.tail;
this.tail = node;
}
}
private void checkIndex(int index) {
if (index < 0 || index >size()) {
throw new RuntimeException("index不合法!");
}
}
private Node searchIndex(int index) {
Node cur = this.head;
while (index != 0) {
cur = cur.next;
index--;
}
return cur;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data) {
checkIndex(index);
if (index == 0) {
addFirst(data);
return;
}
if (index == size()) {
addLast(data);
return;
}
Node cur = searchIndex(index);
Node node = new Node(data);
node.next = cur;
node.prev = cur.prev;
cur.prev.next = node;
cur.prev = node;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key) {
Node cur = this.head;
while (cur != null) {
if (cur.data == key) {
return true;
}
cur = cur.next;
}
return false;
}
//删除第一次出现关键字为key的节点
public int remove(int key) {
Node cur = this.head;
while (cur != null) {
if (cur.data == key) {
int oldDate = cur.data;
if (cur == this.head) {
this.head = this.head.next;
this.head.prev = null;
} else {
cur.prev.next = cur.next;
if (cur.next != null) {
cur.next.prev = cur.prev;
} else {
//删除的是尾巴节点 只需要移动tail
this.tail = cur.prev;
}
}
return oldDate;
}
cur = cur.next;
}
return -1;
}
//删除所有值为key的节点
public int removeAllKey(int key) {
Node cur = this.head;
while (cur != null) {
if (cur.data == key) {
if (cur == this.head) {
this.head = this.head.next;
this.head.prev = null;
} else {
cur.prev.next = cur.next;
if (cur.next != null) {
cur.next.prev = cur.prev;
}else {
this.tail = cur.prev;
}
}
}
cur = cur.next;
}
return -1;
}
//得到单链表的长度
public int size() {
int count = 0;
Node cur = this.head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
public void display() {
Node cur = this.head;
while (cur != null) {
System.out.print(cur.data + " ");
cur = cur.next;
}
System.out.println();
}
public void clear() {
while (this.head != null) {
Node cur = this.head.next;
this.head.prev = null;
this.head.next = null;
this.head = cur;
}
this.tail = null;
}
}
顺序表和链表的区别和联系
顺序表:优点是空间连续、支持随机访问;缺点是中间或前面部分的插入删除时间复杂度O(N) ,增容的代价比较大。
链表:优点是任意位置插入删除时间复杂度为O(1) ,没有增容问题,插入一个开辟一个空间;缺点是以节点为单位存储,不支持随机访问