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);
}