单链表
优点
- 单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制
- 插入和删除更快,时间复杂度为O(n),在给出某位置的指针后,复杂度仅为O(1)
缺点
- 查找效率比顺序存储结构差,时间复杂度为O(n)
package com.company;
/**
* @author wsh
*/
public class Main {
public static void main(String[] args) {
LinkArr linkArr = new LinkArr();
String data = "31,21,17,16,15,26,5,14,23,12,19,30,27,95,22,44,66,99,39";
// String data = "31,21,17,16";
linkArr.headInit(data.split(","));
linkArr.out("头插法初始化输出");
linkArr.lastInit(data.split(","));
linkArr.out("尾插法初始化输出");
linkArr.insert(100,22);
linkArr.out("在第100插入22后输出");
linkArr.insert(0,33);
//这里插入index=-1 也会插在第一个位置
linkArr.out("在第0插入33后输出");
linkArr.delete(99);
linkArr.out("删除第99个后输出");
linkArr.delete(5);
linkArr.out("删除第5个后输出");
}
}
class LinkArr{
private static Node rootNode= new Node();
/**
* 插入第i个
*/
public void insert(int index, int value) {
int k=1;
Node p = rootNode;
//找到位置
while (p.nextNode!=null&&k < index) {
p = p.nextNode;
k++;
}
//插入
Node n = new Node();
n.data = value;
n.nextNode=p.nextNode;
p.nextNode = n;
}
public void delete(int index) {
int k=1;
Node p = rootNode;
//找到位置
while (p.nextNode!=null&&k < index) {
p = p.nextNode;
k++;
}
Node nextNode = p.nextNode;
if (nextNode == null||k>index) {
System.out.println("删除"+index+"失败");
}else{
p.nextNode = nextNode.nextNode;
}
}
/**
* 头插法 初始化
* 新建一个节点A, 设置下个节点null B
* "插入"在 A和B之间
*/
public void headInit(String [] data){
rootNode.data = null;
rootNode.nextNode=null;
for (String datum : data) {
Node n = new Node();
n.data = Integer.parseInt(datum);
n.nextNode = rootNode.nextNode;
rootNode.nextNode = n;
}
}
/**
* 使用尾插法初始化
* 借助变量p 使其指向 当前链表的最后一个节点
* 新节点 "接" 上p
* p再指向这个新节点
*/
public void lastInit(String[] data){
//头节点空数据
rootNode.data = null;
Node p = rootNode;
for (String datum : data) {
Node n = new Node();
n.data = Integer.parseInt(datum);
p.nextNode=n;
p=n;
}
//二更
//在单链表时觉得下面这行可有可无
//书本上写表示当前链表结束,可能更为准确
//在后续学习中,当变成循环链表时尤为重要
p.nextNode=null;
}
public void out(String str){
System.out.print(str+" ");
int i=0;
Node p = rootNode;
while ( p.nextNode!= null) {
//i 变量 为了输出间隔 好辨认
if(i%5==0) {
System.out.print(" ");
}
i++;
System.out.print (p.nextNode.data+",");
p = p.nextNode;
}
System.out.println();
}
}
class Node{
Integer data;
Node nextNode;
}
收获
-
所谓头插法,始终有一个"头节点",第一次不过是"插入"在头节点和下一个null节点直接,并非新节点下一个指向整个链表头,那叫抢风头,重点名字中有个插字
-
尾插法,我理解就是排队接入法, 没有什么"插入"的操作,确实新建一个,接上一个尾巴里,然后其中指向尾巴的变量p,指向这个新节点.以此类推
-
delete删除操作,代码
p.nextNode = nextNode.nextNode;
,例如ABC节点,删除B,让A的直接指向C.我们要做的就是找到B的前一个节点,所以删除代码中循环一直是p.nextNode
,从而这样计数当k==index
,此时的p
变量就是A,p.nextNode
就是index
也就是B,然后套入代码就是A.nextNode=B.nextNode.nextNode
-
delete删除操作时容易出问题,故添加
if (nextNode == null||k>index)
,涉及到两个方面
- 删除index大于链表长度,循环找了一圈,没找到,但是变量p指向了链尾,这样
p.nextNode==null
,在数组这个错误叫溢出,所以要判断出来 - 删除index输入-1或者 0时 ,导致连循环都没有,次数
k==1
值无变化,纯属捣乱 - 拓展一下2的思路,我脑海想 改成
k==1
不也可以?实时证明当删除index=1
时,不满足循环条件,k值无变化,但是确实有index==1
这个节点,k==1
会导致本该成功的显示删除失败