1.迭代。
链表最常见的操作就是迭代。
while (head.next != null) {
head = head.next;
}
2.链表转化为数组
涉及到下标的问题,都可以将链表转化为数组解决,数组的每一个元素都是一个节点。。
示例题目LeetCode 876. 返回链表的中间节点
public ListNode middleNode(ListNode head) {
ListNode[] arr = new ListNode[100];
int t = 0;
while (head.next != null) {
arr[t++] = head;
head = head.next;
}
return arr[t / 2];
}
类似的,输出链表中倒数第k个结点。也可以采用转化为数组的思路。
3.删除链表的一个节点
将当前节点node的指针next指向下一个节点的下一个节点,就相当于删除下个节点了。
node.next=node.next.next;
示例题目 :
一个有序的链表删除重复数据。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null || head.next==null){
return head;
}
ListNode node=head;
while(node!=null && node.next!=null ){
if( node.val==node.next.val ) {
//删除重复的节点,不进行迭代,因为重复的值可能出现多次
node.next=node.next.next;
}else {
//迭代
node=node.next;
}
}
return head;
}
}
3.哈希解法
可以将链表的节点,作为HashMap的key,再用HashMap的containsKey()来判断当前节点是否已经存在。
示例,判断链表是否有环:
public boolean hasCycle(ListNode head) {
if(head==null) {
return false;
}
Map map=new HashMap<>();
while(head!=null) {
if(map.containsKey(head)) {
return true;
}
map.put(head,1);
head=head.next;
}
return false;
}
4.双指针操作
4.1快慢指针
使用两个指针fast、slow,用不同的起点/速度去遍历。
使用双指针,要注意判断走得快的指针是否空指针异常。
还要注意,while循环的条件判断可以是常规的 node!=null ,也可以是fast!=slow,或者是fast!=null , fast.next!=null之类。
示例题目:输入一个链表,输出该链表中倒数第k个结点。
public ListNode FindKthToTail(ListNode head,int k) { //5,{1,2,3,4,5}
if(k<0 || head==null ) {
return head;
}
ListNode p, q;
p = q = head;
int i = 0;
//p指针先跑,并且记录节点数,当p指针跑了k-1个节点后,pre指针开始跑,
//当p指针跑到最后时,pre所指指针就是倒数第k个节点
for ( ; p != null; i++) {
p = p.next;
if (i >= k)
q = q.next;
}
return i < k ? null : q;
}
示例题目:判断一个链表,是否为环形链表。
思路:处理环形链表时,也可以使用快慢指针,一个走得快,一个走得慢,如果是环形链表,那么这两个指针最终会相遇。
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
//fast指针走得比较快,要小心空指针。。
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast)
return true;
}
return false;
}
4.2前后节点指针
上一个节点prev,当前节点curr。
示例:链表删除指定的数据
/* Example:
* Input: 1->2->6->3->4->5->6, val = 6
* Output: 1->2->3->4->5
*/
public class LeetCode203 {
public ListNode removeElements(ListNode head, int val) {
//通过fakeHead.next记住链表,以便返回结果
ListNode fakeHead = new ListNode(-1);
fakeHead.next = head;
//curr是当前节点,prev是上一个节点。
ListNode curr = head, prev = fakeHead;
while (curr != null) {
if (curr.val == val) {
prev.next = curr.next;
} else {
prev = prev.next;
}
curr = curr.next;
}
return fakeHead.next;
}
}
4.新建链表解决问题,或者在原来的链表上解决。
5.反转链表
(1)就地反转。(2)新建链表反转。
(3)可以通过栈实现,将链表数据放入栈里面,再拿出来。也就是通过Stack,push()进去,再pop()出来。
(4)通过递归解决。
详情见: https://www.cnblogs.com/expiator/p/10630481.html
6.链表删除指定位置的数据。
7.组装两个有序链表。.
类似于小学时,老师将两支小分队按从矮到高排成一队。
class LeetCode21 {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null ) {
return l2;
}
if(l2==null) {
return l1;
}
//头节点node
ListNode node=new ListNode(0);
ListNode resultNode=node;
//将数据小的节点插入到新的节点中。
while(l1!=null && l2!=null){
if(l1.val
8.A指针走完了一个链表,就指向另一个链表,B指针也是。最后两个指针会同时到达终点。
示例:求两个链表的相交结点。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA;
ListNode pB = headB;
//如果相遇了就结束循环。
while (pA != pB) {
pA = pA.next;
pB = pB.next;
//如果两个链表都走完了所有的路,由于路程的和相同(pA+pB=pB+pA),速度相同,最终会同时到达终点。
if (pA == null && pB == null) {
return null;
}
//A链表走完就指向B链表
if (pA == null) {
pA = headB;
}
//B链表走完就指向A链表
if (pB == null) {
pB = headA;
}
}
return pA;
}
其他:
ListNode dummy = new ListNode(0);
dummy.next = head;