上一篇讲述了顺序表,顺序表总的来说有以下特点:
- 在线性表中逻辑上相邻的数据元素,在物理存储位置上也是相邻的;
- 存储密度高,但要预先分配“足够应用”的存储空间,这可能将会造成存储空间的浪费,其中存储密度= 数 据 元 素 本 身 值 所 需 的 存 储 空 间 该 数 据 元 素 实 际 所 占 用 的 空 间 \frac{数据元素本身值所需的存储空间}{该数据元素实际所占用的空间} 该数据元素实际所占用的空间数据元素本身值所需的存储空间
- 便于随机存取
- 不便于插入和删除操作,这是因为在顺序表上进行的插入和删除操作会引起大量数据元素的移动
顺序存储虽然是一种很有用的存储结构,但是有如下局限性: - 若要为线性表扩充存储空间,则需要重新创建一个地址连续的更大的存储空间,并把原有的数据元素都复制到新的存储空间(可参考List实现源码)
- 因为顺序存储要求逻辑上相邻的数据元素在物理存储位置上也要相邻,这就使得增删操作会引起平均约一半的数据元素的移动
所以顺序表最适合表示“静态”线性表,即线性表一旦生成,就很少进行插入与删除操作,对于需要频繁插入和删除的“动态”线性表,通常采用的是链式存储结构(链表)
单链表
链表中每一个结点包含存放数据元素值的数据域和存放指向逻辑上相邻的指针域。若一个结点中只包含一个指针域,则称此链表为单链表。
一个单链表由头指针head来唯一标识它
单链表中的最后一个结点(尾结点)没有 后继,所以指针域的值为NULL值
有时为了操作方便,在第一个结点之前虚加一个头结点,头结点的数据域一半不存放具体的值,指针域存放指向第一个结点(首结点)的指针
结点对象用java语言描述如下:
public class Node {
private Object data;
private Node next;
public Node() {
this(null,null);
}
public Node(Object data) {
this(data,null);
}
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
单链表类:
public class LinkList implements IList {
private Node head;
public LinkList() {
head = new Node();
}
public LinkList(int n, boolean order) throws Exception {
this();
if (order)// 两种创建方式,尾插法
create1(n);
else
create2(n);
}
private void create1(int n) throws Exception {
Scanner sc = new Scanner(System.in);
for (int i = 0; i < n; i++) {
insert(0, sc.next());
}
}
private void create2(int n) throws Exception {
Scanner sc = new Scanner(System.in);
for (int i = 0; i < n; i++) {
insert(length(), sc.next());
}
}
@Override
public void clear() {
head.setData(null);
head.setNext(null);
}
@Override
public boolean isEmpty() {
return head.getNext() == null;
}
@Override
public int length() {
Node p = head.getNext();
int length = 0;
while (p != null) {
p = head.getNext();
++length;
}
return length;
}
@Override
public Object get(int i) throws Exception {
Node p = head.getNext();
int j = 0;
while (p != null && j < i) {
p = p.getNext();
++j;
}
if (j > i || p == null) {
throw new Exception("元素不存在");
}
return p.getData();
}
@Override
public void insert(int index, Object object) throws Exception {
Node p = head;
int j = -1;
while (p != null && j < index - 1) {
p = p.getNext();
++j;
}
if (j > index - 1 || p == null) {
throw new Exception("插入位置不合法");
}
Node s = new Node(object);
// s.setNext(head);
// head = s;
s.setNext(p);
p.setNext(s);
}
@Override
public void remove(int index) throws Exception {
Node p = head;
int j = -1;
while (p.getNext() != null && j < index - 1) {
p = p.getNext();
++j;
}
if (j > index - 1 || p.getNext() == null) {
throw new Exception("刪除位置不合法");
}
p.setNext(p.getNext().getNext());
}
@Override
public int indexOf(Object object) {
Node p = head.getNext();
int j = 0;
while (p != null && !p.getData().equals(object)) {
p = p.getNext();
++j;
}
if (p != null)
return j;
return -1;
}
@Override
public void display() {
Node node = head.getNext();
while (node != null) {
System.out.print(node.getData() + " ");
node = node.getNext();
}
System.out.println();
}
public static void main(String[] args) throws Exception {
int n = 10;
LinkList l = new LinkList();
// l.insert(0, 0);
// l.insert(1, 1);
for (int i = 0; i < n; i++) {
l.insert(i, i);
}
System.out.println("请输入i的值:");
int i = new Scanner(System.in).nextInt();
if (0 < i && i <= n)
System.out.println("第" + i + "个元素的直接前驱是" + l.get(i - 1));
else
System.out.println("第" + i + "个元素的直接前驱不存在");
}
}
并且简单的测试了一下:
告辞!