注:定义来源于
《数据结构(C++版)(王红梅/胡明/王涛 编著)》 第2版。
前言
线性表是一种最基本、最简单的数据结构,数据元素之间仅具有单一的前驱和后继关系。
1.定义
1.1 线性表的定义
线性表(linear list)简称表,是n(n>=0)个具有相同类型的数据元素的有限序列,线性表中数据元素的个数称为线性表的长度。
1.2 线性表的抽象数据类型定义
线性表是一个相当灵活的数据结构对线性表的数据元素不仅可以进行存取和访问,还可以进行插入和删除操作。
2. 线性表的顺序存储结构—顺序表
顺序表是用一段地址连续的存储单元依次存储线性表的数据元素。
顺序表的缺点:
- 插入和删除操作需要移动大量元素。
- 表的容量难以确定。
- 造成存储空间碎片。
造成顺序表的上述缺点的根本原因是静态存储分配,为了克服顺序表的缺点,可以采用动态存储分配来存储线性表,也就是采用链式存储结构。静态存储分配是指在编译时为变量分配内存,并且一经分配就始终占有固定的存储单元,直到该变量退出其作用域。动态存储分配是指程序运行期间根据实际需要随时申请内存,并在不需要的时候释放。
3. 线性表的链式存储结构
3.1 单链表
3.1.1 单链表的存储方法
单链表(singly linked list)是用一组任意的存储单元存放线性表的元素,这组存储单元可以连续也可以不连续,甚至可以零散的分布在内存中的任意位置。
3.1.2 代码
public class Node {
//节点内容
int data;
//下一个节点
Node next;
public Node(int data) {
this.data = data;
}
//追加节点
public Node append(Node node) {
//当前节点
Node currentNode = this;
//循环找节点
while (true) {
//取出下一个节点
Node nextNode = currentNode.next;
//如果下一个节点为null,表示当前节点为最后一个节点
if (nextNode == null) {
break;
}
//赋值给当前节点
currentNode = nextNode;
}
//把需要追加的节点放在当前节点的下面
currentNode.next = node;
return this;
}
public Node next() {
return next;
}
public int getData() {
return data;
}
}
test
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
node1.append(node2).append(node3);
System.out.println(node1.next().getData());
输出结果:2
3.1.3 删除单链表中的节点
有点绕,单链表不能拿到本节点的上一个节点,所以只能删除本节点的下一个节点。
public void removeNext(){
Node newNext = next().next();
this.next = newNext;
}
3.1.4 插入一个节点
当然,出入节点的位置在当前节点的下一个节点。
public void insert(Node node){
Node nextNote = next();
this.next = node;
node.next = nextNote;
}
3.2 循环链表
和单链表区别不大,区别就是比单链表多了首尾相连。头尾相接的单链表称为循环单链表,简称循环链表。循环链表中没有明显的尾端,容易进入死循环,需要额外注意循环条件。
public class LoopNode {
//节点内容
int data;
//下一个节点,和单链表差别在这里,没append(Node node) 追加节点这个方法,好好想想为啥
LoopNode next = this;//猪油一个节点的时候,next是他自己
public LoopNode(int data) {
this.data = data;
}
public LoopNode next() {
return next;
}
public int getData() {
return data;
}
public void removeNext() {
LoopNode newNext = next().next();
this.next = newNext;
}
//插入一个节点,
public void insert(LoopNode node) {
LoopNode nextNote = next();
this.next = node;
node.next = nextNote;
}
}
3.3 双链表
在循环链表中,虽然从人一个节点出发可以扫描到其他节点,单要找到其前驱结点,需要需要遍历整个循环链表。如果需要快速准确定位表中任一节点的前驱结点,可以在单链表的每个节点中在设置一个指向其前驱结点的指针域,这样就形成了双链表(doubly linked list)。
感觉这个不需要写啥了,和循环链表去的区别就是多了一个上一个节点,查找下一个节点,查找上个节点。读者自己类比写吧。懒得写了。
public class DoubleNode {
DoubleNode pre = this;
DoubleNode next = this;
int data;//节点数据
public DoubleNode(int data) {
this.data = data;
}
}
3.4 静态链表
静态链表(static linked list)是用数组来表示单链表,用数组元素的下标来模拟单链表的指针。静态链表的每个数组元素有两部分组成:data域存放数据元素,next域存放该元素的后继元素所在的数组下标。由于它是利用数组定义的,属于静态存储分配,因此叫做静态链表。
4.顺序表和链表的比较
4.1 时间性能比较
时间复杂度
4.2 空间性能比较
存储密度。
5 . 递归算法&&斐波那契数列
5.1 递归
说白了,就是自己调用自己,直到满足一个条件跳出循环。
public static void main(String[] args) {
recursive(20);
}
private static void recursive(int i) {
if (i>0){//满足条件,跳出循环,否则会...你猜?
System.out.println(i);
recursive(--i);//调用自己
}
}
5.2 斐波那契数列
经典的,斐波那契数列。
private static void getFibo(int i) {
for (int j = 1; j <= 20; j++) {
System.out.print(Fibo(j) + "\t");
}
}
private static int Fibo(int i) {
if (i == 1 || i == 2) {
return 1;
} else {
return Fibo(i - 1) + Fibo(i - 2);
}
}
输出
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765