线性表的链式表示
线性表的链式表示的存储单元不再是连续的数组,而是一组非连续的空间。
这就导致了,对于这组空间,你无法像顺序表一样轻易的遍历整个空间,你也不可能申请一个数组记录每一个数值的地址,那样本质就和线性表是相同的了,那么解决的方法就是,在每一个数据的结尾加上下一个数据的地址,共同构成一种新的数据类型:结点(Node)。分为数据域和指针域。
下面是对于线性表中的结点的一个泛型定义:
public class Node<T> {
T data; //数据域,存储自己想要的数据类型
Node<T> next; //指针域,指向下一个的结点
public Node(T obj, Node<T> n) { //初始化本结点,并存入数据
data = obj;
next = n;
}
public Node(Node<T> n) { //初始化本结点,但并不存入数据
next = n;
}
public T getData() { //获取当前元素的信息
return data;
}
public void setData(T obj) { //设置当前元素内容
data = obj;
}
public Node<T> getNext(){ //获取下一个结点的信息
return next;
}
}
单链表的具体实现
定义如下,在linklist中有两个数据变量,一个是指向表头元素的头节点,一个是length表示长度。类中的方法和在顺序表中的方法是一致的。
public class linkList<T> {
Node<T> head; //头结点不存储任何数据元素
int length;
public linkList() { //初始化链表,也就是初始化头结点,,传入一个null,此时head数据域为空,指针域为null
length = 0;
head = new Node<T>(null);
}
public Node<T> getHead(){
return head;
}
public boolean add(T obj, int pos) {
if(pos <= 0 || pos > length+1) {
System.out.println("位置有误。");
return false;
}
int num = 1; //在设计链表中,p是当前结点的上一个结点的地址,而q则是当前结点的地址(如果当前结点为空的话,q也为空)
Node<T> p = head, q = head.getNext();
while(num < pos) {
p = q;
q = q.getNext();
num++;
}
p.setNext(new Node<T>(obj, q));
length++;
System.out.println("已经添加数据" + obj);
return true;
}
public T remove(int pos) {
if(pos <= 0 || pos > length) {
System.out.println("删除时位置有误,失败.");
return null;
}
int num = 1;
Node<T> p = head, q = head.getNext();
while(num < pos) {
p = q;
q = q.getNext();
num++;
}
T t = q.getData();
length--;
p.setNext(q.getNext());
return t;
}
public T value(int pos) {
if(pos <= 0 || pos > length) {
System.out.println("获取元素时输入的位置有误。");
return null;
}
int num = 1;
Node<T> p = head, q = head.getNext();
while(num < pos) {
p = q;
q = q.getNext();
num++;
}
return q.getData();
}
public int find(T obj) {
int num = 1;
Node<T> p = head.getNext();
while( p != null) {
if(p.getData().equals(obj))
return num;
else {
p = p.getNext();
num++;
}
}
return -1;
}
public boolean modify(T obj, int pos) {
if(pos <= 0 || pos > length) {
System.out.println("在更新链表时数据位置信息错误");
return false;
}
int num = 1;
Node<T> q = head.getNext();
while(num < pos) {
q = q.getNext();
num++;
}
q.setData(obj);
return true;
}
public boolean isEmpty() {
return length == 0;
}
public void nextOrder() {
System.out.print("该链表的数据包含:");
Node<T> p = head;
while(p.getNext() != null) {
p = p.getNext();
System.out.print(p.getData() + " ");
}
System.out.println();
}
public void clear() {
length = 0;
head.setNext(null);
}
}
在设计中,注意设计好add方法还有remove方法,方法的具体意义同线性表中的意义。
对于上述方法,需要一个测试函数。
public class TestLinkList {
public static void main(String[] args) {
// TODO Auto-generated method stub
linkList<Integer> ll = new linkList<Integer>();
int[] array = new int[]{1,2,3,4,5};
for(int i = 0; i < array.length; i++) {
ll.add(array[i], i+1);
}
ll.nextOrder();
ll.remove(3);
ll.nextOrder();
ll.modify(3, 3);
ll.nextOrder();
boolean bool = ll.isEmpty();
System.out.println("ll为空:" + bool);
int length = ll.length;
System.out.println("链表的长度为:" + length);
}
}
测试函数分别测试了添加,删除,更新,打印等功能。
具体实现结果如下:
已经添加数据1
已经添加数据2
已经添加数据3
已经添加数据4
已经添加数据5
该链表的数据包含:1 2 3 4 5
该链表的数据包含:1 2 4 5
该链表的数据包含:1 2 3 5
ll为空:false
链表的长度为:4
总结:
链表的设计首先是结点的引入,两种构造方法,一种是给head的构造,一种是一般构造(包含数据)
链表中数据域中仅包含长度以及head,而不是存储一系列具体的数据,这与顺序表是一种巨大的不同,他用方法,通过head而完整呈现一整个链表。
通过头指针的方法,更方便去寻找每一个pos的位置,去进行插入与删除。