为什么要用数组进行链表模拟
在Java中我们可以创建Node节点通过创建新节点 new Node() 的方式模拟链表实现,但是每一次都需要创建新节点,是比较慢的,而我们用数组进行模拟链表较快。
而链表又分为单链表和双链表,具体来看看如何用数组进行单、双链表的模拟。
单链表
用法:
单链表用得较多的是邻接表 ——> 存储图和树。
需要什么:
1.需要存储值的数组e[N];
2.下一个的指针数组ne[N];
3.尾指针指向空,则ne[i] = -1;
!值数组n[N]和指针数组ne[N]是通过下标联系起来的!
详解:怎么写单链表
注意:head指的是节点的下标,idx为当前操作值的下标。这两个在刚开始理解可能较困难 ,在此单列出来。
1.进行初始化
初始化需要注意头节点为空,并且让idx从0开始。
2.把 x插入为第一个元素
3.把x点插入到下标为k的点的后面
4.删除k后面的点
题目:
解题完整代码 (Java)
import java.util.Scanner;
public class Main {
static int head;//head表示头结点的下标
static int N = 100010;
static int[] e = new int[N];//e[i]表示结点i的值
static int[] ne = new int[N];//ne[i]表示结点i的next指针是多少
static int idx;//存储当前值的下标
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int M = sc.nextInt();
//首先进行初始化
init();
while (M-- > 0) {
String s = sc.next();
char op = s.charAt(0);
if (op == 'H') {
int x = sc.nextInt();
add_to_head(x);
} else if (op == 'D') {
int k = sc.nextInt();
//k为0时,删除头节点
if (k == 0) {
head = ne[head];//删除头节点就是让原先头节点的下标变成原先头节点下一个结点的下标
} else {
remove(k - 1);
}
} else {
int k = sc.nextInt();
int x = sc.nextInt();
add(k - 1, x);
}
}
for (int i = head; i != -1; i = ne[i]) {//由于头节点可能会改变,遍历的起始值为头结点的下标head
System.out.print(e[i] + " ");
}
}
//初始化
public static void init() {
head = -1;//初始化时,头结点为空
idx = 0;//idx从0号点开始分配
}
//把x插入到头结点上
public static void add_to_head(int x) {
e[idx] = x;
ne[idx] = head;
head = idx;
idx++;
}
//将x点插入到下标为k的点的后面
public static void add(int k, int x) {
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx;
idx++;
}
//删除k后面的点
public static void remove(int k) {
ne[k] = ne[ne[k]];
}
}
双链表
用法:
双链表较多用于某些问题的优化。
特点:双链表有两个指针,一个指向前,一个指向后。
需要什么:
1.存储节点值的数组:e[N];
2.存储e[i]上一个点的下标:l[N];
3.存储e[i]下一个点的下标:r[N];
3.存储当前值的下标:idx
双链表值数组和指针数组也是通过下标联系起来的。
详解:怎么写双链表
1.进行初始化
2.在下标为k的点的右边(下一个)插入x
注意在左边(上一个)插入x,就是在l[k]的后面插入x,进行转换。
3.删除下标为k的点
题目:
解题完整代码(Java)
import java.util.Scanner;
public class Main {
static int N = 100010;
static int[] e = new int[N];//e[i]表示结点i的值
static int[] l = new int[N];//存储e[i]上一个点的下标
static int[] r = new int[N];//存储e[i]下一个点的下标
static int idx;//存储当前值的下标
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int M = sc.nextInt();
init();
while (M-- > 0){
String s = sc.next();
char op = s.charAt(0);
if(op =='L'){
int x = sc.nextInt();
add(0,x);
}else if(op =='R'){
int x = sc.nextInt();
add(l[1],x);
}else if(op =='D'){
int x = sc.nextInt();
remove(x+1);
}else if(s.equals("IR")){
int k = sc.nextInt();
int x = sc.nextInt();
add(k+1,x);
}else {
int k = sc.nextInt();
int x = sc.nextInt();
add(l[k+1],x);
}
}
for (int i = r[0]; i != 1 ; i=r[i]) {
System.out.print(e[i] + " ");
}
}
//初始化
public static void init(){
//0表示左端点,1表示右端点
r[0] = 1;
l[1] = 0;
idx = 2;//0和1已经被占用了
}
//在下标为k的点的右边插入x
public static void add(int k,int x){
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx;
idx++;//不要忘了idx在进行插入时要自增
//在下标为k的点的左边插入x,就是在l[k]后面插入x
}
//删除下标为k的点
public static void remove(int k){
r[l[k]] = r[k];
l[r[k]] = l[k];
}
}
学习链表的时候,首先理解链表是要实现一个什么样的模型,本篇主要是关于数组如何进行模拟实现,理解之后抽出需要的方法,题目就是经过不同的包装,依具体题意分析,但是实现的基本功能是不变的。