目标:
1.理解Java如何构造出链表的
2.链表增加元素,首部、中间和尾部分别会有什么问题,该如何处理?
3.链表删除元素,首部、中间和尾部分别会有什么问题,该如何处理?
4.双向链表是如何构造的,如何实现元素的插入和删除。
链表概念
单链表
链表是由指针串联起来的线性结构,每个节点由数据和指向下一个节点的指针构成,最后一个节点的指针域指向null。
注意:单链表后继只能指向一个节点,但是一个节点可以被多个节点指向。
例如:下图2中 c1指向多个节点所以,不满足单链表
其他概念
**头节点:**链表的第一个节点,知道头结点才可遍历整个链表,很重要
**虚拟节点:**dummyNode,其next指针指向head。用来便利对头结点的操作,统一操作逻辑。
构造链表
链表定义
public class ListNode {
public int val;
public ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
ListNode listnode=new ListNode(1);
链表初始化
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5, 6};
Node head = initLinkedList(a);
System.out.println(head);
}
private static Node initLinkedList(int[] array) {
Node head = null, cur = null;
for (int i = 0; i < array.length; i++) {
Node newNode = new Node(array[i]);
newNode.next = null;
if (i == 0) {
head = newNode;
cur = newNode;
} else {
cur.next = newNode;
cur = newNode;
}
}
return head;
}
static class Node {
public int val;
public Node next;
Node(int x) {
val = x;
next = null;
}
}
可看到如下结构:
遍历
public static String toString(Node head) {
Node current = head;
StringBuilder sb = new StringBuilder();
while (current != null) {
sb.append(current.data).append("\t");
current = current.next;
}
return sb.toString();
}
这里直接操作head,不进行赋值也可以。不会影响外面传入的原head的指向,但如果在遍历的同时还需要对链表有其他操作最好还是需要保留头结点的指向。
新增
考虑三种插入位置的情况。头部新增单独处理,中间插入和尾部插入逻辑相同
头部插入
需注意插入新的头结点后,要将head指针指向新的头结点
中间插入
要注意插入顺序 先将新节点指向后一个节点后,再将前一个节点指向新节点(顺序错误会导致后面的链表失连)
newNode.next = afterNode.next;
preNode.next = newNode;
尾部插入
实现时可使用中间插入逻辑
只要将尾结点指向新结点就行
/**
* 链表插入
*
* @param head 链表头节点
* @param nodeInsert 待插入节点
* @param position 待插入位置,取值从1开始
* @return 插入后得到的链表头节点
*/
public static Node insertNode(Node head, Node nodeInsert, int position) {
// 需要判空,否则后面可能会有空指针异常
if (head == null) {
return nodeInsert;
}
//越界判断
int size = getLength(head);
if (position > size + 1 || position < 1) {
System.out.println("位置参数越界");
return head;
}
//在链表开头插入
if (position == 1) {
nodeInsert.next = head;
head = nodeInsert;
return head;
}
//中间和尾部插入
Node pNode = head;
int count = 1;
//停在插入位置的前一个节点进行操作所以position-1
while (count < position - 1) {
pNode = pNode.next;
count++;
}
//注意这里的 顺序 要先将待插入节点指向下一个节点,再将前一个节点指向待插入节点
nodeInsert.next = pNode.next;
pNode.next = nodeInsert;
return head;
}
删除
也是考虑,头部删除、中间删除、末尾删除3中情况。并在指定元素的前一个进行操作
头部删除
head往后移动一位就可以 head=head.next
中间删除
在删除节点的前一个节点进行操作,只需要cur.next=cur.next.next就可以了。末尾节点删除也是一样
尾部删除
与中间删除逻辑一样,在删除节点的前一个节点进行操作
public static Node deleteNode(Node head, int position) {
if (head == null) {
return null;
}
int size = getLength(head);
if (position > size || position <= 0) {
System.out.println("输入的参数有误");
return head;
}
if (position==1){
head=head.next;
head.next=null;
return head;
}
Node cur=head;
int count=1;
//也是停留在删除元素的前一个进行操作
while (count<position-1){
cur=cur.next;
count++;
}
cur.next=cur.next.next;
return head;
}