1、基本概念
核心:一个结点只能有一个后继,但不代表一个结点只能有一个被指向。
节点:每个点都由值和指向下一个结点的地址组成的独立的单元,称为一个节点,链表一般由头节点与一般节点组成。
虚拟节点:
虚拟节点:
dummyNode,其next指针指向头节点,也就是dummyNode.next=head。
因此,如果我们在算法里使用了虚拟结点,则要注意如果要获得head结点,或者从方法(函数)里返回的时候,则应使用dummyNode.next。
dummyNode的val不会被使用,初始化为0或者-1等都是可以的。
虚拟节点存在的作用为了方便我们处理首部结点,否则我们需要在代码里单独处理首部节点的问题。在链表反转里,我们会看到该方式可以大大降低解题难度。
2、链表的创建
public class ListNode {
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
this.next = null;
}
}
3、链表的遍历
注意:操作之后必须要能找到表头,不能只顾当前位置而将丢掉了标记表头的指针
public static int getListLength(ListNode head) {
int length = 0;
ListNode node = head;
while(node != null){
length++;
node = node.next;
}
return length;
}
4、链表插入
1、链表头插入
注意:要将head指向链首元素
2、链表中间插入
我们应该找到目标节点的前驱节点,利用listNode.next来判断而不是listNode。否则我们将找不到前驱节点。
如果要在7的前面插入,当listNode.next=node(7)了就应该停下来,此时listNode.val=15。然后需要给
new前后接两根线,时只能先让new.next=node(15).next(图中虚线),然后node(15).next=new,而且顺序还不能错。
3、链表结尾插入(一般与链表中间插入一块写)
普通链表的插入
/**
* 链表插入
* @param head 头节点
* @param nodeInsert 待插入的节点
* @param position 待插入的位置,从1开始
* @return 插入后得到的链表的头节点
*/
public static ListNode insertNode(ListNode head,ListNode nodeInsert,int position){
if(head == null){
//插入的节点就是链表的头节点
return nodeInsert;
}
int size = getListLength(head);
if(position > size + 1 || position < 1){
System.out.println("参数越界");
return head;
}
//链表头插入
if(position == 1){
nodeInsert.next = head;
head = nodeInsert;
return head;
}
//链表中间或者尾部插入
int count = 1;
ListNode node = head;
while (count < position - 1){
count ++;
node = node.next;
}
nodeInsert.next = node.next;
node.next = nodeInsert;
return head;
}
有序链表的插入
/**
* 有序链表的插入
*
* @param head 头节点
* @param nodeInsert 待插入的节点
* @return
*/
public static ListNode insertOrderedNode(ListNode head, ListNode nodeInsert) {
if (head == null) {
//插入的节点就是链表的头节点
return nodeInsert;
}
//链表头插入
if (head.val > nodeInsert.val) {
nodeInsert.next = head;
head = nodeInsert;
}
//链表中间或者尾部插入
ListNode node = head;
while (node.next != null && node.next.val < nodeInsert.val) {
node = node.next;
}
nodeInsert.next = node.next;
node.next = nodeInsert;
return head;
}
5、链表的删除
1、删除链表头
直接head=head.next即可
2、删除链最后一个节点
找到要删除的节点的前驱节点,这里同样要在提前一个位置判断。
如需删除40,其前驱节点为7。遍历的时候需要判断cur.next是否为40,如果是,则只要执行cur.next=null即可。
3、删除中间节点
也需要提前一个位置判断
cur.next=cur.next.next
/**
* 删除节点
* @param head 链表头节点
* @param position 删除节点的位置,取值从1开始
* @return 删除后的链表的头节点
*/
public static ListNode deleteNode(ListNode head,int position){
if(head == null){
return null;
}
int size = getListLength(head);
if(position > size || position < 1){
System.out.println("参数越界");
return head;
}
if(position == 1){
head = head.next;
return head;
}
int count = 1;
ListNode node = head;
while (count < position - 1) {
count++;
node = node.next;
}
node.next = node.next.next;
return head;
}