文章目录
- 前言
- 1.链表的概念及结构
- 2.单链表的创建
- 3.功能的实现
- 3.1.创建链表(create)(需要自己创建)
- 3.2.显示链表(display)
- 3.3.获取链表的个数( size() )
- 3.4.是否包含指定元素(contains)
- 3.5.头插法(addFirst)
- 3.6.尾插法(addLast)
- 3.7.在指定位置进行插入(addIndex)
- 3.8.删除出现在第一次的指定元素的节点(remove)
- 3.9.删除全部出现指定元素的节点(removeAllKey)
- 3.10.清空链表
前言
在上一篇文章中我们学习了ArrayList顺序表的相关操作和模拟实现,ArrayList顺序表的底层使用数组进行维护的,由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。
1.链表的概念及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
由上面的图我们可以得知:每种链表都是由一个一个节点组成的,主要分为三类 有无头节点,是否循环以及 是单向的还是双向的,一共8种类型。
其中我们只学习2类,第一类是无头非循环单向链表。
第二种是无头非循环双向链表(Java中自带的链表)
下来我们来模拟一下单链表的相关操作。
2.单链表的创建
功能实现:
//无头单向非循环链表实现
//头插法
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 clear();
public void display();
我们把上述功能放在IList接口里面,即使是以后学到的LinkList(双向链表)也要实现这些功能,这样的话,只需要使用重写接口即可,不需要在将这些方法放到类中实现。
Llist接口
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 clear();
public void display();
}
节点的创建:
public class MySingleList {
static class ListNode{
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
}
然后连接接口
public class MySingleList implements IList {
static class ListNode{
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
@Override
public void addFirst(int data) {
}
@Override
public void addLast(int data) {
}
@Override
public void addIndex(int index, int data) {
}
@Override
public boolean contains(int key) {
return false;
}
@Override
public void remove(int key) {
}
@Override
public void removeAllKey(int key) {
}
@Override
public int size() {
return 0;
}
@Override
public void clear() {
}
@Override
public void display() {
}
}
3.功能的实现
3.1.创建链表(create)(需要自己创建)
第一次看这个方法,可能比较糙,先这样写,等会写到3种add方法的时候,再用高级的方法。为等会测试display,size()等方法,做铺垫
public void create(){
ListNode node = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
head = node;
node.next = node2;
node2.next = node3;
node3.next = node4;
}
3.2.显示链表(display)
显示链表(display)的逻辑和获取节点个数(size() )以及 是否包含指定元素的节点(contains)一样。
在进行循环链表的时候,我们一般会在创建一个节点(cur)来代替head,让cur去循环链表而不是让head循环链表。
public void display() {
ListNode cur = head;
while (cur != null){
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
3.3.获取链表的个数( size() )
跟上面的逻辑如出一辙
public int size() {
ListNode cur = head;
int count = 0;
while (cur != null){
count++;
cur = cur.next;
}
return count;
}
3.4.是否包含指定元素(contains)
public boolean contains(int key) {
ListNode cur = head;
while (cur != null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
3.5.头插法(addFirst)
在进行插入和删除的时候,一定要注意只有一个head节点的情况,可能会进行分类讨论。
public void addFirst(int data) {
ListNode node = new ListNode(data);
node.next = head;
head = node;
}
3.6.尾插法(addLast)
public void addLast(int data) {
ListNode node = new ListNode(data);
if(head == null){
head = node;
return;
}
ListNode cur = head;
while (cur.next != null){
cur = cur.next;
}
cur.next = node;
}
3.7.在指定位置进行插入(addIndex)
public void addIndex(int index, int data) {
int len = size();
if (index < 0 || index > len){
throw new CheckPos("位置不合法...");
}
if (index == 0){
addFirst(data);
return;
}
if (index == len){
addLast(data);
return ;
}
ListNode node = new ListNode(data);
ListNode cur = head;
while (index - 1 != 0){
cur = cur.next;
index-- ;
}
node.next = cur.next;
cur.next = node;
}
为了将不合规的位置特殊标出一下,使用了异常。
public class CheckPos extends RuntimeException{
public CheckPos(){
super();
}
public CheckPos(String s){
super(s);
}
}
3.8.删除出现在第一次的指定元素的节点(remove)
@Override
public void remove(int key) {
if(head == null) {
return;
}
if(head.val == key) {
head = head.next;
return;
}
ListNode cur = NodeOfKey(key);
ListNode del = cur.next;
cur.next = del.next;
}
3.9.删除全部出现指定元素的节点(removeAllKey)
要求只遍历1遍,就可以清除指定元素的节点。
可以使用两个变量进行操作 (prev.cur)
private ListNode NodeOfKey(int key){
ListNode cur = head;
while (cur != null){
if (cur.next.val == key){
return cur;
}
cur = cur.next;
}
return null;
}
public void removeAllKey(int key) {
if(head == null){
return;
}
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;
}
}
if (head.val == key){
head = head.next;
}
}
3.10.清空链表
@Override
public void clear() {
ListNode cur = head;
while(cur != null){
ListNode curN = null;
cur = null;
cur = curN;
}
head = null;
}
下一篇我们来讲几道关于单链表的题。