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

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值