1.虚拟头节点的设置
在链表问题中,我们直接控制节点往往不好操作,往往需要设置一个虚拟头节点来模拟操作。
ListNode dummy = new ListNode(-1,head);//连接两个链表,且当前链表头值为-1
ListNode pre =dummy;//指针指向dummy
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(-1,head);//连接两个链表,且当前链表头值为-1
ListNode pre =dummy;//指针指向dummy
while(pre.next!=null){
if(pre.next.val!=val){
pre=pre.next;
}else{
pre.next=pre.next.next;//连接pre节点的后两个节点
}
}
return dummy.next;
}
}
2.设计链表
涉及到列表的增删(双链表,单链表)
单链表
/*
* 实体类
*/
class ListNode{
int val;//当前节点的值
ListNode next;//指向下一个节点的指针
ListNode(){}//无参构造方法
ListNode (int val){
this.val=val;
}
}
/*
* 工具类
*/
class MyLinkedList {
int size;//size存储链表元素的个数
ListNode head;//设置虚拟头节点
//初始化链表
public MyLinkedList() {
size = 0;
head = new ListNode(0);//虚拟头节点,并不是真正的头节点
}
//获取链表中第 index 个节点的值。
public int get(int index) {
//如果index非法返回-1
if(index<0||index>=size){
return -1;
}
ListNode currentNode = head;
for(int i =0;i<index+1;i++){
currentNode=currentNode.next;
}
return currentNode.val;
}
//在链表的第一个元素之前添加一个值为 val 的节点。
public void addAtHead(int val) {
addAtIndex(0,val);
}
//将值为 val 的节点追加到链表的最后一个元素。
public void addAtTail(int val) {
addAtIndex(size,val);
}
//在链表中的第 index 个节点之前添加值为val的节点。
public void addAtIndex(int index, int val) {
if(index>size){
return;
}
if(index<0){
index=0;
}
size++;//要插入,所以要加一
ListNode pred = head;
for(int i =0;i<index;i++){//得到index的前一个节点
pred=pred.next;
}
ListNode toAdd = new ListNode(val);
toAdd.next = pred.next;
pred.next = toAdd;
}
//deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
ListNode pred = head;
for (int i = 0; i < index; i++) {
pred = pred.next;
}
pred.next = pred.next.next;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
3.删除链表的倒数第n个节点
思路:
1.删除链表,链表反转等问题一般需要设置虚拟头节点
2.快慢指针处理:从虚拟头节点开始,快指针先向前移动n个节点,然后快慢节点一起移动,这样就保证了,慢指针所处的位置时链表的倒数第n个节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next=head;
ListNode slow = dummy;//慢指针
ListNode fsst = dummy;//快指针
//快指针移动向前移动n个节点
for(int i =0;i<n;i++){
fsst = fsst.next;
}
//快指针移动到链表尾部,同时慢指针也移动
while(fsst.next!=null){
fsst=fsst.next;
slow=slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
}
4.面试题 02.07. 链表相交
思路:
1.分别得到a,b链表的长度
2.然后将两个链表尾部对齐
3.从头对齐处开始遍历,如果两个节点相等,返回该节点。否则返回null
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA =0;//链表A的长度
int lenB =0;//链表B的长度
//链表A的长度
while(curA!=null){
curA=curA.next;
lenA++;
}
//链表B的长度
while(curB!=null){
curB = curB.next;
lenB++;
}
curA=headA;
curB=headB;
//让curA成为最长链表的头,lenA为它的长度
if(lenB>lenA){
int temp = lenA;
lenA =lenB;
lenB =temp;
ListNode tempNode = curA;
curA =curB;
curB = tempNode;
}
//求长度差
int gap = lenA-lenB;
//两个链表末尾位置对齐
while(gap!=0){
curA=curA.next;
gap--;
}
//遍历curA和curB,遇到相同的就直接返回
while(curA!=null){
if(curA==curB){
return curA;
}
curA=curA.next;
curB =curB.next;
}
return null;
}
}
5.环形链表II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
//1.先用快慢指针判断是否有环,倘若二者能够相遇说明存在环
//2.x为头节点到环形入口的节点,y为环形入口的节点到相遇位置的节点,z为相遇位置到环形入口的节点。
//3.我们有(x+y)*2=x+y+n(y+z),化解得到x=(n-1)z
//4.这个式子说明了,当头节点和相遇节点的位置,各自向前走一步,相遇位置就是环的入口
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast!=null&&fast.next!=null){
slow=slow.next;//慢指针移动一位
fast=fast.next.next;//快指针移动两位
if(slow==fast){//有环
ListNode index1 =head;//起始位置
ListNode index2 = slow;//相遇位置
//两个指针,从头节点和相遇节点开始,各走一步,直到形象与,相遇即为环入口
while(index1!=index2){
index1=index1.next;
index2=index2.next;
}
return index1;
}
}
return null;
}
}