Java是如何构造链表的
节点定义
首先我们需要明白的是链表时由一个个节点Node串连起来的,每一个Node都包含着两个部分:数据域和指针域。数据域用来保存Node的信息,指针域则保存下一个节点的地址,即指向下一个节点。
在Java中定义Node通常以以下形式:
class Node{
int val; //数据域
Node next; //指针域
public Node(int val) {
this.val = val;
}
}
创建链表
下面介绍一种简单的创建链表的方式,针对给出的数组,利用数组元素创建链表:
private static Node initList(int[] arr) {
Node head=null, cur=null;
for (int i = 0; i < arr.length; i++) {
Node node = new Node(arr[i]);
node.next=null;
//要判断开始时cur为null
if(i==0){
head=node;
cur=node;
}else{
cur.next=node;
cur=node;
}
}
return head;
}
需要注意的是要对第一次插入节点是做出判断,因为此时cur.next为null,否则会报空指针错误。
链表插入元素
向链表中插入元素一般要考虑三种情况:头部插入、中间插入以及尾部插入。
头部插入比较简单,只需要将新节点指向head,然后调整head即可:
node.next=head;
head=node;
中间插入一般会给出寻找插入位置的条件,比如:直接给出特定位置、插入后保持链表元素的有序性等。中间插入的关键在于找到要插入位置的前一个节点prev,找到后即可轻松插入节点,在找节点位置时,要注意所给位置和链表长度的大小关系,处理越界情况。
node.next=prev.next;
prev.next=node;
尾部插入找到最后一个节点即可
Node cur=head;
while(cur.next!=null){
cur=cur.next;
}
//此时cur为最后一个元素
cur.next=node;
链表删除元素
链表的删除同样分为三个情况:头部删除、中间删除以及尾部删除。
头部删除只要将head指针后移一位即可:
head=head.next;
中部删除元素同样需要找到需删除的元素的前驱元素prev,如何找前驱元素将在后面所给完整代码中演示。
prev.next=prev.next.next;
尾部删除元素即需要找到倒数第二个数node
node.next=null;
操作单链表代码
public class CreateLink {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6};
Node list = initList(arr); //初始化链表
print(list);
System.out.println();
list = insert(list, new Node(8), 7); //插入元素
print(list);
System.out.println();
list = delete(list, 1);
print(list);
}
//创建链表
private static Node initList(int[] arr) {
Node head = null, cur = null;
for (int i = 0; i < arr.length; i++) {
Node node = new Node(arr[i]);
node.next = null;
//要判断开始时cur为null
if (i == 0) {
head = node;
cur = node;
} else {
cur.next = node;
cur = node;
}
}
return head;
}
private static Node insert(Node head, Node newNode, int position) {
if (head == null) { //判断是否为空链表
return newNode;
}
//判断下标是否越界
int size = getLength(head);
if (position < 1 || position > size + 1) {
System.out.println("输入下标错误");
return head;
}
//插入表头
if (position == 1) {
newNode.next = head;
return newNode;
}
//在后面插入,找前驱
Node p = head;
for (int i = 1; i < position - 1; i++) {
p = p.next;
}
newNode.next = p.next;
p.next = newNode;
return head;
}
private static Node delete(Node head, int position) {
if (head == null) {
System.out.println("链表为空");
return null;
}
int size = getLength(head);
if (position < 1 || position > size) {
System.out.println("传入下标错误");
return head;
}
//删除头部元素
if (position == 1) {
head = head.next;
}
//删除中间元素
Node p = head;
for (int i = 1; i < position - 1; i++) {
p = p.next;
}
p.next = p.next.next;
return head;
}
private static int getLength(Node head) {
int count = 0;
Node p = head;
while (p != null) {
count++;
p = p.next;
}
return count;
}
private static void print(Node head) {
Node p = head;
while (p != null) {
System.out.print(p.val + "-");
p = p.next;
}
}
static class Node {
int val;
Node next;
public Node(int val) {
this.val = val;
}
}
}
双向链表
双向链表与普通单链表相比,其节点多了一个前驱指针prev指向前一个元素,解决了单链表只能以头指针开始遍历的缺陷。
节点定义
public class DoubleNode {
int val;
DoubleNode prev;
DoubleNode next;
public DoubleNode(int val) {
this.val = val;
}
}
双向链表定义
public class DoubleLinkList {
DoubleNode first; //第一个节点
DoubleNode last; //最后一个节点
public DoubleLinkList() {
first=null;
last=first;
}
}
插入元素
双向链表的插入与删除元素与单向链表的操作差不多,主要思路都是先找到位置,然后在进行操作。
private static DoubleNode insert(DoubleLinkList list,DoubleNode newNode,int position){
if(list.first==null){//链表为空
list.first=newNode;
list.last=newNode;
return newNode;
}
int size=getLength(list); //链表长度
if(position<1||position>size+1){
System.out.println("传入下标错误");
return list.first;
}
//首部插入
if(position==1){
newNode.next=list.first;
list.first.prev=newNode;
list.first=newNode;
return list.first;
}
//尾部插入
if(position==size+1){
list.last.next=newNode;
newNode.prev=list.last;
list.last=newNode;
return list.first;
}
//中间插入
DoubleNode p=list.first;
for (int i = 0; i < position-1; i++) { //找到前驱
p=p.next;
}
newNode.next=p.next;
p.next=newNode;
newNode.prev=p;
newNode.next.prev=newNode;
return list.first;
}
删除元素
与插入类似,不过更加复杂,需要注意在删除前链表元素的个数,这里就不做详细代码解释。