链表的概念及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
实际中链表的结构非常多样,虽然有这么多的链表的结构,但是重点掌握两种:
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶,图的邻接表等等。另外这种结构在笔试面试中出现很多。
- 无头双向链表:在Java的集合框架中LinkedList底层实现就是无头双向循环链表。
链表的实现
测试文件
public class TestDemo2 {
public static void main(String[] args) {
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addLast(3);
myLinkedList.addLast(2);
myLinkedList.addLast(3);
myLinkedList.addLast(3);
myLinkedList.addLast(5);
myLinkedList.addLast(3);
myLinkedList.addLast(7);
myLinkedList.display();
System.out.println(myLinkedList.contains(6));
System.out.println(myLinkedList.size());
myLinkedList.addIndex(0,11);
myLinkedList.display();
myLinkedList.remove(7);
myLinkedList.removeAllKey(3);
myLinkedList.display();
myLinkedList.display();
System.out.println("清空之前");
myLinkedList.clear();
myLinkedList.display();
}
}
链表的功能实现文件
/*无头单向非循环链表实现*/
class Node {
public int data;//默认为0
public Node next;//默认为null
public Node(int data){
this.data = data;
this.next = null;
}
}
public class MyLinkedList {
public Node head;//保存单链表的头节点的引用(地址),默认为null
public void addFirst(int data){//头插法(用构造方法将传来的数字转换为节点对象,构造方法就是实例化对象的)
Node node = new Node(data);//这行代码就将传入的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;
}
private Node searchIndex(int index){//找到index位置的前一个节点的地址
if(index < 0||index > this.size()){//判断index的位置是否合法
throw new RuntimeException("index位置不合法!");
}
Node cur = this.head;
while (index-1!=0) {//往后走index-1步
cur = cur.next;
index--;
}
return cur;
}
public void addIndex(int index,int data){//任意位置插入,第一个数据节点为0号下标
/*1:首先判断index位置是否为0,为0的话可以按照头插法来做
* 2:然后定义一个cur走index-1步
* 3:再去该位置上插入:node.next = cur.next
* cur.next = node;*/
if (index == 0){
addFirst(data);//头插法
return;
}
if (index == this.size()){
addLast(data);//尾插法
return;
}
Node node = new Node(data);
Node cur = searchIndex(index);
node.next = cur.next;
cur.next = node;
}
public boolean contains(int key){//查找是否包含关键字key,也就是关键字key是否在单链表中
Node cur = this.head;
while (cur != null){
if (cur.data == key){
return true;
}
cur = cur.next;
}
return false;
}
private Node searchPrev(int key){//找到要删除节点的头一个节点(称为前驱)
Node prev = this.head;
while (prev.next != null){
if (prev.next.data == key){
return prev;
} else {
prev = prev.next;
}
}
return null;//走到这一步说明没有找到要删除的节点,就返回Null
}
public void remove(int key){//删除第一次出现关键字为key的节点
if (this.head == null){//判断链表中是否有数据,也就是看头节点是否为null
return;
}
if(this.head.data == 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;
}
public void removeAllKey(int key){//删除所有值为key的节点
Node prev = this.head;
Node cur = this.head.next;//代表要删除的节点
while (cur != null){
if (cur.data == key){
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
if (this.head.data == key){//如果删除的元素在头节点
this.head = this.head.next;
}
}
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;
}
}
public void clear(){//清空该链表
this.head = null;
}
}