package com.zf.test.linken;
/**
* @program: test
* @description: 测试链表的结构
* @author: liuzongfang
* @create: 2019-08-15 09:14
**/
public class LinkenList {
//头结点指针
private Node head;
//尾结点指针
private Node last;
//链表的实际长度
private int size;
/**
* 链表插入元素
* @param data 插入元素
* @param index 插入位置
*/
public void insert(int data,int index) throws Exception{
if(index<0 || index >size){
throw new IndexOutOfBoundsException("超出链表结点范围");
}
Node insertedNode = new Node(data);
if(size == 0){
//空链表
head = insertedNode;
last = insertedNode;
}else if (index == 0){
//插入头部
insertedNode.next = head;
head = insertedNode;
}else if(size == index){
//插入尾部
last.next = insertedNode;
last = insertedNode;
}else {
//中间插入
Node prevNode = get(index - 1);
insertedNode.next = prevNode.next;
prevNode.next = insertedNode;
}
size++;
}
/**
* 查找元素链表位置
* @param index
* @return
*/
private Node get(int index){
if(index < 0 || index >= size){
throw new IndexOutOfBoundsException("超出链表结点范围!");
}
Node temp = head;
for (int i = 0; i<index ;i++){
temp = temp.next;
}
return temp;
}
/**
* 打印输出链表
*/
public void output(){
Node temp = head;
while (temp!=null){
System.out.println(temp.data);
temp = temp.next;
}
}
/**
* 结点对象
*/
private static class Node{
int data;
Node next;
Node(int data){
this.data = data;
}
}
public static void main(String[] args) {
LinkenList linkenList = new LinkenList();
try {
linkenList.insert(3,0);
linkenList.insert(7,1);
linkenList.insert(9,2);
linkenList.insert(5,3);
linkenList.insert(6,4);
linkenList.output();
// linkenList.(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先明白,list是一个链表,链表在内存中种的存储方式是随机储存的,即见缝插针的方式,链表的每一个节点分布在内存中的不同位置,通过next指针像连接,这样可以灵活有效的利用零散的内存空间,前一个结点存储后一个结点的hash地址,
因此可以可以设计一个单项链表的对象作为一个内存节点,如果是双向链表,可以有三个属性,before,data,next
/**
* 结点对象
*/
private static class Node{
int data;
Node next;
Node(int data){
this.data = data;
}
}
下面两张图可以比好好的阐述上面的代码,链表是怎么插入新节点,新节点怎么和前一个结点进行关联的
public void insert(int data,int index) throws Exception{
if(index<0 || index >size){
throw new IndexOutOfBoundsException("超出链表结点范围");
}
Node insertedNode = new Node(data);
if(size == 0){
//空链表
head = insertedNode;
last = insertedNode;
}else if (index == 0){
//插入头部
insertedNode.next = head;
head = insertedNode;
}else if(size == index){
//插入尾部
last.next = insertedNode;
last = insertedNode;//此时会有一个疑惑,浅层赋值会让last消失
}else {
//中间插入
Node prevNode = get(index - 1);
insertedNode.next = prevNode.next;
prevNode.next = insertedNode;
}
size++;
}
如果新的节点信息赋值给last对象,到最后会不会已有一个last对象,此处疑惑了很久,只好使用debug查看地址的引用情况
之后每一次debug都去看下head的变化
看图会发现,当size=0的时候创建了两个对象,一个是head用来确定头结点,一个操作对象last用来后续操作的,
在last =insertedNode 赋值以后会发现原本head的地址是533,last的地址是535, 此时插入的是第二个值,head的next的引用地址指向了 下一个节点对象,
此时插入了第三个结点对象可以发现 next的地址随之发生了改变,通过观察head消除了疑惑