3.1 线性表的定义
线性表(List):零个或多个数据元素的有限序列。(有顺序的,一个接一个的,有限的)
数学语言定义克表示为:
若将线性表记为(
a1,a2,…,ai−1,ai,ai+1,…,an
),则表中
ai−1
领先于
ai
,
ai
领先于
ai+1
,称
ai−1
是
ai
的直接前驱元素,
ai+1
是
ai
的直接后驱元素。当
i=1,2,…,n−1
时,
ai
有且仅有一个直接后继,
i=2,3,…,n
时,
ai
有且仅有一个直接前驱。
所以线性表元素的个数
n(n≥0)
定义为线性表的长度,当
n=0
时,成为空表。
在比较复杂的线性表中,一个数据元素可以由若干个数据项组成。
3.2 线性表的抽象数据类型
ADT 线性表(List)
Data
线性表的数据对象集合为{ a1,a2,…,an } ,每个元素的类型均为DataType。其中,除第一个元素 a1 外,每一个元素有且只有一个直接前驱元素,除了最后一个元素 an 外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
InitList(L); 初始化操作,建立一个空的线性表L。。
ListEmpty(L); 若线性表为空,返回true,否则返回false。
ClearList(L);
3.3 线性表的链式存储结构
3.3.1 定义
n个结点(
ai
的存储影像)链结成一个链表,即为线性表({
a1,a2,…,an
})的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。
链表中的第一个结点的存储位置叫做头指针。最后一个结点的指针为“空”(通常用NULL或“^”表示)。
有时会在单链表的第一个结点前附设一个结点,称为头结点。可以不存任何信息,也可以存储如线性表的长度等附加信息。
3.3.2 头指针和头结点的异同
头指针:
- 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针
- 头指针具有标识作用,所以常用头指针冠以链表的名字
- 无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
头结点:
- 头结点是为了操作的统一和方便而设立的,放在第一元素的节点之前,其数据域一般无意义(也可以存放链表的长度)。
- 有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它节点的操作就统一了。
- 头结点不一定是链表必须要素。
3.3.3 线性表链式存储结构代码描述
结点由存放数据元素的数据域存放后继结点地址的指针域组成。
class Node <T>{
private T data;
private Node<T> next;
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
public Node<T> getNext(){
return next;
}
public void setNext(Node<T> next){
this.next = next;
}
}
单链表的读取
获得链表第i个数据的算法思路:
① 生声明一个结点p指向链表第一个结点,初始化j从1开始;
② 当
j<i
时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1;
③ 若到链表末尾p为空,则说第i个元素不存在;
④ 否则查找成功,返回节点p的数据。
public T getElem(int loc){
int j = 1;
Node<T> n= head;
while (n !=null){
if(j == loc){
return n.getData();
}
n = n.getNext();
j++;
}
return null;
}
3.3.4 单链表的插入与删除
单链表的插入:
让p的后继结点改成s的后继结点,再把结点s变成p的后继结点。
但要注意考虑一下表头和表尾的特殊情况:
单链表的第i个数据插入结点的算法思路:
① 声明一结点p指向链表第一个结点,初始化j从1开始
② 当 j<i 时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
③ 若到链表末尾p为空,则说明第i个元素不存在
④ 否则查找成功,在系统中生成一个空节点s
⑤ 将数据元素e赋值给s的data
⑥ 单链表的插入标准语句
⑦ 返回成功单链表的删除:
把p的后继结点改成p的后继的后继结点。
单链表第i个数据删除结点的算法思路:
① 声明一结点p指向链表第一个结点,初始化j从1开始;
② 当 j<i 时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
③ 若到链表末尾p为空,则说明第i个元素不存在
④ 否则查找成功,将欲删除的结点p.next赋值给q
⑤ 单链表的删除标准语句
⑥ 将q结点中的数据赋值给e,作为返回
⑦ 释放q结点
⑧ 返回成功
public class LinkList{
ListNode head;
int length;
public LinkList(){
this.head = null;
}
public static void main(String[] args){
LinkList linkList = new LinkList();
linkList.insertElem(1, 1);
linkList.insertElem(1, 2);
linkList.insertElem(1, 3);
linkList.deleteElem(2);
linkList.displayNodes();
linkList.insertElem(2, 2);
linkList.displayNodes();
}
public boolean insertElem(int i, int data){
if (length+1 < i){
System.out.println("非法插入");
return false;
}
if(head == null & i == 1){//链表为空
head = new ListNode(data);
length++;
} else if (head != null && i == 1){//插入头结点
ListNode tempNode = new ListNode(data);
tempNode.setNext(head);
head = tempNode;
length++;
} else {
ListNode current = head;
int j=1;
while(current != null && j< i-1){
current = current.getNext();
j++;
}
ListNode nodeInsert = new ListNode(data);
nodeInsert.setNext(current.getNext());
current.setNext(nodeInsert);
length++;
}
return true;
}
public int deleteElem(int i){
if (head == null || i > length){
System.out.println("非法删除");
return -1;
}
int old = 0;
if (head != null && i == 1){
old = head.getValue();
head = head.getNext();
} else {
ListNode current = this.head;
int j = 1;
while (current != null && j< i-1){
current = current.getNext();
j++;
}
old = current.getNext().getValue();
current.setNext(current.getNext().getNext());
}
length--;
return old;
}
public void displayNodes(){
ListNode current = head;
while (current != null){
current.display();
current = current.getNext();
}
System.out.println();
}
}
class ListNode{
private int value;
private ListNode next;
public ListNode(int value){
this.value = value;
}
public ListNode(int value, ListNode next){
this.value = value;
this.next = next;
}
public void setValue(int value){
this.value = value;
}
public void setNext(ListNode next){
this.next = next;
}
public int getValue(){
return this.value;
}
public ListNode getNext(){
return this.next;
}
public void display(){
System.out.print(value + " ");
}
}
分组成:第一部分就是遍历查找第i个元素;第二部分就是插入和删除元素。
3.4 静态链表
用数组描述的链表叫做静态链表,也叫游标实现法,包含data和cur两个数据域,cur相当于链表的next指针叫游标。
数组的第一个和最后一个作为特殊元素处理,不存数据。第一个元素(下标为0)的cur存放备用链表(未被使用的数组元素)的第一个节点的下标,数组的最后一个元素的cur则存放第一个有数值的元素的下表,相当于单链表中的头结点作用。
假设静态链表中存放着“甲”“乙”“丁”“戊”“己”“庚”“辛”等数据:
3.4.1 静态链表的插入
3.4.2 静态链表的删除
程序:
public class StaticList<T> {
private Node<T>[] data;
private static int MAXSIZE = 1000;
public StaticList(){
data = new Node[MAXSIZE];
initList(data);
}
public StaticList(int maxsize){
data = new Node[maxsize];
initList(data);
}
private void initList(Node<T>[] data) {
for (int i=0;i<data.length-1;i++){
data[i]= new Node<T> (null,i+1);
}
data[data.length-1] = new Node<T>(null,0);
}
public boolean add(T item){
int newCur = data[0].cur;
int temp = data[newCur].cur;
Node<T> newNode = new Node<T>(item,0);
data[newCur] = newNode;
if (data[0].cur>1){
data[newCur-1].cur = newCur;
}
data[0].cur = temp;
data[data.length-1].cur=1;
return true;
}
public boolean insert(T item, int index){
int loopCur = data[data.length-1].cur;
for(int i=0;i<index-1;i++){
loopCur = data[loopCur].cur;
}
int nextCur = data[loopCur].cur;
data[loopCur].cur = data[0].cur;
int newCur = data[0].cur;
Node<T> newNode = new Node<T>(item,nextCur);
data[newCur] = newNode;
data[0].cur++;
return true;
}
public String toString(){
String str = "[";
int loopCur = data[data.length-1].cur;
while (data[loopCur].value != null){
str = str + data[loopCur].value+" ";
loopCur = data[loopCur].cur;
}
str = str+"]";
return str;
}
public static void main(String[] args) {
StaticList <String> staticList1 = new StaticList <String>(6);
staticList1.add("元素1");
staticList1.add("元素2");
staticList1.add("元素4");
System.out.println(staticList1);
staticList1.insert("元素3",2);
System.out.println(staticList1);
}
class Node<T>{
private T value;
private int cur;
public Node(T data, int cur){
this.value = data;
this.cur = cur;
}
}
}
3.5循环链表
将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称为循环链表(circular linked list)。可以解决从中间任何一个结点出发就可以访问到链表的全部结点的问题。
3.6 双向链表
双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。