2、链表
1、简介
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s6E6tI1q-1608815080744)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201215205226359.png)]
2、创建结点类
package linkedList;
/**
* @author Administrator
* 结点类
*/
public class Node {
//1、定义数据域,存放结点数据值
public Object data;
//2、定义指针域,存放后继结点
public Node next;
//无参构造函数
public Node() {
}
//有参构造函数
public Node(Object data, Node next) {
super();
this.data = data;
this.next = next;
}
}
3、创建单链表
package linkedList;
import java.util.Scanner;
import sqList.IList;
/**
* @author Administrator 单链表描述类
*/
public class LinkedList implements IList {
// 1、定义头结点
public Node head;
// 2、初始化头结点
public LinkedList() {
head = new Node();
}
// // 构造函数构造长度为n的单链表
// public LinkedList(int n, boolean Order) {
// this();
// if (Order)
// create1(n);
// else
// create1(n);
// }
// // 头插法(逆序)建立单链表
// public void create1(int n) {
// Scanner scanner = new Scanner(System.in);
// for (int i = 0; i < n; i++) {
// insert(i, scanner.next());
// }
//
// }
//
// // 尾插法(顺序)建立单链表
// public void create2(int n) throws Exception{
// Scanner scanner = new Scanner(System.in);
// for (int i = 0; i < n; i++) {
// insert(length(), scanner.next());
// }
// }
@Override
public void clear() {
// 将头结点置空,后继结点置空
head.data = null;
head.next = null;
}
@Override
public boolean isEmpty() {
// 判断头结点是否有后继结点即可
return head.next == null;
}
@Override
public int length() {
// 遍历单链表,返回长度
// 定义长度
int length = 0;
// p 为后继结点
Node p = head.next;
while (p != null) {
p = p.next;
length++;
}
return length;
}
@Override
public Object get(int i) {
// 定义后继结点
Node p = head.next;
int j;
// 遍历链表
for (j = 0; j < i && p != null; j++) {
p = p.next;
}
// 如果是空表或已经越过了 i
if (j > i | p == null) {
System.out.println("查找错误!");
}
return p.data;
}
// 带头节点的插入
@Override
public void insert(int i, Object x) {
Node p = head;
// 考虑到插入位置可能是链表头
int j = -1;
// 寻找i位置的前驱结点(第 i-1 个结点)
while (p != null && j < i - 1) {
p = p.next;
j++;
}
// 判断插入位置合法性
if (j > i - 1 | p == null) {
System.out.println("插入位置不合法!");
}
// 建立要插入的新结点
Node n = new Node();
n.data = x;
n.next = p.next;
p.next = n;
}
@Override
public void remove(int i) {
Node p = head;
int j = -1;
// 寻找i位置的前驱结点(第 i-1 个结点)
while (p != null && j < i - 1) {
p = p.next;
j++;
}
if(j > i - 1 | p == null) {
System.out.println("删除位置不合法!");
}
p.next = p.next.next;
}
@Override
public int indexOf(Object x) {
Node p = head;
// 定义一个游标
int tp = 0;
while (p != null && p.data.equals(x)) {
p = p.next;
tp++;
}
if (p != null)
return tp;
else
return -1;
}
@Override
public void display() {
Node p = head;
while (p != null && p.next !=null) {
p = p.next;
System.out.println(p.data);
}
}
}
4、创建测试类
package linkedList;
/**
* @author Administrator 测试类
*/
public class Test {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
// 判断是否空链表,链表长度
System.out.println(linkedList.isEmpty());
System.out.println(linkedList.length());
// 插入结点
linkedList.insert(0, "徐凤年");
linkedList.insert(1, "青鸟");
linkedList.insert(2, "红麝");
linkedList.insert(3, "绿蚁");
linkedList.insert(4, "姜泥");
linkedList.insert(2, "洛阳");
// 判断是否插入成功
System.out.println(linkedList.isEmpty());
System.out.println(linkedList.length());
// 按位置查找
System.out.println(linkedList.get(3));
// 按值查找
// 删除结点
linkedList.remove(1);
// 判断是否删除成功
System.out.println(linkedList.length());
linkedList.display();
// //头插法
// linkedList.create1(2);
}
}
5、总结
- 链表动态分配存储空间,能避免资源浪费,提高运行效率和存储空间利用率。
- 链表便于插入和删除。
- 不能按照位号随机存取。
6、顺序存储和链式存储的比较
顺序表 | 链表 | |
---|---|---|
缺点 | 存储空间预先分配,容易造成资源浪费。插入和删除效率低。 | 存储密度低。不能随机存取。 |
优点 | 可进行高效随机存取。存储密度高。 | 插入和删除效率高。进行动态的存储空间分配。 |
缺点 |
优点 |