《面试算法 LeetCode 刷题班》—— 1. 链表

本文内容是基于小象学院——林沐 《面试算法 LeetCode 刷题班》,后期仍将对相关内容进行不定期更新!

1.链表

LeetCode 206 链表逆序 a (E)

问题描述:

已知链表头节点指针head,将链表逆序。

提交代码:

class Solution {
public:
	ListNode* reverseList(ListNode* head) {
		ListNode *new_head=NULL;
		while (head)
		{
			ListNode *next = head->next;
			head->next = new_head;
			new_head = head;
			head = next;
		}
		return new_head;
	}
};
LeetCode 92 链表逆序 b(M)

问题描述:

已知链表头节点指针head,将链表从位置 m 到 n 逆序。

提交代码:

class Solution {
public:
	ListNode* reverseBetween(ListNode* head, int m, int n) {
		int change_len = n - m + 1;
		ListNode *pre_head = NULL;
		ListNode *result = head;
		while (head && --m)
		{
			pre_head = head;
			head = head->next;
		}

		ListNode *modify_list_tail = head;
		ListNode *new_head = NULL;
		while (head && change_len)
		{
			ListNode *next = head->next;
			head->next = new_head;
			new_head = head;
			head = next;
			change_len--;
		}

		modify_list_tail->next = head;
		if (pre_head)
		{
			pre_head->next = new_head;
		}
		else {
			result = new_head;
		}

		return result;
	}
};
LeetCode 160 求两个链表的交点(E)

问题描述:

已知链表A 的头节点指针 headA,链表B 的头节点 headB, 两个链表相交,求两个链表交点对应的节点。
要求:
1.如果两个链表没有交点,则返回NULL
2.在求交点过程中,不可破坏链表的结构或者修改链表的数据域
3.确保链表A和B都没有环
4*.实现算法时间复杂度O(n),空间复杂度O(1)

算法思路1:用标准库 set 求交集

1.遍历链表A,将A中节点对应的指针(地址),插入 set
2.遍历链表B,将B中节点依次在 set 中查找,发现即为两个链表交点,未发现则返回NULL

代码实现:

class Solution {
public:
	ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
		set<ListNode*> tmp_set;
		while (headA)
		{
			tmp_set.insert(headA);
			headA = headA->next;
		}
		while (headB)
		{
			if (tmp_set.find(headB) != tmp_set.end()) {
				return headB;
			}
			else
			{
				headB = headB->next;
			}
		}
		return NULL;
	}
};

思路2: 空间复杂度O(1)

1.计算headA链表长度,计算链表B的链表长度,从而得出较长的链表多出的长度
2.将较长的链表的指针移动到和较短链表指针对齐的位置
3.headA 与 headB 同时移动,当两个指针指向同一个节点时,即找到了

代码实现:

int get_len(ListNode *head) {
	int len = 0;
	while (head)
	{
		len++;
		head = head->next;
	}
	return len;
}

class Solution {
public:
	ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
		int headA_len = get_len(headA);
		int headB_len = get_len(headB);
		if (headA_len > headB_len)
		{
			int dis = headA_len - headB_len + 1;
			while (headA && -- dis)
			{
				headA = headA->next;
			}
		}
		else if(headA_len < headB_len)
		{
			int dis = headB_len - headA_len + 1;
			while (headB && --dis)
			{
				headB = headB->next;
			}
		}
		while (headA && headB)
		{
			if (headA == headB) {
				return headA;
			}
			headA = headA->next;
			headB = headB->next;
		}
		return NULL;
	}
};
LeetCode 141 & 142 链表求环(M) (快慢指针问题)

问题描述:

141.已知链表中可能存在环,若有则返回,否则返回

142.已知链表中可能存在环,若有则返回环起始节点,否则返回NULL

思路1:用 set 求环起始节点

提交代码:141

class Solution {
public:
	bool hasCycle(ListNode *head) {
		set<ListNode *> tmp_set;
		while (head)
		{
			if (tmp_set.find(head) == tmp_set.end()) {
				tmp_set.insert(head);
				head = head->next;
			}
			else
			{
				return true;
			}
		}
		return false;
	}
};

提交代码:142

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
		set<ListNode *> tmp_set;
		while (head)
		{
			if (tmp_set.find(head) == tmp_set.end()) {
				tmp_set.insert(head);
				head = head->next;
			}
			else
			{
				return head;
			}
		}
		return NULL;
    }
};

思路2:快慢指针

首先设两个指针,一个slow指针每次前进1次,一个 fast 指针每次前进2次,然后当他们他们首次相遇时,从相遇点 meet 出发,和从 head 出发,速度相同,相遇时即为环的起点

class Solution {
public:
	ListNode *detectCycle(ListNode *head) {
		ListNode *slow = head;
		ListNode *fast = head;
		ListNode *meet = NULL;
		while (fast)
		{
			slow = slow->next;
			fast = fast->next;
			if (!fast)
			{
				break;
			}
			fast = fast->next;
			if (fast == slow)
			{
				meet = fast;
				break;
			}
		}
		if (meet == NULL)
		{
			return NULL;
		}
		while (head && meet)
		{
			if (head == meet) {
				return meet;
			}
			head = head->next;
			meet = meet->next;
		}
		return NULL;
	}
};
LeetCode 86 链表划分(M)

问题描述:

已知链表头指针head与数值x,将所有小于x的节点放在大于或等于x的节点前,且保持这些节点的原来的相对位置

提交代码:

