单链表的模拟实现
- 一:单链表的概念:
- 二:单链表中的方法:
- 1:得到单链表的长度
- 2:查找是否包含关键字key是否在单链表当中
- 3:打印单链表中的数据:display()
- 3: 头插法
- 4:尾插法
- 5:任意位置插入:
- 6:删除第一次出现关键字为key的节点
- 6:删除所有值为key的节点
一:单链表的概念:
单链表是一种在内存上不连续,而逻辑上连续的一种线性表。
单链表是由若干个节点和头节点组成的数据结构;
每个节点又分为数值域和next域:
数值域用来存储数据;
next域用来存储下一个节点的地址;
下图就是一个节点:
链表由若干个节点组成:
二:单链表中的方法:
// 1、无头单向非循环链表实现
public interface IList {//头插法public void addFirst(int data);//尾插法public void addLast(int data);//任意位置插入,第一个数据节点为0号下标public void 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 display();//清空单链表public void clear();
}
我们将模拟实现这些方法。
1:得到单链表的长度
public int size();
模拟实现:
定义一个变量count,用来计数;
这里我们采取遍历头结点的方法来获取长度:
当头节点不为空的时候,我们就让count++;
public int size() {ListNode cur=head;int count=0;while(cur!=null){count++;cur=cur.next;//cur指向下一个节点}return count;}
2:查找是否包含关键字key是否在单链表当中
public boolean contains(int key);
总体思路还是遍历链表节点中的数据,如果找到了返回true;如果链表遍历完了,仍没有找到,返回false。
public boolean contains(int key) {ListNode cur=head;while(cur!=null){if(cur.val==key){return true;}cur=cur.next;}return false;}
可能有人会想到:如果单链表为空呢?
上面的代码也是没问题的,因为单链表为空,不会进入循环,会返回false的。
3:打印单链表中的数据:display()
@Overridepublic void display() {ListNode cur=head;while(cur!=null){System.out.print(cur.val+" ");cur=cur.next;}System.out.println();}
3: 头插法
将一个节点放在头结点的位置:
头插:在这里我们只需要将node指向他的下一个节点,然后再让头节点更新数据:
看到上图:可能有小伙伴和我有一样的疑问,为什么插入数据的时候,要先绑定后面的数据呢?
这是因为
head=node;
node.next=head;
具体代码实现:
当先更新head的指向时,这时,还没有修改node 的指向,那么原来head指向的节点就没有被指向了,这是它就会被回收!
public void addFirst(int data) {ListNode node =new ListNode(data);//实例化一个单链表if(head==null){head=node;//如果单链表为空,插入的节点就是头结点}else{//node节点的next指向head;node.next=head;head=node;//node变成头节点}}
测试一下:
public class Test {public static void main(String[] args) {SingleLinkedList singleLinkedList=new SingleLinkedList();singleLinkedList.addFirst(1);singleLinkedList.addFirst(2);singleLinkedList.addFirst(3);singleLinkedList.addFirst(4);singleLinkedList.display();}
}
4:尾插法
在这里我们将最后一个节点的next域指向node即可!
head是指向头结点的,那么我们如何获得最后一个节点,进而修改它的next域呢?
很简单,我们看最后一个节点的特点:它的next域为null;那么我们只需要让cur节点从头结点往后移动,当cur.next==null时,说明cur节点指向最后一个节点
public void addLast(int data) {ListNode node =new ListNode(data);if(head==null){head=node;}else {ListNode cur = head;while (cur.next != null) {cur = cur.next;}//此时cur指向最后一个节点cur.next = node;}}
测试一下:
public class Test{public static void main(String[] args) {SingleLinkedList singleLinkedList=new SingleLinkedList();singleLinkedList.addLast(1);singleLinkedList.addLast(2);singleLinkedList.addLast(3);singleLinkedList.addLast(4);singleLinkedList.display();}
}
5:任意位置插入:
在任意位置插入:我们首先要判断插入的位置是否有效;
然后判断单链表是否为空;
在根据插入的位置:
(1)index=0:往第一个节点也就是0下标的位置插入数据,也就是头插法;
(2)index=size():往最后一个位置插入数据,也就是尾插法
(3)其他情况下:
index就是插入中间数据:因为要修改节点的指向,我们需要让index的前一个位置(cur)的next域指向index节点,让index处的节点指向cur的下一个节点
代码实现:
public void addIndex(int index, int data) {if(index<0||index>size()){//判断index是否有效throw new IndexException("index越界"+index);}ListNode node =new ListNode(data);if(head == null) {//判断链表是否为空head = node;return;}if(index==0){//头插addFirst(data);return;}if(index==size()){//尾插addLast(data);return;}//中间插入ListNode cur=searchPrevIndex(index);//cur指向index下标的前一个节点ListNode curNext=cur.next;cur.next=node;node.next=curNext;}private ListNode searchPrevIndex(int index) {ListNode cur=head;int count=0;while(count!=index-1){count++;cur=cur.next;}return cur;}@Overridepublic boolean contains(int key) {ListNode cur=head;while(cur!=null){if(cur.val==key){return true;}cur=cur.next;}return false;}
测试一下:
public class Test{public static void main(String[] args) {SingleLinkedList singleLinkedList=new SingleLinkedList();singleLinkedList.addLast(1);singleLinkedList.addLast(2);singleLinkedList.addLast(3);singleLinkedList.addLast(4);singleLinkedList.display();System.out.println("===========================");singleLinkedList.addIndex(0,10);singleLinkedList.display();System.out.println("=================");singleLinkedList.addIndex(4,22);singleLinkedList.display();System.out.println("=================");singleLinkedList.addIndex(1,100);singleLinkedList.display();}
}
6:删除第一次出现关键字为key的节点
在这里定义一个cur指针用来表示要删除节点的头节点,
public void remove(int key) {if(head==null){return;}if(head.val==key){head=head.next;return;}ListNode cur=searchPrevRemove(key);if(cur==null){return;}ListNode del=cur.next;cur.next=del.next;}private ListNode searchPrevRemove(int key){ListNode cur=head;while(cur.next!=null){if(cur.next.val==key){return cur;}cur=cur.next;}return null;}@Overridepublic void removeAllKey(int key) {}@Overridepublic int size() {ListNode cur=head;int count=0;while(cur!=null){count++;cur=cur.next;//cur指向下一个节点}return count;}@Overridepublic void display() {ListNode cur=head;while(cur!=null){System.out.print(cur.val+" ");cur=cur.next;}System.out.println();}@Overridepublic void clear() {}
}
测试一下:
public class Test{public static void main(String[] args) {SingleLinkedList singleLinkedList=new SingleLinkedList();singleLinkedList.addLast(1);singleLinkedList.addLast(2);singleLinkedList.addLast(3);singleLinkedList.addLast(4);singleLinkedList.addLast(5);singleLinkedList.display();singleLinkedList.remove(1);singleLinkedList.display();System.out.println("=============");singleLinkedList.remove(5);singleLinkedList.display();System.out.println("===============");singleLinkedList.remove(3);singleLinkedList.display();}
}
6:删除所有值为key的节点
定义两个指针:
prev表示可能要删除节点的前一个节点
cur表示要删除的节点。
我们在这里从第二个节点开始遍历,如果cur是要删除的节点,让prev.next=cur.next,也就是让prev指向下一个节点,prev是可能要删除的节点的前一个节点,当cur被删除后,prev仍然是cur下一个节点的前一个节点,所以cur被删除后,prev不能移动。
如果cur节点没有被删除,说明cur不是要被删除的节点,prev要移动到这个cur节点。
为什么此时prev移动呢?
cur节点没有被删除,说明可能删除节点的前一个节点就是cur节点了;判断cur移动到的下一个节点是否要被删除,prev表示可能要删除节点的前一个节点,所以当cur不被删除时,prev要移动
public void removeAllKey(int key) {if(head==null){return;//单链表为空,直接返回}ListNode cur=head.next;//从第二个节点判断是否等于keyListNode prev=head;while(cur!=null){if(cur.val==key){prev.next=cur.next;cur=cur.next;}else{prev=cur;cur=cur.next;}}if(head.val==key){//判断头节点是否==keyhead=head.next;}}
测试一下:
public class Test{public static void main(String[] args) {SingleLinkedList singleLinkedList=new SingleLinkedList();singleLinkedList.addLast(1);singleLinkedList.addLast(2);singleLinkedList.addLast(6);singleLinkedList.addLast(3);singleLinkedList.addLast(4);singleLinkedList.addLast(5);singleLinkedList.addLast(6);singleLinkedList.display();System.out.println("==========================");singleLinkedList.removeAllKey(6);singleLinkedList.display();}
}