5.力扣2018年常見編程題總結(鏈表)

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

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章