剑指offer面试常考手撸算法题-链表篇
1. 从头到尾打印链表
1 class Solution { 2 public: 3 // 可以先压栈,再出栈到vector 4 // 时间/空间:O(n) 5 vector<int> printListFromTailToHead(ListNode* head) { 6 if(head == nullptr) 7 return {}; 8 vector<int> res; 9 stack<int> s; 10 while(head != nullptr) 11 { 12 s.push(head->val); 13 head = head->next; 14 } 15 while(!s.empty()) 16 { 17 res.push_back(s.top()); 18 s.pop(); 19 } 20 return res; 21 } 22 // 可以直接插入vector中,翻转vector 23 // 时间/空间:O(n) 24 vector<int> printListFromTailToHead(ListNode* head) { 25 vector<int> res; 26 if(head == nullptr) 27 return {}; 28 while(head) 29 { 30 res.push_back(head->val); 31 head = head->next; 32 } 33 reverse(res.begin(), res.end()); 34 return res; 35 } 36 };
2. 链表中倒数第k个节点
1 class Solution { 2 public: 3 //快慢指针,快指针先走k-1步,之后一起走,直到快指针到达链表尾。 4 //时间:O(n), 空间O(1) 5 ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { 6 if(pListHead == nullptr || k == 0) 7 return nullptr; 8 auto p = pListHead; 9 for(int i=0; i<k-1; i++) 10 { 11 if(p->next == nullptr) 12 return nullptr; 13 p = p->next; 14 } 15 while(p->next != nullptr) 16 { 17 p = p->next; 18 pListHead = pListHead->next; 19 } 20 return pListHead; 21 } 22 };
3. 翻转链表
1 class Solution { 2 public: 3 //一个取巧的方法(如果允许使用额外空间):先遍历链表用栈存储元素值,然后重新遍历链表,将链表值置为栈顶。 4 //时间:O(n), 空间O(n) 5 ListNode* ReverseList(ListNode* pHead) { 6 stack<int> s; 7 auto p = pHead, q = pHead; 8 while(pHead != nullptr) 9 { 10 s.push(pHead->val); 11 pHead = pHead->next; 12 } 13 while(p != nullptr) 14 { 15 p->val = s.top(); 16 s.pop(); 17 p = p->next; 18 } 19 return q; 20 } 21 };
1 class Solution { 2 public: 3 //2. 真正地翻转链表,交换地址 4 ListNode* ReverseList(ListNode* pHead) { 5 if(pHead == nullptr) 6 return nullptr; 7 decltype(pHead) pre = nullptr; 8 auto next = pre; 9 ListNode *res = nullptr; 10 while(pHead != nullptr) 11 { 12 next = pHead->next; 13 if(next == nullptr) 14 res = pHead; 15 pHead->next = pre; 16 pre = pHead; 17 pHead = next; 18 } 19 return res; 20 } 21 };
4. 合并两个排序链表
1 class Solution { 2 public: 3 //非递归版本:双指针分别遍历两个链表 4 ListNode* Merge1(ListNode* pHead1, ListNode* pHead2) 5 { 6 if(pHead1 == nullptr) 7 return pHead2; 8 if(pHead2 == nullptr) 9 return pHead1; 10 ListNode *head = new ListNode(0); 11 head->next = nullptr; 12 ListNode *res = head; 13 while(pHead1 != nullptr && pHead2 != nullptr) 14 { 15 if(pHead1->val <= pHead2->val) 16 { 17 head->next = pHead1; 18 head = head->next; 19 pHead1 = pHead1->next; 20 } 21 else 22 { 23 head->next = pHead2; 24 head = head->next; 25 pHead2 = pHead2->next; 26 } 27 } 28 if(pHead1 != nullptr) 29 head->next = pHead1; 30 else 31 head->next = pHead2; 32 return res->next; 33 } 34 //递归版本 35 ListNode* Merge(ListNode* pHead1, ListNode* pHead2) 36 { 37 if(pHead1 == nullptr) 38 return pHead2; 39 if(pHead2 == nullptr) 40 return pHead1; 41 42 ListNode* head = nullptr; 43 if(pHead1->val <= pHead2->val) 44 { 45 head = pHead1; 46 head->next = Merge(pHead1->next, pHead2); 47 } 48 else 49 { 50 head = pHead2; 51 head->next = Merge(pHead2->next, pHead1); 52 } 53 return head; 54 } 55 };
5. 两个链表第一个公共节点
9 class Solution { 10 public: 11 //暴力法太低级,O(n2)不可接受 12 //使用unordered_map存储一个链表节点吧,时间O(n),空间O(n) 13 //unordered_map使用[]/insert插入,不是push_back() 14 ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) { 15 if(pHead1 == nullptr) 16 return nullptr; 17 if(pHead2 == nullptr) 18 return nullptr; 19 20 unordered_map<int, ListNode*> ump; 21 while(pHead1 != nullptr) 22 { 23 ump.insert({pHead1->val, pHead1}); 24 pHead1 = pHead1->next; 25 } 26 while(pHead2 != nullptr) 27 { 28 auto res = ump.find(pHead2->val); 29 if(res != ump.end()) 30 return res->second; 31 pHead2 = pHead2->next; 32 } 33 return nullptr; 34 } 35 };
6. 链表中环的入口节点(快2满1指针判断成环,再走一圈计算环长,快慢指针找到入口)
判断链表是否成环(快慢指针解决)
1 /* 2 struct ListNode { 3 int val; 4 struct ListNode *next; 5 ListNode(int x) : 6 val(x), next(NULL) { 7 } 8 }; 9 */ 10 class Solution { 11 public: 12 //判断成环及入口:快慢指针 13 ListNode* EntryNodeOfLoop(ListNode* pHead) 14 { 15 if(pHead == nullptr) 16 return nullptr; 17 ListNode *pMeet = judgeLoop(pHead); 18 if(pMeet == nullptr)//未成环 19 return nullptr; 20 auto p = pMeet; 21 int loopLen = 1; 22 //计算环长,相遇点再走一圈 23 while(p->next != pMeet) 24 { 25 p = p->next; 26 loopLen++; 27 } 28 auto q = pHead; 29 //后指针先走环长 30 while(loopLen--) 31 { 32 q = q->next; 33 } 34 //快慢一起走 35 p = pHead; 36 while(p != q) 37 { 38 p = p->next; 39 q = q->next; 40 } 41 return p; 42 } 43 //判断是否成环,快指针走两步-慢指针走一步,指针相遇必在环内 44 ListNode* judgeLoop(ListNode* pHead) 45 { 46 if(pHead == nullptr) 47 return nullptr; 48 auto pSlow = pHead->next; 49 if(pSlow == nullptr) 50 return nullptr; 51 auto pFast = pSlow->next; 52 while(pSlow != nullptr && pFast != nullptr) 53 { 54 if(pSlow == pFast) 55 return pSlow; 56 pSlow = pSlow->next; 57 pFast = pFast->next; 58 if(pFast != nullptr) 59 pFast = pFast->next; 60 } 61 return nullptr; 62 } 63 };
7. 删除链表重复节点(重复保留一个)
1 class Solution { 2 public: 3 //一次遍历,前后节点值相等,删除后一节点 4 ListNode* deleteDuplication(ListNode* pHead) 5 { 6 if(pHead == nullptr) 7 return nullptr; 8 auto pre = pHead; 9 auto cur = pre->next; 10 while(cur != nullptr) 11 { 12 while(cur->val == pre->val) 13 { 14 pre->next = cur->next; 15 cur = pre->next; 16 if(cur == nullptr) 17 return pHead; 18 } 19 pre = cur; 20 cur = cur->next; 21 } 22 return pHead; 23 } 24 };
8. 删除链表重复节点(重复节点不保留)
1 class Solution { 2 public: 3 /*创建一个头节点,它的next指向链表头,然后再用两个指针 4 一前一后来遍历链表,后一个指针判断有无重复并进行后移*/ 5 ListNode* deleteDuplication(ListNode* pHead) 6 { 7 if(pHead == nullptr) 8 return nullptr; 9 ListNode *first = new ListNode(0); 10 first->next = pHead; 11 ListNode *last = first; 12 ListNode *p = pHead; 13 while(p != nullptr && p->next != nullptr) 14 { 15 if(p->val == p->next->val)//有重复,需要删除 16 { 17 int val = p->val; 18 while(p != nullptr && p->val == val) 19 p = p->next; 20 last->next = p; 21 } 22 else 23 { 24 last = p; 25 p = p->next; 26 } 27 } 28 return first->next; 29 } 30 };
9. 判断两个链表是否交叉
(同样可使用一个unordered_map来存储一个链表中的节点指针,再遍历另外一个链表逐个查找)
1 bool FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) { 2 if(pHead1 == nullptr || pHead2 == nullptr) 3 return false; 4 unordered_map<ListNode*, int> ump; 5 while(pHead1 != nullptr) 6 { 7 ump.insert({pHead1, pHead1->val}); 8 pHead1 = pHead1->next; 9 } 10 while(pHead2 != nullptr) 11 { 12 auto res = ump.find(pHead2); 13 if(res != ump.end()) 14 return true; 15 pHead2 = pHead2->next; 16 } 17 return false; 18 }
10.相交链表
O(n)
1 class Solution { 2 public: 3 ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { 4 auto p = headA, q = headB; 5 while(p != q) 6 { 7 if(p == q) 8 return p; 9 p = (p == nullptr) ? headB : p->next; 10 q = (q == nullptr) ? headA : q->next; 11 } 12 return p; 13 } 14 };
说明:以上其他题目代码由牛客oj/leetcode通过,第9个未测试。
11.删除链表第n个节点(leetcode 19)
O(n)
1 class Solution { 2 public: 3 ListNode* removeNthFromEnd(ListNode* head, int n) { 4 int len = 0; 5 auto p = head; 6 while(p != nullptr)//计算表长 7 { 8 len++; 9 p = p->next; 10 } 11 if(n > len)//边界处理 12 return nullptr; 13 14 len = len-n-1;//先走len-n-1步 15 p = head; 16 if(len < 0)//删除头节点 17 return p->next; 18 19 while(len--)//删除其他节点 20 p = p->next; 21 p->next = p->next->next; 22 return head; 23 } 24 };
the end.