注意:
* 如果想弄清楚单链表的原理,可以通过画图+代码去理解。
* 我的单链表实现了11个功能,文章前面具体列出了每个方法的代码,以及详细的代码注释和个人解释。
* 如果只要整个源代码,可以直接到文章末尾。包含完整的单链表源代码和main函数测试代码,以及每个方法的测试结果。
*
* 码字不易,也达不到深入理解,但都是经过测试可以运行的。如果有问题,欢迎提出,欢迎白嫖。
1. 实现链表中的单向无头不循环链表
单向指的是每个节点只有后继节点的引用。无头指的是,不含有头节点,也就是头节点不固定。在本次单链表实现中,为了方便打印,查找等功能,我们将会定义一个可变头节点不循环指的是,尾结点指向空,整个链表不构成一个环。
2. 单链表数据结构
每个单链表由n个节点构成。一个节点有两个域,一个data值域,用来存放当前节点的值。另外一个域是引用域,用来存放后继节点的引用。因此,我们可以定义一个节点类,其中包含基本数据类型data,以及后继节点的引用的引用类型next。
class Node {
public int data
;
public Node next
;
public Node(int data
) {
this.data
= data
;
}
}
单链表中包含许多方法,比如插入,删除,等等。我们为了方便方法,可以在单链表类开始,定义一个全局引用head。
public class MySinglyLinkedList {
public Node head
;
}
单链表类中还有许多方法,接下来,将一一实现。
3.单链表实现增删改查等功能
1. 头插法:每次插入数据,都在链表的第一个位置。
public void addFirst(int data
) {
Node node
= new Node(data
);
node
.next
= this.head
;
this.head
= node
;
}
2. 尾插法:每次插入的数据放在最后,即接在原来尾结点后面。
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
;
node
.next
=null
;
}
3. 打印链表:因为单链表有n个节点,所以用自己的打印方法,可以很方便的打印一个单链表。
public void display() {
Node cur
= this.head
;
while (cur
!= null
) {
System
.out
.print(cur
.data
+" ");
cur
= cur
.next
;
}
System
.out
.println();
}
4. 获取单链表长度:通过循环,从头节点移动到尾结点,每移动一个节点,计数变量count加一
public int size() {
int count
= 0;
Node cur
= this.head
;
while (cur
!= null
) {
cur
= cur
.next
;
count
++;
}
return count
;
}
5. 查找index号位置节点的前驱节点。头节点从0开始计算,查找某一个位置的前一个节点。
public Node
searchPrevIndex(int index
) {
if (index
<= 0 || index
>= size()) {
return null
;
}
Node prev
= this.head
;
int count
= 0;
while (count
< index
- 1) {
prev
= prev
.next
;
count
++;
}
return prev
;
}
6. 任意位置插入:一个下标index,一个数据data。
public void addIndex(int index
,int data
) {
if (index
== 0) {
addFirst(data
);
} else if (index
== size()) {
addLast(data
);
} else {
Node cur
= searchPrevIndex(index
);
Node node
= new Node(data
);
node
.next
= cur
.next
;
cur
.next
= node
;
}
}
7. 查找是否包含某个关键字key。
public boolean contains(int key
) {
if (this.head
== null
) {
return false;
} else {
Node cur
= this.head
;
while (cur
!= null
) {
if (cur
.data
== key
) {
return true;
}
cur
= cur
.next
;
}
return false;
}
}
8. 查找关键字key的前驱节点:通过查找单链表中是否含有关键字key,没有返回null,有则返回key节点的前驱节点。
public Node
searchPrevKey(int key
) {
Node prev
= this.head
;
while (prev
.next
!= null
) {
if (prev
.next
.data
== key
) {
return prev
;
}
prev
= prev
.next
;
}
return null
;
}
9. 删除第一次出现的关键字:只删除一次,没有关键字则不删除。
public void removeKey(int key
) {
if (this.head
== null
) {
return;
}
Node del
= searchPrevKey(key
);
if (del
== null
) {
this.head
= this.head
.next
;
} else if (del
.next
.next
== null
) {
del
.next
= null
;
} else {
del
.next
= del
.next
.next
;
}
}
10. 删除单链表中,所有关键字为key的节点。此方法不用循环调用前面删除一个关键字的方法,而是遍历单链表一次,删除所有关键字节点。
首先定义一个无用节点,值随便给,让它指向头节点。然后从这个节点的下一个节点开始判断,这样可以方便的解决,需要删除头节点关键字而带来的麻烦。
public void removeKeyAll(int key
) {
if (this.head
== null
) {
return;
}
Node myHead
= new Node(-1);
Node temp
= myHead
;
myHead
.next
= this.head
;
while (myHead
.next
!= null
) {
if (myHead
.next
.data
== key
) {
if (myHead
.next
.next
== null
) {
myHead
.next
= null
;
return;
} else {
myHead
.next
= myHead
.next
.next
;
}
}
myHead
= myHead
.next
;
}
this.head
= temp
.next
;
}
11. 清空链表。如果直接将头节点置为空,那么头节点后面的节点的next域还有指向关系,这些存在指向关系的节点,因为我们不知道他们的引用(因为我们已经把头节点的引用置为空了),而引用又不为空,所以JVM不会自动回收。就会造成内存泄露。为此,我们必须利用循环,将每一个节点的next域都置为空,这样才会被系统自动回收。
public void clear() {
while (this.head
!= null
) {
Node cur
= this.head
.next
;
this.head
= null
;
this.head
= cur
;
}
}
4. 完整源代码
单链表类(包含单链表的方法+节点Node类)
package day9_10_20
;
public class MySinglyLinkedList {
public Node head
;
public void addFirst(int data
) {
Node node
= new Node(data
);
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
;
node
.next
=null
;
}
public void addIndex(int index
,int data
) {
if (index
== 0) {
addFirst(data
);
} else if (index
== size()) {
addLast(data
);
} else {
Node cur
= searchPrevIndex(index
);
Node node
= new Node(data
);
node
.next
= cur
.next
;
cur
.next
= node
;
}
}
public void display() {
Node cur
= this.head
;
while (cur
!= null
) {
System
.out
.print(cur
.data
+" ");
cur
= cur
.next
;
}
System
.out
.println();
}
public int size() {
int count
= 0;
Node cur
= this.head
;
while (cur
!= null
) {
cur
= cur
.next
;
count
++;
}
return count
;
}
public Node
searchPrevIndex(int index
) {
if (index
<= 0 || index
>= size()) {
return null
;
}
Node prev
= this.head
;
int count
= 0;
while (count
< index
- 1) {
prev
= prev
.next
;
count
++;
}
return prev
;
}
public Node
searchPrevKey(int key
) {
Node prev
= this.head
;
while (prev
.next
!= null
) {
if (prev
.next
.data
== key
) {
return prev
;
}
prev
= prev
.next
;
}
return null
;
}
public boolean contains(int key
) {
if (this.head
== null
) {
return false;
} else {
Node cur
= this.head
;
while (cur
!= null
) {
if (cur
.data
== key
) {
return true;
}
cur
= cur
.next
;
}
return false;
}
}
public void removeKey(int key
) {
if (this.head
== null
) {
return;
}
Node del
= searchPrevKey(key
);
if (del
== null
) {
this.head
= this.head
.next
;
} else if (del
.next
.next
== null
) {
del
.next
= null
;
} else {
del
.next
= del
.next
.next
;
}
}
public void removeKeyAll(int key
) {
if (this.head
== null
) {
return;
}
Node myHead
= new Node(-1);
Node temp
= myHead
;
myHead
.next
= this.head
;
while (myHead
.next
!= null
) {
if (myHead
.next
.data
== key
) {
if (myHead
.next
.next
== null
) {
myHead
.next
= null
;
return;
} else {
myHead
.next
= myHead
.next
.next
;
}
}
myHead
= myHead
.next
;
}
this.head
= temp
.next
;
}
public void clear() {
while (this.head
!= null
) {
Node cur
= this.head
.next
;
this.head
= null
;
this.head
= cur
;
}
}
}
class Node {
public int data
;
public Node next
;
public Node(int data
) {
this.data
= data
;
}
}
main方法测试类
package day9_10_20
;
import org
.w3c
.dom
.Node
;
public class MySinglyLinkedListTest {
public static void main(String
[] args
) {
MySinglyLinkedList myList
= new MySinglyLinkedList();
System
.out
.println("1、头插法1,2,3,4,5测试:");
myList
.addFirst(1);
myList
.addFirst(2);
myList
.addFirst(3);
myList
.addFirst(4);
myList
.addFirst(5);
myList
.display();
System
.out
.println("2、打印测试:");
myList
.display();
System
.out
.println("3、尾插法6,7,8,9,10测试:");
myList
.addLast(6);
myList
.addLast(7);
myList
.addLast(8);
myList
.addLast(9);
myList
.addLast(10);
myList
.display();
System
.out
.println("4、查找3号位置前驱节点测试:");
System
.out
.println(myList
.searchPrevIndex(3).data
);
System
.out
.println("5、获取单链表长度测试:");
System
.out
.println(myList
.size());
System
.out
.println("6、在10号位置插入元素99测试:");
myList
.addIndex(10,99);
myList
.display();
System
.out
.println("7、是否包含关键字99测试:");
System
.out
.println(myList
.contains(99));
System
.out
.println("8、查找关键字99的前驱测试:");
System
.out
.println(myList
.searchPrevKey(99).data
);
System
.out
.println("9、删除第一个出现的关键字99测试:");
myList
.removeKey(99);
myList
.display();
System
.out
.println("10、删除所有关键字8测试:");
myList
.addIndex(4,8);
myList
.addIndex(1,8);
myList
.addIndex(0,8);
System
.out
.print("删除前:");
myList
.display();
System
.out
.print("删除后:");
myList
.removeKeyAll(8);
myList
.display();
System
.out
.println("11、清空单链表测试(防止内存泄露):");
myList
.clear();
myList
.display();
System
.out
.println("-----------结束------------");
}
}
测试结果展示
欢迎白嫖,感谢提出问题^ - ^