一、IList
IList 是一个链表操作规范接口,它只规定要实现什么函数,具体实现内容在别的地方,代码如下。
java
public interface IList {
void addFirst(int data);
void addLast(int data);
void addIndex(int index,int data);
boolean contains(int key);
void remove(int key);
void removeAllKey(int key);
int size();
void clear();
void display();
}
二、MySingleList
MySingleList是一个单链表实现类,实现了接口 IList 中规定的所有操作功能,包括头插、尾插、任意位置插入、查找、删除、统计长度、清空和遍历等。内部使用 ListNode 节点类,通过 next 指针把节点串成一条链。代码如下。
java
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 creatList(){
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=this.head;
if(this.head==null){
this.head=node;
}else{
while(cur.next!=null){
cur=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 void addIndex(int index, int data) {
if(index<0||index>size()){
return;
}
if(index==0){
addFirst(data);
return;
}
if(index==size()){
addLast(data);
return;
}
ListNode cur=searchPrev(index);
ListNode node=new ListNode(data);
node.next=cur.next;
cur.next=node;
}
@Override
public boolean contains(int key) {
ListNode cur=this.head;
while(cur!=null)
{
if(cur.val==key){
return true;
}
cur=cur.next;
}
return false;
}
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 remove(int key) {
if(this.head==null){
return;
}
if(this.head.val==key){
this.head=this.head.next;
return;
}
ListNode cur=findPrev(key);
if (cur == null) {
System.out.println("没找到");
return;
}
ListNode del=cur.next;
cur.next=del.next;
}
@Override
public void removeAllKey(int key) {
if(this.head==null){
return;
}
if(head.val==key){
head=head.next;
}
ListNode prev=head;
ListNode cur=head.next;
while(cur!=null){
if(cur.val==key){
prev.next=cur.next;
cur=cur.next;
}else{
prev=cur;
cur=cur.next;
}
}
}
@Override
public int size() {
int count=0;
ListNode cur=head;
while(cur!=null){
count++;
cur=cur.next;
}
return count;
}
@Override
public void clear() {
ListNode cur=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();
}
}
creatList()通过手动创建多个节点对象,然后用 next 指针把它们依次连接起来,这样可以快速得到一条固定数据的链表。
addFirst(int data)用于头插新节点。首先创建一个新节点,如果链表为空,直接让 head 指向该节点即可;如果链表不为空,就先让新节点的 next 指向当前头节点,再让 head 指向新节点,这样新节点就成为新的第一个节点。
addLast(int data)用于尾插新节点。先创建新节点,如果链表为空,直接让 head 指向它;如果不为空,就从头节点开始遍历,找到最后一个节点,再让这个节点的 next 指向新节点,从而把新节点接到链表末尾。
searchPrev(int index)这个私有方法用于查找指定下标位置的前一个节点。从头节点开始遍历,用计数器记录当前位置,当计数器等于 index - 1 时停止,此时返回的节点正好是目标插入位置的前驱节点。
addIndex(int index, int data)该方法用于在指定下标位置插入新节点。首先判断下标是否合法,如果非法直接返回;如果插入位置是 0,就调用 addFirst();如果插入位置是链表长度,就调用 addLast();否则通过 searchPrev(index) 找到前驱节点,再把新节点插入到前驱节点与其后继节点之间。
contains(int key)该方法用于判断链表中是否存在指定值。从头节点开始遍历链表,只要发现某个节点的值等于 key,就立即返回 true;如果遍历完整个链表仍未找到,则返回 false。
findPrev(int key)这个私有方法用于查找第一个值为 key 的节点的前驱节点。从头节点开始遍历,如果发现 cur.next.val == key,就返回当前节点 cur;如果遍历到尾部仍未找到,则返回 null,表示链表中不存在该值。
remove(int key)该方法用于删除链表中第一次出现的指定值节点。若链表为空直接返回;如果头节点的值等于 key,则直接让 head 指向第二个节点完成删除;否则通过 findPrev(key) 找到目标节点的前驱节点,然后让该前驱节点跳过目标节点,即 cur.next = cur.next.next,从而完成删除。
removeAllKey(int key)该方法用于删除链表中所有值为 key 的节点。先单独处理头节点为 key 的情况,然后使用 prev 和 cur 两个指针遍历链表:当 cur.val == key 时,让 prev.next 指向 cur.next 实现删除;否则两个指针同时向后移动,直到遍历结束。
size()该方法用于统计链表长度。从头节点开始遍历链表,每访问一个节点就让计数器加一,直到遍历到 null,最终返回计数值,即链表中节点的个数。
clear()该方法用于清空链表。通过遍历链表,将每个节点的 next 置为 null,断开节点之间的引用关系,最后将 head 置为 null,使链表恢复为空状态。
display()该方法用于输出链表中所有节点的值。从头节点开始遍历,每访问一个节点就打印其 val,直到遍历结束并换行,用于直观查看链表当前结构。
三、Test
下面是测试代码。
java
public class Test {
public static void main(String[] args) {
MySingleList list = new MySingleList();
System.out.println("=== 1. 测试 creatList ===");
list.creatList();
list.display(); // 12 23 34 45 56
System.out.println("=== 2. 测试 addFirst ===");
list.addFirst(100);
list.display(); // 100 12 23 34 45 56
System.out.println("=== 3. 测试 addLast ===");
list.addLast(200);
list.display(); // 100 12 23 34 45 56 200
System.out.println("=== 4. 测试 addIndex ===");
list.addIndex(3, 999);
list.display(); // 100 12 23 999 34 45 56 200
System.out.println("=== 5. 测试 contains ===");
System.out.println(list.contains(34)); // true
System.out.println(list.contains(888)); // false
System.out.println("=== 6. 测试 remove ===");
list.remove(100); // 删头
list.display();
list.remove(999); // 删中间
list.display();
list.remove(200); // 删尾
list.display();
System.out.println("=== 7. 测试 removeAllKey ===");
list.addLast(34);
list.addLast(34);
list.display();
list.removeAllKey(34);
list.display();
System.out.println("=== 8. 测试 size ===");
System.out.println(list.size());
System.out.println("=== 9. 测试 clear ===");
list.clear();
list.display(); // 空
System.out.println(list.size()); // 0
}
}