线性表的链式存储结构:单链表
特点:逻辑上相邻的元素在物理存储上是不一定相邻的。每个内存单元之间通过指针相连。
单链表和顺序表的不同点在于存储结构,由于存储位置不连贯,想要找到下个元素,需要指针,也就是地址(java中也叫引用)
算法的实现还是在代码中,要点还是老样子,记录在注释中。
首先是遍历链表
借助指针,“顺藤摸瓜” (沿着链表访问结点)。
//遍历单链表
public void visitLinkList(LinkList1 L){
LinkList1 p = L;
//P.getNext()方法获取的是LinkList1对象的指针域,这里跟C语言中的L.next是一致的。
while(p!=null){
System.out.println(p.getData());
p = p.getNext();
}
}
@Test
//测试遍历单链表
public void testVisitLinkList(){
//创建对象,并赋值。
LinkList1 l1 = new LinkList1();
LinkList1 l2;
LinkList1 l3;
l3 = l1;//l3指向头结点
for(int i = 0 ;i < 6 ;i++){
l2 = new LinkList1();//创建新结点l2
System.out.println(i);
l2.setData(""+i);
l3.setNext(l2);//在第一次循环中因为l3指向头结点l1,所以l3的指针域就是头结点l1的指针域,相当于l1.next -> l2
l3 = l2;//l3指向新结点。
}
l3.setNext(null);
//遍历l1
visitLinkList(l1);
//查找指定元素
LinkList1 q = l1;
int count = 0;
while(q!=null){
if("3".equals(q.getData())){
System.out.println(count);
}
count ++;//查找指定元素的位置或者根据指定位置查找数据都需要借助计数器
q = q.getNext();
}
// 打印结果:
// 0
// 1
// 2
// 3
// 4
// 5
// null
// 0
// 1
// 2
// 3
// 4
// 5
// 4
}
插入结点
思路:
先查找第 i-1 个元素
若找到,在其后插入新结点
//测试插入单链表
@Test
public void testInsertLinkList(){
//创建对象,并赋值。
LinkList1 l1 = new LinkList1();
LinkList1 l2;
LinkList1 l3;
l3 = l1;//l3指向头结点
for(int i = 0 ;i < 6 ;i++){
l2 = new LinkList1();//创建新结点l2
System.out.println(i);
l2.setData(""+i);
l3.setNext(l2);//在第一次循环中因为l3指向头结点l1,所以l3的指针域就是头结点l1的指针域,相当于l1.next -> l2
l3 = l2;//l3指向新结点。
}
l3.setNext(null );
int index = 4; //位置
int count = 0;
String data = "test"; //插入元素的数据
LinkList1 P = l1;
while(P!=null && count < index-1){
P = P.getNext();
count++;
}
if(P != null && count == index-1){ //1指针在链表上且2计数器在指定位置
LinkList1 q = new LinkList1();//创建插入结点
q.setData(data);//将数据放入插入结点
q.setNext(P.getNext());//1.插入结点的指针指向指针P所指向的链表结点的指针域。q.next -> P.next
P.setNext(q);//2.P的指针指向插入结点。p.next -> q 如图。
}
visitLinkList(l1);
// 打印结果:
// null
// 0
// 1
// 2
// test
// 3
// 4
// 5
}
删除结点
思路:
先查找第 i-1 个元素
若找到且其后存在第 i 个元素,则用 x 返回数据,并删除之
//测试删除结点
@Test
public void testdeleteLinkNode(){
//创建对象,并赋值。
LinkList1 l1 = new LinkList1();
LinkList1 l2;
LinkList1 l3;
l3 = l1;//l3指向头结点
for(int i = 0 ;i < 6 ;i++){
l2 = new LinkList1();//创建新结点l2
System.out.println(i);
l2.setData(""+i);
l3.setNext(l2);//在第一次循环中因为l3指向头结点l1,所以l3的指针域就是头结点l1的指针域,相当于l1.next -> l2
l3 = l2;//l3指向新结点。
}
l3.setNext(null );
int index = 4 ; //要删除的位置
int count = 0; //计数器
LinkList1 p = l1;
while(p != null && count < index - 1){
count ++ ;//计数器自增
p = p.getNext();//指针后移
}
if(p != null && count == index - 1){//1指针在链表上且2计数器在指定位置的前一位置
System.out.println(p.getNext().getData());//打印要删除结点的数据
LinkList1 q = p.getNext(); //创建指针指向要删除的结点
p.setNext(q.getNext()); //删除结点的前一结点的指针域指向删除结点的下一个结点 p.next ->q.next
p = null;//我们把指针置空,丢弃之前的结点,java的垃圾回收机制会不定时自动回收丢弃的内存。
q = null;//注意我们用的java所以不需要手动释放内存,在C或者其他语言中需要手动释放丢弃的内存。
}
visitLinkList(l1);
//打印结果:
// 0
// 1
// 2
// 3
// 4
// 5
// 3
// null
// 0
// 1
// 2
// 4
// 5
}
此外,创建链表的方式也是一个重点,代码中我使用的是顺序建表,即先创建表头,在依次创建结点,上一结点的指针域指向新增结点。还可以逆序建表,首先创建尾结点,创建指针指向尾结点,然后依次创建新节点,指向指针,指正迭代到新创建的节点,在这里就不在代码中演示了。链表的插入删除关键点在于对指针指向的先后问题。
循环链表