get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。 addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。 addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。 addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。 deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
/** * 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); */
/** Initialize your data structure here. */ publicMyLinkedList() { size = 0; head = newListNode(0); tail = newListNode(0); head.next = tail; tail.prev = head; } /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */ publicintget(int index) { if(index < 0 || index >= size){return -1;} ListNodecur= head;
// 通过判断 index < (size - 1) / 2 来决定是从头结点还是尾节点遍历,提高效率 if(index < (size - 1) / 2){ for(inti=0; i <= index; i++){ cur = cur.next; } }else{ cur = tail; for(inti=0; i <= size - index - 1; i++){ cur = cur.prev; } } return cur.val; } /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */ publicvoidaddAtHead(int val) { ListNodecur= head; ListNodenewNode=newListNode(val); newNode.next = cur.next; cur.next.prev = newNode; cur.next = newNode; newNode.prev = cur; size++; } /** Append a node of value val to the last element of the linked list. */ publicvoidaddAtTail(int val) { ListNodecur= tail; ListNodenewNode=newListNode(val); newNode.next = tail; newNode.prev = cur.prev; cur.prev.next = newNode; cur.prev = newNode; size++; } /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */ publicvoidaddAtIndex(int index, int val) { if(index > size){return;} if(index < 0){index = 0;} ListNodecur= head; for(inti=0; i < index; i++){ cur = cur.next; } ListNodenewNode=newListNode(val); newNode.next = cur.next; cur.next.prev = newNode; newNode.prev = cur; cur.next = newNode; size++; } /** Delete the index-th node in the linked list, if the index is valid. */ publicvoiddeleteAtIndex(int index) { if(index >= size || index < 0){return;} ListNodecur= head; for(inti=0; i < index; i++){ cur = cur.next; } cur.next.next.prev = cur; cur.next = cur.next.next; size--; } }
由于我们需要找到倒数第 n 个节点,因此我们可以使用两个指针 first 和second 同时对链表进行遍历,并且 first 比second 超前 n 个节点。当 first 遍历到链表的末尾时,second 就恰好处于倒数第 n 个节点。
具体地,初始时first 和 second 均指向头节点。我们首先使用 first 对链表进行遍历,遍历的次数为 n。此时,first 和 second 之间间隔了 n−1 个节点,即 first 比 second 超前了 n 个节点。
在这之后,我们同时使用first 和second 对链表进行遍历。当 first 遍历到链表的末尾(即first 为空指针)时,second 恰好指向倒数第 n 个节点。
根据方法一和方法二,如果我们能够得到的是倒数第 n 个节点的前驱节点而不是倒数第 n 个节点的话,删除操作会更加方便。因此我们可以考虑在初始时将 second 指向哑节点,其余的操作步骤不变。这样一来,当 first 遍历到链表的末尾时,second 的下一个节点就是我们需要删除的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public ListNode removeNthFromEnd(ListNode head, int n) { ListNodedummyNode=newListNode(0,head); ListNodefirst= head; ListNodesecond= dummyNode; for(inti=0 ; i < n ;i++){ first = first.next; }
while(first != null){ first = first.next; second = second.next; } second.next = second.next.next; ListNodeans= dummyNode.next; return ans; } }