1 定义
线性表是由同一类型数据元素构成的有序序列的线性结构。
线性表中元素的个数被称为线性表的长度;
当一个线性表中没有元素时,称为空表;
表的起始位置称为表头,结束位置称为表尾;
线性表的抽象数据类型描述为:
类型名称:线性表(List)
数据对象集:线性表是n(n ≥ 0)个元素构成的有序序列(a1,a2,...,an),其中a1是表的第一个元素,表头。an是表的最后一个元素,表尾。
操作集:对于一个具体的线性表 L ∈ List,一个表示位序的整数i,一个元素X∈ElementType,线性表的基本操作有:
(1) List MakeEmpty():初始化一个新的空线性表;
(2) ElementType FindKth(List L, int i):根据指定位序i返回L中相应元素ai
(3) Position Find(List L, ElementType X):已知X,返回线性表L中与X元素相同的第一个元素的位置;若不存在返回错误信息;
(4) bool Insert(List L, ElementType X, int i):在L的指定位序i前插入一个新元素X;成功则返回true,否则返回false;
(5) bool Delete(List L, int i):从L中删除指定位序i的元素;成功返回true,失败返回false;
(6) int Length(List L):返回线性表L的长度。
2 线性表的顺序存储实现
考虑到线性表的运算有插人、删除等,即表的长度是动态可变的,因此,数组的容量需设计得足够大。
假设用Data[ MAXSIZE]来表示,其中MAXSIZE是一个根据实际问题定义的足够大的整数,
线性表中的数据从Data[0]开始依次顺序存放。由于当前线性表中的实际元素个数可能未达到MAXSIZE多个,
因此需用一个变量Last记录当前线性表中最后一个元素在数组中的位置,即Last 起一个指针(实际是数组下标)的作用
,始终指向线性表中最后一个元素。表空时Last=-1。
Java实现
/**
-
线性表的顺序存储实现 */ public class LinList01 { public static void main(String[] args) { System.out.println("LinList01 -> staring..."); Test(); System.out.println("LinList01 -> end"); }
public static void Test() { LNode l = new LNode(); l.insert(1, 11); l.insert(2, 12); l.insert(3, 13); l.insert(4, 14); l.insert(5, 15); l.insert(18, 16); int i = 0; while (i <= l.get_last()) System.out.print(String.format("%d,", l.get_data()[i++]));
System.out.println(); System.out.println(l.findKth(5)); System.out.println(l.findKth(6)); System.out.println(l.find(32)); System.out.println(l.getLength()); l.delete(4); System.out.println(l.getLength()); i = 0; while (i <= l.get_last()) System.out.print(String.format("%d,", l.get_data()[i++])); System.out.println();} }
/**
-
线性表的顺序存储实现 */ class LNode { private static final int MAX_SIZE = 100; private static final int ERROR = -1; private int[] _data;
public int[] get_data() { return _data; }
private int _last;
public int get_last() { return _last; }
/**
- 初始化 */ public LNode() { _data = new int[MAX_SIZE]; _last = -1; }
/**
- 根据指定位序返回线性表中相应元素
- @param i 元素位序从1开始 */ public int findKth(int i) { if (i < 1 || i > this._last + 1) { System.out.println(String.format("位序%d不存在", i)); return ERROR; } return this._data[i - 1]; }
/**
-
在线性表查找元素
-
@param x 需要查找的元素 */ public int find(int x) { int i = 0; while (i <= _last && _data[i] != x) i++;
if (i > _last) return ERROR; else return i; }
/**
-
在线性表指定位置插入元素
-
@param i 插入位序从1开始
-
@param x 插入元素 */ public boolean insert(int i, int x) { int j; if (this._last == MAX_SIZE - 1) { System.out.println("表满"); return false;
} // 检查插入的位序是否合法 if (i < 1 || i > this._last + 2) { System.out.println("位序不合法"); return false; } // 位序i及以后的元素顺序向后移 for (j = this._last; j >= i - 1; j--) { this._data[j + 1] = this._data[j]; } // 新元素插入第i位序 this._data[i - 1] = x; // last仍指向最后元素 this._last++;
return true; }
/**
-
删除指定位序元素
-
@param i 删除位序从1开始 */ public boolean delete(int i) { int j; // 检查位序是否越界 if (i < 1 || i > this._last + 1) { System.out.println(String.format("位序%d不存在", i)); return false; }
// 将位序i+1后面的元素顺序向前移,覆盖掉需要删除的位序i for (j = i - 1; j <= this._last; j++) this._data[j] = this._data[j + 1]; // last仍指向最后元素 this._last--;
return true; }
/**
- 返回线性表长度 */ public int getLength() { return this._last + 1; }
}
3 线性表的链式存储实现
由于顺序表的存储特点是用物理上的相邻实现了逻辑上的相邻,它要求用连续的存储单元顺序存储线性表中各元素,
因此,对顺序表插入、删除时需要通过移动数据元素来实现,影响了运行效率。
本节介绍线性表链式存储结构,它不需要用地址连续的存储单元来实现,因为它不要求,它是通过“链”建立起数据元素之间的逻辑关系
,因此对线性表的插入、删除不需要移动数据元素,只需要修改“链"。
用链表结构可以克服数组表示线性表的缺陷。
下图为单向链表的图示表示形式,它有n个数据单元,每个数据单元由数据域和链接域两部分组成。
数据域用来存放数值.图中用a1,a2,...,an表示。链接域是线性表数据单元的结构指针,用一带箭头的线段表示
,线性表的顺序是用各结点上指针构成的指针链实现的。
为了访问链表,必须先找到链表的第一个数据单元,$\color{red}{因此实际应用中常用一个称为“表头(Header)”的指针指向链表的第一个单元,
并用他表示一个具体的链表}$。
3.1 插入
设我们要在线性表的位序为i(从1开始)的位置插入一个元素值为x。
由于链表的特性是由一个指针链来管理元素间的逻辑关系,那么当我们在线性表新增一个元素时我们需要知道这个元素的
前一个元素称为Prev和后一个元素称为Next就可以轻松在他们之间插入一个元素了。
如下图所示:
注意这里有一种特殊情况当我们插入的位序位置为1时,将新元素的Next设置为头指针,再将头指针指向新元素即可(要确保头指针始终指向链表的第一个节点位置)
3.2 删除
设我们要删除线性表的位序为i(从1开始)的元素值,此时我们只需要找到位序为i-1的元素Prev,修改指向关系为i+1的元素即可。
如下图所示:
注意这里有一种特殊情况当我们删除的位序位置为1时,仅需要将头指针指向位序为2的元素即可。
3.3 实现
Java实现
/**
-
线性表的链式存储实现 */ public class LinList02 { public static void main(String[] args) { System.out.println("LinList02 -> staring..."); Test(); System.out.println("LinList02 -> end"); }
public static void Test() { PNodeList l = new PNodeList(); l.insert(1, 11); l.insert(2, 12); l.insert(3, 13); l.insert(4, 14); l.insert(5, 15); l.insert(18, 16);
PNode tmp = l.get_head(); while (tmp != null) { System.out.print(String.format("%d,", tmp.get_data())); tmp = tmp.get_next(); } System.out.println(); System.out.println(l.findKth(5)); System.out.println(l.findKth(6)); System.out.println(l.find(32)); l.delete(1); l.delete(3); System.out.println(l.getLength()); tmp = l.get_head(); while (tmp != null) { System.out.print(String.format("%d,", tmp.get_data())); tmp = tmp.get_next(); } System.out.println();} }
class PNodeList { private static final int ERROR = -1; private PNode _head;
public PNode get_head() {
return _head;
}
public PNodeList() {
_head = null;
}
/**
* 查找指定位序元素
*
* @param k 位序从1开始
*/
public int findKth(int k) {
PNode p = _head;
int counter = 1;
while (p != null && counter < k) {
p = p.get_next();
counter++;
}
if (counter == k && p != null)
return p.get_data();
else
return ERROR;
}
/**
* 查找指定元素
*
* @param x 需要查找的元素
*/
public PNode find(int x) {
PNode p = _head;
while (p != null && p.get_data() != x)
p = p.get_next();
return p;
}
/**
* 在指定位序插入元素
*
* @param i 位序,从1开始
* @param x 需要插入的元素
*/
public boolean insert(int i, int x) {
PNode newNode;
PNode prev;
// 生成新节点
newNode = new PNode();
newNode.set_data(x);
// 插入位序为表头处理
if (i == 1) {
newNode.set_next(_head);
_head = newNode;
return true;
}
// 查找插入位序前一个元素 i-1
prev = FindPrev(i);
if (prev == null) return false;
// 插入元素
newNode.set_next(prev.get_next());
prev.set_next(newNode);
return true;
}
private PNode FindPrev(int i) {
PNode prev = _head;
int counter = 1;
while (counter < i - 1 && prev != null) {
prev = prev.get_next();
counter++;
}
if (prev == null || counter != i - 1) {
System.out.println("插入位序不合法");
return null;
}
return prev;
}
/**
* 删除指定位序节点
*
* @param i 位序
*/
public boolean delete(int i) {
PNode tmp;
PNode prev;
// 删除的位序为1
if (i == 1) {
tmp = _head;
_head = tmp.get_next();
tmp.set_next(null);
return true;
}
// 查找Prev元素
// 查找插入位序前一个元素 i-1
prev = FindPrev(i);
if (prev == null) return false;
// 删除元素
tmp = prev.get_next();
prev.set_next(tmp.get_next());
tmp.set_next(null);
return true;
}
public int getLength() {
PNode p = _head;
int counter = 0;
while (p != null) {
counter++;
p = p.get_next();
}
return counter;
}
}
/**
-
线性表链式存储实现 */ class PNode {
private int _data; private PNode _next;
public int get_data() { return _data; }
public PNode get_next() { return _next; }
public void set_data(int _data) { this._data = _data; }
public void set_next(PNode _next) { this._next = _next; } }
4. 链式存储和顺序存储比较
1、顺序存储实现简单,对元素查找速度快,单对于增删来说效率较低且表长固定,适合静态管理的数据。
2、链式存储实现,增删元素效率很高,查找效率偏低,适合频繁增删节点,且表长有较大变化的情况。