class Solution {
public:
	ListNode* partition(ListNode* head, int x) {
		ListNode  less_head(0);
		ListNode  more_head(0);
		ListNode * less_ptr = &less_head;
		ListNode * more_ptr = &more_head;
		while (head)
		{
			if (head->val < x) {
				less_ptr->next = head;
				less_ptr = head;
			}
			else
			{
				more_ptr->next = head;
				more_ptr = head;
			}
			head = head->next;
		}
		less_ptr->next = more_head.next;
		more_ptr->next = NULL;
		return less_head.next;
	}
};
LeetCode 21 排序链表合并(2个)(E)

问题描述:

已知两个已排序链表头节点指针l1 与 l2,将这两个链表合并,合并后仍为有序的,返回合并后的头节点。

思路:
比较 l1 与 l2 指向的节点,将较小的节点插入到 pre 指针后,并向前移动较小的节点对应的指针。

class Solution {
public:
	ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
		ListNode tmp_node(0);
		ListNode *pre_node = &tmp_node;
		while (l1 && l2)
		{
			if (l1->val < l2->val)
			{
				pre_node->next = l1;
				l1 = l1->next;
			}
			else {
				pre_node->next = l2;
				l2 = l2->next;
			}
			pre_node = pre_node->next;
		}
		if (l1)
		{
			pre_node->next = l1;
		}

		if (l2)
		{
			pre_node->next = l2;
		}
		return tmp_node.next;
	}
};
LeetCode 23 排序链表合并(多个)(H)

问题描述:

已知K个已排序链表头节点指针,将这k个链表合并,合并后仍为有序的,返回合并后的头节点。

思路1:

按照上述方式,将k个链表按顺序合并 k-1 次, 可以通过但是效率很慢。

提交代码1:

class Solution {
public:

	ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
		ListNode tmp_node(0);
		ListNode *pre_node = &tmp_node;
		while (l1 && l2)
		{
			if (l1->val < l2->val)
			{
				pre_node->next = l1;
				l1 = l1->next;
			}
			else {
				pre_node->next = l2;
				l2 = l2->next;
			}
			pre_node = pre_node->next;
		}
		if (l1)
		{
			pre_node->next = l1;
		}
		if (l2)
		{
			pre_node->next = l2;
		}
		return tmp_node.next;
	}

	ListNode* mergeKLists(vector<ListNode*>& lists) {
		if (lists.size() == 0)
		{
			return NULL;
		}
		if (lists.size() == 1)
		{
			return lists[0];
		}
		for (int i = 0; i <= lists.size() - 2; i++)
		{
			lists[i + 1] = mergeTwoLists(lists[i], lists[i + 1]);
		}
		return lists[lists.size() - 1];
	}
};

思路2:

将 K*n 个节点放到 vector 中,再将vector排序,再将所有节点顺序相连。

提交代码2:**

bool cmp(const ListNode* A, const ListNode* B) {
		return A->val < B->val;
	}

class Solution {
public:

	ListNode* mergeKLists(vector<ListNode*>& lists) {
		vector<ListNode*> List_all;
		for (int i = 0; i < lists.size() ; i++)
		{
			while (lists[i])
			{
				List_all.push_back(lists[i]);
				lists[i] = lists[i]->next;
			}
		}
		if (List_all.size() == 0)
		{
			return NULL;
		}
		sort(List_all.begin(), List_all.end(),cmp);
		for (int i = 1; i < List_all.size(); i++)
		{
			List_all[i - 1]->next = List_all[i];
		}
		List_all[List_all.size() - 1]->next = NULL;
		return List_all[0];
	}
};

思路3:

对 k 个链进行分制,两两进行合并。

提交代码3:

class Solution {
public:
	ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
		ListNode tmp_node(0);
		ListNode *pre_node = &tmp_node;
		while (l1 && l2)
		{
			if (l1->val < l2->val)
			{
				pre_node->next = l1;
				l1 = l1->next;
			}
			else {
				pre_node->next = l2;
				l2 = l2->next;
			}
			pre_node = pre_node->next;
		}
		if (l1)
		{
			pre_node->next = l1;
		}
		if (l2)
		{
			pre_node->next = l2;
		}
		return tmp_node.next;
	}

	ListNode* mergeKLists(vector<ListNode*>& lists) {
		if (lists.size() == 0)
		{
			return NULL;
		}
		if (lists.size() == 1)
		{
			return lists[0];
		}

		int mid = lists.size() / 2;
		vector<ListNode*> sub1_lists;
		vector<ListNode*> sub2_lists;
		for (int i = 0; i < mid; i++)
		{
			sub1_lists.push_back(lists[i]);
		}
		for (int i = mid; i < lists.size(); i++)
		{
			sub2_lists.push_back(lists[i]);
		}
		ListNode *l1 = mergeKLists(sub1_lists);
		ListNode *l2 = mergeKLists(sub2_lists);
		
		return mergeTwoLists(l1, l2);
	}
};
LeetCode 138 有随机指针的链表拷贝(H)

问题描述:

已知一个复杂的链表,节点中有一个指向本链表任意某个节点的随机指针(也可以为空),求这个链表的深度拷贝。

思路:节点地址与节点序号对应

提交代码:

class Solution {
public:
	RandomListNode *copyRandomList(RandomListNode *head) {
		map<RandomListNode*, int> node_map;
		vector<RandomListNode *> node_vec;
		RandomListNode *ptr = head;
		int i = 0;
		while (ptr)
		{
			node_vec.push_back(new RandomListNode(ptr->label));
			node_map[ptr] = i;
			ptr = ptr->next;
			i++;
		}
		node_vec.push_back(0);
		ptr = head;
		i = 0;
		while (ptr)
		{
			node_vec[i]->next = node_vec[i+1];
			if (ptr->random)
			{
				int id = node_map[ptr->random];
				node_vec[i]->random = node_vec[id];
			}
			ptr = ptr->next;
			i++ ;
		}
		return node_vec[0];
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值