描述
给定一个链表,删除链表的倒数第n个节点并返回链表的头指针。例如,给出的链表为: 1→2→3→4→5, n= 2。删除了链表的倒数第n个节点之后,链表变为1→2→3→5.
备注: 题目保证n一定是有效的
请给出请给出时间复杂度为O(n)的算法
示例1
输入:{1,2},2
返回值:{2}
方法一:快慢指针
核心思想:
利用快慢双指针完成删除链表倒数第n个节点。假设链表的节点数为n,想要删除到数第k个节点(k<=n),则当快指针指向第k+1个节点时,此时慢指向第一个节点。快慢指针的差距为k个节点,你那么当快指针指向最后一个节点时(第n个节点),此时慢指针指向第n-k个节点。那么此时倒数第k个节点为慢指针所指的后继节点。
图解:
核心代码:
ListNode* removeNthFromEnd(ListNode* head, int n) { if(head == NULL){ return head; } ListNode* dummy = new ListNode(0); //创建一个空节点作为头结点 dummy->next = head; head = dummy; //head指针指向真正的头结点 ListNode* slow = head; ListNode* fast = head; for(int i = 0; i <n; i++){ //先让快指针后移n个节点 fast = fast ->next; } while(fast->next != NULL){ //快慢指针同时移动,直到快指针移动到链表最后一个节点,此时慢指针刚好指向倒数第n个节点的前驱节点 fast = fast->next; slow = slow->next; } //删除慢指针所指节点的后继节点(后继节点刚好为倒数第n个节点) ListNode* temp = slow->next; slow ->next = slow->next->next; delete temp; return head->next; }
复杂度分析:
代码循环遍历链表,循环次数为链表的长度,因此该方法的时间复杂度为O(N)。由于没有采用额外的空间,因此空间复杂度为O(1)
时间复杂度O(n)
空间复杂度O(1)
方法二:直接节点倒数第n个节点的位置
核心思想:
从头遍历链表计算出链表的长度L,则倒数第n个节点即为顺着的第L-n+1个节点,因此需要删除该节点。
核心代码:
int getLength(ListNode* head) { //计算链表长度 int length = 0; while (head) { ++length; head = head->next; } return length; } ListNode* removeNthFromEnd(ListNode* head, int n) { if(head == NULL){ return head; } ListNode* dummy = new ListNode(0); //创建一个空节点作为头结点 dummy->next = head; ListNode* cur = dummy; int i,length = getLength(head); //计算链表长度 for (i = 1; i < length - n + 1; i++) { //找到倒数第n个节点的前驱节点 cur = cur->next; } //删除慢指针所指节点的后继节点(后继节点刚好为倒数第n个节点) ListNode* temp = cur->next; cur->next = cur->next->next; delete temp; return dummy->next; }
复杂度分析:
代码循环遍历链表,循环次数为链表的长度,因此该方法的时间复杂度为O(N)。由于没有采用额外的空间,因此空间复杂度为O(1)
时间复杂度O(n)
空间复杂度O(1)