1.给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。要求返回这个链表的深拷贝。
解:为了实现在O(n)的时间复杂度内完成操作,可以先复制每一个结点到其后面,在复制其random指针,最后进行拆分
1.复制每一个结点,并接在原始结点的后面
2.根据原始结点复制random指针。
3.删除奇数位置的结点,连接偶数位置的结点。
代码:
#include <iostream>
using namespace std;
class Node {
public:
int val;
Node* next, *random;
Node() {}
Node(int val, Node* next, Node* random)
{
val = val;
next = next;
random = random;
}
};
class Solution {
public:
Node* copyRandomList(Node* head) {
//复制新的结点到每个原始结点的后面
//A->A'->B->B'->C->C'
if (!head)
return head;
Node* Current = head;
while (Current)
{
Node* newNode = new Node();
//复制当前结点到新结点上
newNode->val = Current->val;
newNode->random = NULL;
newNode->next = Current->next;
//更新当前的结点,并将新结点连接到当前节点的next后面
Current->next = newNode;
Current = newNode->next;
}
//复制random指针
//原始结点A的random指向C,则A'指向C'
Current = head;
while (Current)
{
Node* p_newNode = Current->next;//始终指向复制的结点
if (Current->random!=NULL)
{
p_newNode->random = Current->random->next;//复制random指针
}
Current = p_newNode->next;//更新当前指针
}
//奇数位置是原链表,偶数位置是复制后的链表,拆分即可
Current = head;
Node* Even_Head_node = NULL;
Node* Even_Node = head;
if (Current)
{
Even_Head_node = Current->next;
Even_Node = Current->next;
Current->next = Even_Head_node->next;
Current = Current->next;
}
while (Current)
{
Even_Node->next = Current->next;
Even_Node = Even_Node->next;
Current->next = Even_Node->next;
Current = Current->next;
}
return Even_Head_node;
}
};
int main()
{
Node *head = new Node();
head->val = 1;
head->next = new Node();
head->random = head->next;
}
2.给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos
是 -1
,则在该链表中没有环。
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
解:有环与无环的唯一区别是经过若干次循环后,有环的指针会回到原处,因此可以设置两个指针,一个以1的步进移动,另一个指针以2的步进移动,若干次循环后发现它们地址相同则代表有环。否则无环
代码:
#include <iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
bool hasCycle(ListNode*head)
{
ListNode*p1, *p2;
p1 = head;
p2 = head;
while (p1&&p2->next&&p2->next->next)
{
p1 = p1->next;
p2 = p2->next->next;
if (p1 == p2)
return 1;
}
return 0;
}
};
int main()
{
ListNode *head = new ListNode(2);
head->next = new ListNode(3);
}
3.在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:输入: 4->2->1->3 输出: 1->2->3->4
解:采用归并排序,通过设置两个指针,找到链表的一半
代码:
#include <iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
ListNode* sortList(ListNode* head) {
return mergeSortList(head);
}
ListNode* mergeSortList(ListNode* head)
{
ListNode *slow = head;
ListNode *fast = head;
ListNode *pre = slow;
if (head==NULL||head->next==NULL)
return head;
while (fast!=NULL&&fast->next!=NULL)
{
pre = slow;
slow = slow->next;//fast走完时slow走一半
fast = fast->next->next;
}
pre->next = NULL;
ListNode* l = mergeSortList(head);
ListNode* r = mergeSortList(slow);
return mergeList(l, r);
}
ListNode* mergeList(ListNode*l, ListNode* r)
{
if (!l) return r;
if (!r) return l;
if (l->val<r->val)
{
l->next = mergeList(l->next, r);
return l;
}
else
{
r->next = mergeList(l, r->next);
return r;
}
}
};
int main()
{
ListNode *head = new ListNode(2);
head->next = new ListNode(3);
head->next->next = new ListNode(4);
Solution s1;
ListNode* r = s1.sortList(head);
}
4.编写一个程序,找到两个单链表相交的起始节点。
解:计算出每个链表的长度,并求出长度差,让较长的链表先移动diff个单位,使得我们起始到结束的长度一样,再进行while循环,比较两个结点地址是否一样,一样则代表为相交的起始节点。
代码:
#include <iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int len1=0, len2=0;
ListNode* H1 = headA;
ListNode* H2 = headB;
if (!headA || !headB)
return NULL;
while (H1)
{
++len1;
H1 = H1->next;
}
while (H2)
{
++len2;
H2 = H2->next;
}
H1 = headA;
H2 = headB;
int diff;
if (len1 > len2)
{
diff = len1 - len2;
for (int i = 0; i < diff; i++)
H1 = H1->next;
}
else
{
diff = len2 - len1;
for (int i = 0; i < diff; i++)
H2 = H2->next;
}
while (H1 && (H2))//此时两个链表的长度一样
{
if (H1==H2)
return H1;
H1 = H1->next;
H2 = H2->next;
}
return NULL;
}
};
int main()
{
ListNode *head1 = new ListNode(2);
head1->next = new ListNode(3);
head1->next->next = new ListNode(4);
ListNode *head2 = new ListNode(2);
head2->next = head1->next;
Solution s1;
ListNode* r = s1.getIntersectionNode(head1, head2);
cout << r->val << endl;
}
5.反转一个单链表。
示例:输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
解:翻转链表即把头结点作为尾节点,尾节点作为头结点,因此需要一个变量来保存前一个结点的信息,当前结点指向前一个结点,当前结点的下一个结点指向当前结点。
#include <iostream>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* Current = head;
ListNode* Prev = NULL;
ListNode* Next = NULL;
ListNode* Reverse_Head = NULL;
while (Current)
{
Next = Current->next;
if (Next == NULL)
Reverse_Head = Current;
Current->next = Prev;//反转后前一个结点为当前结点的下一个节点
Prev = Current;//更新前一个结点
Current = Next;
}
return Reverse_Head;
}
};
int main()
{
ListNode *head1 = new ListNode(2);
head1->next = new ListNode(3);
head1->next->next = new ListNode(4);
Solution s1;
ListNode* r = s1.reverseList(head1);
cout << r->val << endl;
}
6.请判断一个链表是否为回文链表。
示例 1:输入: 1->2->2->1 输出: true
解:判断回文链表,即判断首尾元素是否相等,,首元素可以利用链表移动尾元素可以利用栈来进行弹出。
代码:
#include <iostream>
#include<stack>
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode* Current = head;
stack<int> s;
int len = 0;
while (Current)
{
++len;
s.push(Current->val);
Current = Current->next;
}
Current = head;
for (int i = 0; i < len/2; i++)
{
if (s.top() != Current->val)
{
return 0;
}
s.pop();
Current = Current->next;
}
return 1;
}
};
int main()
{
ListNode *head1 = new ListNode(2);
head1->next = new ListNode(3);
head1->next->next = new ListNode(4);
Solution s1;
cout<<s1.isPalindrome(head1);
}
7.请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
解:给的结点是需要删除的结点,没有给头结点,因此只能通过复制下一个结点来代表删除节点。
代码:
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
};
int main()
{
ListNode *head1 = new ListNode(2);
head1->next = new ListNode(3);
head1->next->next = new ListNode(4);
Solution s1;
s1.deleteNode(head1->next);
}
8.给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
解:偶节点始终在奇节点后面,因此定义两个指针分别指向偶节点和奇节点,进行间隔指向。
代码:
using namespace std;
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) :val(x), next(NULL) {}
};
class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
if (!head)
return head;
ListNode *odd = head;
ListNode* even = head->next;
ListNode* evenHead = even;
while (odd->next&&even->next)
{
odd->next = even->next;
odd = odd->next;
even->next = odd->next;
even = even->next;
}
odd->next = evenHead;
return head;
}
};
int main()
{
ListNode *head1 = new ListNode(2);
head1->next = new ListNode(3);
head1->next->next = new ListNode(4);
Solution s1;
ListNode* res=s1.oddEvenList(head1);
}