LeetCode-链表
LeetCode 206 - Reverse Linked List - 反转链表 - easy
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
三步:备份、更新、移动。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode * newHead = NULL;
ListNode *next = NULL;
while(head)
{
next = head->next;
head->next = newHead;
newHead = head;
head = next;
}
return newHead;
}
};
LeetCode 92 - Reverse Linked List II - 反转链表 2 - medium
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
如示例所示,需要保存1和5。
其次,要考虑m=1的情况。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
int nReverseLen = n - m + 1;
// if m != 1, return head
ListNode *result = head;
// find (m - 1) and m
ListNode *preHead = NULL;
while(head && --m)
{
// if m == 1, preHead == NULL
preHead = head;
head = head->next;
}
ListNode *newHead = NULL;
// find n+1
ListNode *tail = head;
while(head && nReverseLen--)
{
ListNode *next = head->next;
head->next = newHead;
newHead = head;
head = next;
}
// 2->5
tail->next = head;
if (preHead)
{
preHead->next = newHead;
}
else
{
// m == 1
result = newHead;
}
return result;
}
};
LeetCode 160 - Intersection of Two Linked Lists - 相交链表 - easy
编写一个程序,找到两个单链表相交的起始节点。
- 如果两个链表没有交点,返回 null.
- 在返回结果后,两个链表仍须保持原有的结构。
- 可假定整个链表结构中没有循环。
- 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
解法1
可以借助set容器保存一个链表节点的地址,再和另一链表进行节点地址匹配。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
std::set<ListNode*> setNode;
while(headA)
{
setNode.insert(headA);
headA = headA->next;
}
while(headB)
{
if ( setNode.find(headB) != setNode.end() )
{
return headB;
}
headB = headB->next;
}
return NULL;
}
};
方法2,空间复杂度O(1)
获取两个链表长度,用两个指针指向距离末尾节点长度为n的节点,n为短链表长度,然后遍历。
class Solution {
public:
int getListLen(ListNode *head)
{
int nLen = 0;
while(head)
{
head = head->next;
++nLen;
}
return nLen;
}
ListNode* forwardLongList(int nDelta, ListNode *head)
{
while(head && nDelta)
{
head = head->next;
--nDelta;
}
return head;
}
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
int nLenA = getListLen(headA);
int nLenB = getListLen(headB);
if(nLenA > nLenB)
{
headA = forwardLongList(nLenA - nLenB, headA);
}
else if(nLenA < nLenB)
{
headB = forwardLongList(nLenB - nLenA, headB);
}
while(headA && headB)
{
if (headA == headB)
{
return headA;
}
headA = headA->next;
headB = headB->next;
}
return NULL;
}
};
LeetCode 141/142 - Linked List Cycle - easy
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
解法1
使用set容器。
class Solution {
public:
bool hasCycle(ListNode *head) {
std::set<ListNode*> setNode;
while(head)
{
if(setNode.find(head->next) != setNode.end())
{
return true;
}
setNode.insert(head);
head = head->next;
}
return false;
}
};
解法2 快慢指针
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head;
ListNode *slow = head;
ListNode *meet = NULL;
while(fast)
{
//先各走一步
fast = fast->next;
slow = slow->next;
if (!fast)
{
return NULL;
}
fast = fast->next;
if(fast == slow)
{
meet = fast;
break;
}
}
if (!meet) return NULL;
while(head)
{
if(head == meet)
{
return head;
}
head = head->next;
meet = meet->next;
}
return NULL;
}
};
LeetCode 86 - Partition List - 分隔链表 - medium
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5
需要借助局部临时节点。
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode bigList(0);
ListNode smallList(0);
ListNode *pBigList = &bigList;
ListNode *pSmallList = &smallList;
while(head)
{
if(head->val < x)
{
pSmallList->next = head;
pSmallList = head;
}
else
{
pBigList->next = head;
pBigList = head;
}
head = head->next;
}
pBigList->next = NULL;
pSmallList->next = bigList.next;
return smallList.next;
}
};
LeetCode 138 - Copy List with Random Pointer - 复制带随机指针的链表 - medium
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
- val:一个表示 Node.val 的整数。
- random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
难点在于,拷贝随机指针。
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
可以多耗费点空间,用map存储[node, ord]
,即节点地址和序号键值对,然后用vector保存新链表各个节点地址(可以看成[ord, node]
)。
class Solution {
public:
Node* copyRandomList(Node* head) {
std::map<Node*, int> mapNode;
std::vector<Node*> vecNode;
Node *pNode = head;
int i = 0;
while(pNode)
{
vecNode.push_back(new Node(pNode->val));
// map address to ord
mapNode[pNode] = i++;
pNode = pNode->next;
}
vecNode.push_back(NULL);
// 连接next, random
pNode = head;
i = 0;
while(pNode)
{
vecNode[i]->next = vecNode[i+1];
if(pNode->random)
{
int nTmp = mapNode[pNode->random];
vecNode[i]->random = vecNode[nTmp];
}
pNode = pNode->next;
++i;
}
return vecNode[0];
}
};
LeetCode 21 - Merge Two Sorted Lists - easy
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
和86题一样,像这种返回新链表的函数,都需要借助临时头节点。
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode preHead(0);
ListNode *pNode = &preHead;
while(l1 && l2)
{
if( l1->val < l2->val )
{
pNode->next = l1;
l1 = l1->next;
}
else
{
pNode->next = l2;
l2 = l2->next;
}
pNode = pNode->next;
}
if (l1)
{
pNode->next = l1;
}
if (l2)
{
pNode->next = l2;
}
return preHead.next;
}
};
LeetCode 23 - Merge k Sorted Lists - hard
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
方法1是两两合并,即合并k-1次,但假设平均每个链表n各节点,(n+n) + (2n+n) + .. + ((k-1n) + n)
,会超时。
方法2是将kn各节点放在vector中排序,然后链接。排序最优复杂度就是O(kn logkn)
.
class Solution {
public:
static bool cmp(const ListNode *a, const ListNode *b)
{
return a->val < b->val;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
std::vector<ListNode*> vecNode;
for(int nSize = lists.size(), i = 0; i < nSize; ++i)
{
ListNode *pNode = lists[i];
while(pNode)
{
vecNode.push_back(pNode);
pNode = pNode->next;
}
}
if(vecNode.empty()) return NULL;
std::sort(vecNode.begin(), vecNode.end(), cmp);
for(int nSize = vecNode.size(), i = 1; i < nSize; ++i)
{
vecNode[i - 1]->next = vecNode[i];
}
vecNode.back()->next = NULL;
return vecNode[0];
}
};
最优方法是对k个链表分治,两两合并。
- 第一轮,处理k/2个链表,每次2n个节点;
- 第二轮,处理k/4个链表,每次4n个节点;
- 。。。
- 最后一轮,处理
k/(2^logk)
次,每次n*2^logk
个节点。
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode preHead(0);
ListNode *pNode = &preHead;
while(l1 && l2)
{
if( l1->val < l2->val )
{
pNode->next = l1;
l1 = l1->next;
}
else
{
pNode->next = l2;
l2 = l2->next;
}
pNode = pNode->next;
}
if (l1)
{
pNode->next = l1;
}
if (l2)
{
pNode->next = l2;
}
return preHead.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int nSize = lists.size();
if (nSize == 0) return NULL;
if (nSize == 1) return lists[0];
if (nSize == 2) return mergeTwoLists(lists[0], lists[1]);
int nMid = nSize / 2;
std::vector<ListNode*> subList_1, subList_2;
for(int i = 0; i < nMid; ++i)
{
subList_1.push_back(lists[i]);
}
for(int i = nMid; i < nSize; ++i)
{
subList_2.push_back(lists[i]);
}
ListNode *l1 = mergeKLists(subList_1);
ListNode *l2 = mergeKLists(subList_2);
return mergeTwoLists(l1, l2);
}
};