剑指offer-链表相关

剑指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.

posted @ 2019-08-11 17:35  yocichen  阅读(311)  评论(0编辑  收藏  举报