1.链表的基本概念
首先看一下什么是链表?单向链表就像一个铁链一样,元素之间相互连接,包含多个结点,每个结点有一个指向后继元素的next指针。表中最后一个元素的next指向null。如下图
特点:①缺点:只能连续访问,效率低,但是通过双向链表缓解了该问题②优点:有助于添加元素,扩容方便,没空间限制,不会溢出,可以存储很多个元素。
节点和头结点
在链表中 ,每个点都由值和指向下一个结点的地址组成的独立的单元,称为一个结点,有时也称为节点,含义都是一样的。
对于单链表,如果知道了第一个元素,就可以通过遍历访问整个链表,因此第一个结点最重要,一般称为头结点
虚拟节点
在做题以及在工程里经常会看到虚拟结点的概念,其实就是一个结点dummyNode,其next指针指向head,也就是dummyNode.next=head。
因此,如果我们在算法里使用了虚拟结点,则要注意如果要获得head结点,或者从方法(函数)里返回的时候,则应使用dummyNode.next。
- 虚拟节点可以简化链表的操作,可以不用考虑特殊节点头节点
2.链表的构建
/**
* 单链表的定义
* @author NuoMi
*/
public class Node {
public int value;
public Node next;
public Node(int value) {
this.value = value;
next = null;
}
}
3.链表的基本操作
1.遍历:
/**
* 获取链表的长度
* @param head 头节点
* @return
*/
public static int getListLength(Node head) {
int length = 0;
Node node = head;
while (node != null) {
length++;
node = node.next;
}
return length;
}
2.插入节点:
需要考虑是否是头节点
表头插入:
如果是头节点直接nodeInsert.next = head ,head = nodeInsert
在中间插入:
不是头节点则需要先遍历到头节点的前一个节点,然后nodeInsert.next = pNode.next,
pNode.next = nodeInsert
/**
* 插入节点
* @param head 头节点
* @param nodeInsert 插入节点
* @param position 位置
* @return
*/
public static Node insertNode(Node head, Node nodeInsert, int position) {
// 如果为空将认为是一个空链表
if (head == null) {
return nodeInsert;
}
int size = getListLength(head);
if (position > size + 1 || position < 1 ) {
throw new RuntimeException("位置越界");
}
// 如果插入位置为1时
if (position == 1) {
nodeInsert.next = head;
head = nodeInsert;
return head;
}
Node pNode = head;
// 注意要从1开始
int count = 1;
// 遍历道要插入的前一个节点
while (count < position - 1) {
pNode = pNode.next;
count++;
}
// 顺序不能变
nodeInsert.next = pNode.next;
pNode.next = nodeInsert;
return head;
}
实现插入后仍然保持单调:
同样先考虑是否是头节点?
当值比头节点小时为头节点(head.value > nodeInsert.value)
不是头节点时
同样遍历到要插入的前一个节点
定义一个节点curNode保存遍历的节点,pNode保存遍历的前一个节点
当curNode.value < nodeInsert.value时pNode就是要插入的前一个节点进行插入
/**
* 插入节点
* @param head 头节点
* @param nodeInsert 插入节点
* @return
*/
public static Node insertNodeOrder(Node head, Node nodeInsert) {
// 如果为空将认为是一个空链表
if (head == null) {
return nodeInsert;
}
// 如果插入位置为1时
if (head.value > nodeInsert.value) {
nodeInsert.next = head;
head = nodeInsert;
return head;
}
Node pNode = null;
Node curNode = head;
// 遍历道要插入的前一个节点
while (curNode != null && curNode.value < nodeInsert.value) {
pNode = curNode;
curNode = curNode.next;
}
// 顺序不能变
nodeInsert.next = pNode.next;
pNode.next = nodeInsert;
return head;
}
3.删除节点
同样要考虑是否是头节点
删除表头:
/**
* 删除节点
* @param head 头节点
* @param position 位置
* @return
*/
public static Node deleteNode(Node head, int position) {
if (head == null) {
throw new RuntimeException("链表不存在");
}
int size = getListLength(head);
if (position < 1 || position > size) {
throw new RuntimeException("位置过界");
}
// 删除头节点
if (position == 1) {
head = head.next;
return head;
}
Node pNode = head;
int count = 1;
while (count < position - 1) {
pNode = pNode.next;
count ++;
}
pNode.next = pNode.next.next;
return head;
}