方法一:
用遍历链表,用数组保存每一个节点,因为数组的特性,所以达到目的很简单:
public ListNode removeNthFromEnd(ListNode head, int n) {
List<ListNode> list = new ArrayList<>();
while (head != null) {
list.add(head);
head = head.next;
}
if (list.size() == 1) {
return null;
}
if (n == 1) { // 最后一个
ListNode listNode = list.get(list.size() - n - 1);
listNode.next = null;
} else if (n == list.size()) { // 第一个
return list.get(1);
} else {
ListNode listNodePre = list.get(list.size() - n - 1);
ListNode listNodePro = list.get(list.size() - n + 1);
listNodePre.next = listNodePro;
}
return list.get(0);
}
方法二:
遍历计算链表的长度,注意: 在获取链表长度时遍历,如果直接用head遍历,head会直接指向链表尾,这样后面再用的时候,head就不是指向头节点了,然后就知道要删除哪一个节点了,这里有一个删除节点时的窍门:
创建一个新的节点,放在头节点前,这样做就能够做到删除节点时判断的统一性,当删除的是头节点的时候就不用专门去处理了。
这个窍门处理还是有点秀的:
public ListNode removeNthFromEnd(ListNode head, int n) {
int length = 0;
ListNode temp = head;
while (temp != null){
length ++;
temp = temp.next;
}
ListNode first = new ListNode();
first.next = head;
ListNode cur = first; // 为什么从first开始
for (int i = 1; i < length + 1 - n ; i++) { // length + 1因为加了first节点,如果不加1会造成错误,后面加了1,如果还从第0个开始会产生错乱,所以要从1开始,正好head也是第二个元素
cur = cur.next; //i = 1的时候 cur = cur.next 现在的cur正好指向head,在i=1的时候已经是 cur.next了,相当于加一了 (这就是操作指针的时候需要注意的)
}//这样做相当于加上first节点后,在遍历时撇过了first,逻辑上没有问题
//现在curt正好是要删除的那个元素的前一个元素
cur.next = cur.next.next;
ListNode ans = first.next;
return ans;
}
方法三:
快慢指针,前后指针正好相差n个
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode first = new ListNode();
first.next = head;
ListNode preNode = head; // 注意🌟🌟🌟
ListNode proNode = first;
int index = 0;
while (preNode != null){
preNode = preNode.next;
index++;
if (index > n){
proNode = proNode.next;
}
}
proNode.next = proNode.next.next;
return first.next;
}