参考:
http://blog.csdn.net/huangxy10/article/category/1244320这位博友的链表专题
和别人的leetcode代码,在此致谢。
问题:
删除链表中的某个点。
解答:
1)可以借助于节点的前一个节点来删除。
要花费O(n)的时间来查找节点的前继节点。时间复杂度O(n),空间复杂度O(1).
2)将后一个节点的内容复制到本节点,然后删除后一个节点。时间复杂度O(1),空间复杂度O(1)
这样就省略了查找上一个节点的时间。注意要删除的节点为尾节点的情况!
1 Node* DelNode(Node* node,Node* head) 2 { 3 if(node->next == NULL) 4 { 5 while(head->next != node) 6 head = head->next; 7 8 head->next = NULL; 9 return NULL; 10 }else 11 { 12 *node = *(node->next); 13 node->next = node->next->next; 14 return node; 15 } 16 }
问题:
单链表的旋转
解答:
1)每次取得两个相邻的三个节点,反转前两个节点的指针,记录第三个节点。依次往后移动。
时间复杂度:O(n)
空间复杂度:O(1)
1 Node* reverseList(Node* head) 2 { 3 if(head == NULL ||head->next == NULL) 4 return head; 5 6 Node* prev = head; 7 Node* curr = head->next; 8 Node* next; 9 10 prev->next=NULL; 11 while(curr != NULL) 12 { 13 next = curr->next; 14 15 curr->next = prev; 16 17 prev = curr; 18 curr = next; 19 } 20 21 return prev; 22 }
2)使用两个指针,一直指向最左端,一个指向最右端。交换两个指针指向对象的内容,之后左端指针右移,右端指针左移。
耗时操作在于右端指针移动时定位其前一节点的动作。
总体时间复杂度为:O(n2);空间复杂度为:O(1)
问题:
找出链表中的倒数第n个元素。
类似于 Leetcode -- Remove Nth Node from End of List
解答:
1)两个遍历解决。注意:n大于链表长度的情况。
时间复杂度:O(n),空间复杂度:O(1)
如何一次遍历就解决问题?
2)使用连个指针【又是双指针!】。选择一个指针向前先走n步,然后两个指针同时移动。当先移动的指针到达链表尾部的时候,另一个指针就指在倒数第n个位置。
时间复杂度:O(n),空间复杂度:O(1)
1 Node* find_backwark_n(Node* head,int n) 2 { 3 if(n <= 0) 4 return NULL; 5 6 Node* frontNPtr = head; 7 while(n-- && frontNPtr)//这里要注意一点:当最后退出时会判断到n==0,虽然此时会退出while循环,但是n--操作还是会执行,n的值变为了-1 8 frontNPtr = frontNPtr->next; 9 10 //对于n大于链表长度的情况我们返回NULL! 11 //如果head == NULL的情况,也会在这里返回 12 if(n >= 0) 13 return NULL; 14 15 Node* backNPtr = head; 16 17 while(frontNPtr) 18 { 19 frontNPtr = frontNPtr->next; 20 backNPtr = backNPtr->next; 21 } 22 23 return backNPtr; 24 }
3)借助一个数组来模拟的滑动窗口。窗口的大小为n,窗口在滑动的时候,剔除链表左端的元素,加入右端新元素。
这样当遍历完数组后,窗口中记录的链表中最左端元素即为所求。
时间复杂度:O(n),空间复杂度:O(m)
1 Node* find_backwark_n(Node* head,int n) 2 { 3 if(n <= 0 || head == NULL) 4 return NULL; 5 6 Node** window = new Node*[n]; 7 if(window==NULL) 8 return NULL; 9 10 Node* curr = head; 11 int pos = 0; 12 while(curr) 13 { 14 window[pos % n] = curr; 15 16 pos++; 17 curr = curr->next; 18 } 19 20 if(pos < n) 21 return NULL; 22 23 Node* res = window[pos % n]; 24 delete window; 25 26 return res; 27 }
问题:
寻找链表的中间元素。
解答:
1)两次遍历。
2)快+慢指针。
寻找链表的中间元素,也就是寻找链表中位于1/2位置的元素。我们考虑下,寻找链表中1/n位置的元素。
此时仍用快慢指针来处理。只不过快指针每次向前移动n步。
时间复杂度:O(n),空间复杂度:O(1)
1 Node* get_position(Node* node,int n) 2 { 3 while(n-- && node) 4 node = node->next; 5 if(n >=0) 6 return NULL; 7 8 return node; 9 } 10 Node* find_Node(Node* head,int n) 11 { 12 if(n <= 1) 13 return NULL; 14 15 Node* fast = get_position(head,n); 16 if(fast == NULL) 17 return NULL; 18 19 Node* slow = head->next; 20 while(fast = get_position(fast,n)) 21 { 22 slow = slow->next; 23 } 24 25 return slow; 26 }
问题:
交换链表中”相邻“的节点。只能通过操作节点的next指针来实现。
题目:Leetcode -- Swap Nodes in Pairs
解答:
可以申请使用newHead节点来减少边界判断!
1 ListNode* swapPairs(ListNode *head) 2 { 3 //可以通过使用一个新头来减少对左边界情况的判断 4 ListNode newHead(0); 5 ListNode* p = &newHead; 6 p->next = head; 7 8 //我们在代码中为新的nnext节点连向了nnnext节点 9 //所以不需要处理只剩下单个节点的情况了。 10 while(p->next && p->next->next) 11 { 12 ListNode* n = p->next; 13 ListNode* nnn = p->next->next->next; 14 15 p->next = p->next->next; 16 p->next->next = n; 17 p->next->next->next = nnn; 18 19 p = p->next->next; 20 } 21 22 return newHead.next; 23 }
问题:
旋转链表n位。 题目: Leetcode -- Rotate List
解答:
1)参考动态窗口的思路。注意窗口为1的情况需要特别处理。
1 int get_length(ListNode* head) 2 { 3 int len = 0; 4 while(head) 5 { 6 len++; 7 head = head->next; 8 } 9 return len; 10 } 11 12 //使用k的数组存储后[k+1 ~ 2]个值 13 //思路来自“寻找单链表中的倒数第n个数” 14 ListNode *rotateRight(ListNode *head, int k) 15 { 16 if(head == NULL) 17 return NULL; 18 19 k = k % get_length(head);//在使用get_length之前,得确认链表是否为空 20 if(k==0) 21 return head; 22 23 ListNode** window = new ListNode*[k]; 24 25 ListNode* curr = head; 26 int pos = 0; 27 while(curr->next) 28 { 29 window[pos % k] = curr; 30 curr = curr->next; 31 pos ++ ; 32 } 33 34 //倒数第k+1个节点被存储在window[pos % (k+1)]中 35 //最后一个节点位于curr中 36 37 curr->next = head; 38 window[pos % k]->next = NULL; 39 40 if(k == 1) 41 return curr; 42 else 43 return window[(pos+1) % k]; 44 }
2)既然归根到底是定位的问题,当然可以用块+慢指针啦
1 int get_length(ListNode* head) 2 { 3 int len = 0; 4 while(head) 5 { 6 len++; 7 head = head->next; 8 } 9 return len; 10 } 11 12 //快+慢指针啦 ~ 13 ListNode *rotateRight(ListNode *head, int k) 14 { 15 if(head == NULL || k<0) 16 return NULL; 17 if(k == 0) 18 return head; 19 20 k = k % get_length(head); 21 22 ListNode* frontPtr = head; 23 while(k--) 24 frontPtr = frontPtr->next; 25 26 ListNode* backPtr = head; 27 while(frontPtr->next) 28 { 29 frontPtr = frontPtr->next; 30 backPtr = backPtr->next; 31 }//最终停止时,frontPtr指向最后一个节点,backPtr指向倒数第k+1个节点。 32 33 frontPtr->next = head; 34 35 head = backPtr->next; 36 backPtr->next = NULL; 37 38 return head; 39 }
可以通过先移动,后判断的方法来减少不必要的就按链表长度。
1 //快+慢指针啦 ~ 2 ListNode *rotateRight(ListNode *head, int k) 3 { 4 if(head == NULL || k<=0) 5 return head; 6 7 ListNode* frontPtr = head; 8 int pos = 0; 9 while(pos < k) 10 { 11 pos++; 12 frontPtr = frontPtr->next; 13 if(frontPtr == NULL) 14 break; 15 } 16 17 if(frontPtr == NULL) 18 return rotateRight(head,k%pos); 19 20 ListNode* backPtr = head; 21 while(frontPtr->next) 22 { 23 frontPtr = frontPtr->next; 24 backPtr = backPtr->next; 25 }//最终停止时,frontPtr指向最后一个节点,backPtr指向倒数第k+1个节点。 26 27 frontPtr->next = head; 28 29 head = backPtr->next; 30 backPtr->next = NULL; 31 32 return head; 33 }