1.链式存储结构
顺序存储结构要求所有元素依次存放在一片连续的存储空间中,而链式存储结构,无需占用一整块存储空间。但为表示结点之间的关系,需要给每个结点附加指针字段,用于存放后继元素的存储地址。
仍然使用我们在学习顺序表时使用的ElemType作为数据元素。建一个新的LinkNode类作为结点,结点包含一个ElemType作为数据域,附加一个LinkNode作为指针域,用于存放后继结点的地址。
ElemType封装类的代码见 顺序表的Java实现
LinkNode封装类的代码如下
public class LinkNode{
private ElemType e;
private LinkNode next;
public LinkNode(){}//无参构造方法
public LinkNode(ElemType e){//以数据为参数构造结点
this.e=e;
}
public ElemType getE() {
return e;
}
public void setE(ElemType e) {
this.e = e;
}
public LinkNode getNext() {
return next;
}
public void setNext(LinkNode next) {
this.next = next;
}
}
链式存储结构如图:
2.链表的构造
和顺序表一样,我们将链表封装为一个类,这个类同样实现了我们在 顺序表的Java实现 中定义的抽象数据类型(ADT)接口。
在顺序表中,定义一个ELemType类型的数组来存放数据,通过有参数的构造方法来创建固定长度的数组;
而在链表中,只需要定义一个LinkNode结点,用这个结点来作为我们存储和操作的对象,new一个链表,就是初始化一个头结点。
public class LinkList implements MyList {
private LinkNode linkList;
public LinkList(){
linkList=new LinkNode();
linkList.setNext(null);
}
/*链表的相关方法见下文*/
}
3.链表的清空
链表的头结点携带者链表的全部信息,因此判断链表是否为空,或者将链表清空都只需要访问或者操作头结点。
@Override
public void clear() {
linkList.setNext(null);
}
@Override
public boolean isEmpty() {
return linkList.getNext()==null;
}
4.链表的长度
获得链表的长度就是要从头结点开始不断访问下一个结点,直到结点为null。
1.用p=头结点;i=0
2.p=p.getNext();i++
3.不断重复2,直到p==null;
4.返回i.
@Override
public int listLength() {
LinkNode p=linkList;
int i;
for(i=0;p.getNext()!=null;i++){
p=p.getNext();
}
return i;
}
5.链表的访问、遍历与查询
访问
1.用p=头结点;j=0
2.p=p.getNext();j++
3.不断重复2,直到j==i;判读i是否合理(写的时候忘了判断和抛出异常)
4.返回p.getE().
遍历
1.用p=头结点;
2.p=p.getNext(); 打印数据域p.getE();
3.不断重复2,直到p.getNext()=null
查询
1.用p=头结点;
2.p=p.getNext();
3.不断重复2,直到p.getE().equal(x);判读i是否合理(忘了判断)
4.返回p.getE().
@Override
public ElemType getElem(int i) throws Exception {
LinkNode p=linkList;
int j;
for(j=0;j<i||p!=null;j++){
p=p.getNext();
}
return p.getE();
}
@Override
public void display() {
LinkNode p=linkList.getNext();
System.out.print(p.getE().toString());
p=p.getNext();
while(p!=null){
System.out.print(" -> ");
System.out.print(p.getE().toString());
p=p.getNext();
}
System.out.println();
}
@Override
public int getIndex(ElemType x) throws Exception {
LinkNode p=linkList;
for(int i=0;p!=null;i++){
p=p.getNext();
if (p.getE().toString().equals(x.toString())){
return i;
}
}
return 0;
}
5.链表的插入与删除
插入
1.新建p=头结点; 建立以参数x为数据域的新结点xNode; j=0;
2.p=p.getNext();j++
3.不断重复2,直到j==i;判读是否越界,是则抛出异常
4.xNode的指针域置位p.getNext();
5.p的指针域置位xNode
删除
1.新建p=头结点 ; j=0;
2.p=p.getNext();j++
3.不断重复2,直到j==i;判读是否越界,是则抛出异常
4.新建q=p.getNext();
5.把p的next设置为q.getNext().
@Override
public void insert(int i, ElemType x) throws Exception {
LinkNode p=linkList;
LinkNode xNode=new LinkNode(x);
for(int j=0;j<i;j++){
if (i<0||p.getNext()==null){
throw new Exception("越界了,无法插入");
}
p=p.getNext();
}
xNode.setNext(p.getNext());
p.setNext(xNode);
}
@Override
public void remove(int i) throws Exception {
LinkNode p=linkList;
for(int j=0;j<i;j++){
if (i<0||p.getNext()==null){
throw new Exception("越界了,无法删除");
}
p=p.getNext();
}
LinkNode q=p.getNext();
p.setNext(q.getNext());
}
6.链表的测试
和顺序表一样,我们创建一个测试的类来完成测试,结果如下:
测试代码:
public class ListTest {
public static void main(String[] args){
ElemType elem1=new ElemType("王",1);
ElemType elem2=new ElemType("张",2);
ElemType elem3=new ElemType("李",3);
ElemType elem4=new ElemType("李",4);
ElemType elem5=new ElemType("赵",5);
System.out.println();
System.out.println("/---创建链表:---/");
LinkList linkList=new LinkList();
System.out.println("是否为空:"+linkList.isEmpty());
System.out.println();
System.out.println("/---插入操作:---/");
try {
linkList.insert(0,elem1);
linkList.insert(1,elem2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("输出链表:");
linkList.display();
System.out.println("链表长度:"+linkList.listLength());
System.out.println();
System.out.println("/---查找索引:---/");
try {
System.out.println(elem2.toString()+"的下标为:"+linkList.getIndex(elem2));
} catch (Exception e1) {
e1.printStackTrace();
}
System.out.println();
System.out.println("/---删除操作:---/");
try {
linkList.remove(0);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("输出链表:");
linkList.display();
System.out.println("链表长度:"+linkList.listLength());
}
}