Algorithm problem of linked list

Table of contents

Question type 1. Cloning a linked list containing a rand pointer

Written Exam: Hash Table

Interview: Simulate the function of a hash table without a container

Question type 2. Given a single-link list head node Head, determine whether it constitutes a palindrome

Question type 3. Divide the singly linked list according to a certain value into small on the left, equal in the middle, and large on the right

6 variables, no container, but also ensure stability

Question type 4. Find the first intersecting node of two linked lists

1. First find the entry points of the two linked lists

2. Discuss the following situations

Question type 5. Do not give the head node, and ask to delete a given node?

 1. Clever Method: Borrowing a Corpse to Resurrect the Soul

Topic 6. The nodes in the linked list are flipped every k groups


For the written test: don't care too much about the space complexity, all for the time complexity.

For interviews: Time complexity is still the first priority, but we must find the most space-saving method.

Question type 1. Cloning a linked list containing a rand pointer

Written Exam: Hash Table

 code:

	Node* Hashfunc(Node* head)
	{
		unordered_map<Node*,Node*> Node_map;
		Node* cur = head;
		while (cur!= nullptr)
		{
			Node* new_cur = new Node(cur->value);
			
			Node_map.insert(make_pair(cur, new_cur));
			cur = cur->next;
		}
		cur = head;
		while (cur != nullptr)
		{
			Node_map.find(cur)->second->next = cur->next;
			Node_map.find(cur)->second->rand = cur->rand;
			cur = cur->next;
		}
		return Node_map.find(head)->second;
	}

Interview: Simulate the function of a hash table without a container

	Node* copyRandomList2(Node* head) {
		if (head == nullptr) {
			return nullptr;
		}
		Node* cur = head;
		Node* next = nullptr;
		// 1 -> 2 -> 3 -> nullptr
		// 1 -> 1' -> 2 -> 2' -> 3 -> 3'
		while (cur != nullptr) {
			next = cur->next;
			cur->next = new Node(cur->value);
			cur->next->next = next;
			cur = next;
		}
		cur = head;
		Node* copy = nullptr;
		// 1 1' 2 2' 3 3'
		// 依次设置 1' 2' 3' random指针
		while (cur != nullptr) {
			next = cur->next->next;
			copy = cur->next;
			copy->rand = cur->rand != nullptr ? cur->rand->next : nullptr;
			cur = next;
		}
		Node* res = head->next;
		cur = head;
		// 老 新 混在一起,next方向上,random正确
		// next方向上,把新老链表分离
		while (cur != nullptr) {
			next = cur->next->next;
			copy = cur->next;
			cur->next = next;
			copy->next = next != nullptr ? next->next : nullptr;
			cur = next;
		}
		return res;
	}

Question type 2. Given a single-link list head node Head, determine whether it constitutes a palindrome

written examination:

Traverse in turn, put it on the stack , and then pop it out for comparison

interview:

1. First find the middle node through the fast and slow pointer, and the next of the middle node points to null

2. Then reverse the second half and save the last node.

3. After the reverse order, traverse and compare from L and R in turn, and return if there is any difference

4. Regardless of whether the palindrome is satisfied or not, you must remember to restore the structure of the linked list to its original state at the end

 Code:

class FindLink_Mid;
class Node
{
	friend class FindLink_Mid;
public:
	Node(int v)
	{
		value = v;
	}
private:
	int value;	
	Node* next;
};

class FindLink_Mid
{

public:
	//奇数返回中点偶数返回上中点
	Node* midOrUpMidNode(Node* head) 
	{
		if (head == nullptr || head->next == nullptr || head->next->next == nullptr) {
			return head;
		}
		// 链表有3个点或以上
		Node* slow = head->next;
		Node* fast = head->next->next;
		while (fast->next != nullptr && fast->next->next != nullptr) 
		{
			slow = slow->next;
			fast = fast->next->next;
		}
		return slow;
	}
	//奇数返回中点,偶数返回下中点
	Node* midOrDownMidNode(Node* head) 
	{
		if (head == nullptr || head->next == nullptr) 
		{
			return head;
		}
		Node* slow = head->next;
		Node* fast = head->next;
		while (fast->next != nullptr && fast->next->next != nullptr)
		{
			slow = slow->next;
			fast = fast->next->next;
		}
		return slow;
	}
	//奇数返回中点前一个,偶数返回上中点前一个
	Node* midOrUpMidPreNode(Node* head) {
		if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
		{
			return nullptr;
		}
		Node* slow = head;
		Node* fast = head->next->next;
		while (fast->next != nullptr && fast->next->next != nullptr)
		{
			slow = slow->next;
			fast = fast->next->next;
		}
	}

	//奇数返回中点前一个,偶数返回下中点前一个
	Node* midOrDownMidPreNode(Node* head) 
	{
		if (head == nullptr || head->next == nullptr)
		{
			return head;
		}
		if (head->next->next == nullptr)
		{
			return head;
		}
		Node* slow = head;
		Node* fast = head->next;
		while (fast->next != nullptr && fast->next->next != nullptr) {
			slow = slow->next;
			fast = fast->next->next;
		}
		return slow;
	}

};

Question type 3. Divide the singly linked list according to a certain value into small on the left, equal in the middle, and large on the right

written examination:

Put the linked list in the array, and then go to partition

interview:

6 variables, no container, but also ensure stability

Schematic diagram of the process:

We need to construct three regions first, and then string the three regions together

Code:

class SmallerEqualBigger
{
public:
	Node* listPartition2(Node* head, int pivot) 
	{
		Node* sH = nullptr; // small head
		Node* sT = nullptr; // small tail
		Node* eH = nullptr; // equal head
		Node* eT = nullptr; // equal tail
		Node* mH = nullptr; // big head
		Node* mT = nullptr; // big tail
		Node* next = nullptr; // save next node
		// every node distributed to three lists
		while (head != nullptr)
		{
			next = head->next;
			head->next = nullptr;
			if (head->value < pivot) 
			{
				if (sH == nullptr) 
				{
					sH = head;
					sT = head;
				}
				else 
				{
					sT->next = head;
					sT = head;
				}
			}
			else if (head->value == pivot) 
			{
				if (eH == nullptr) 
				{
					eH = head;
					eT = head;
				}
				else {
					eT->next = head;
					eT = head;
				}
			}
			else {
				if (mH == nullptr) 
				{
					mH = head;
					mT = head;
				}
				else {
					mT->next = head;
					mT = head;
				}
			}
			head = next;
		}
		// 小于区域的尾巴,连等于区域的头,等于区域的尾巴连大于区域的头
		if (sT != nullptr) 
		{ // 如果有小于区域
			sT->next = eH;
			eT = eT == nullptr ? sT : eT; // 下一步,谁去连大于区域的头,谁就变成eT
		}
		// 下一步,一定是需要用eT 去接 大于区域的头
		// 有等于区域,eT -> 等于区域的尾结点
		// 无等于区域,eT -> 小于区域的尾结点
		// eT 尽量不为空的尾巴节点
		if (eT != nullptr) 
		{ // 如果小于区域和等于区域,不是都没有
			eT->next = mH;
		}
		//return sH != nullptr ? sH : (eH != nullptr ? eH : mH);
		return sH;
	}
public:
	void printList(Node* node)
	{
		cout<<"Linked List: ";
		while (node != nullptr) 
		{
			cout<<node->value <<" ";
			node = node->next;
		}
		cout << endl;
	}

Question type 4. Find the first intersecting node of two linked lists

This problem and the Joseph ring problem are called the two nightmares of linked lists

1. First find the entry points of the two linked lists

1. Use set to find the entry point directly

2. Use the fast and slow pointer

Write a function to achieve this function

// 找到链表第一个入环节点,如果无环,返回nullptr
Node* GetLoopNode(Node* head)
{
	if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
	{
		return nullptr;
	}
	// 快慢指针
	Node* slow = head->next; 
	Node* fast = head->next->next; 
	while (slow != fast)
	{
		if (fast->next == nullptr || fast->next->next == nullptr)
			return nullptr;//说明这个链表就没有环

		slow = slow->next;
		fast = fast->next->next;
	}
	//这时候是找到了第一个相遇的位置
	fast = head;
	while (slow != fast)//第二次是两个指针都一次走一步
	{
		slow = slow->next;
		fast = fast->next;
	}
	return fast;
}

2. Discuss the following situations

We already have a function that finds the first entry point, head1 corresponds to loop1, head is loop2

2.1、loop1==nullptr && loop2==nullptr

 1. You can use the hash table to check the first intersecting part

Put a linked list into the set, and then traverse the second linked list to check whether it is in the set

2. It is also possible to use no container

 code:

// 如果两个链表都无环,返回第一个相交节点,如果不相交,返回nullptr
Node* NoLoop(Node* head1, Node* head2) {
	if (head1 == nullptr || head2 == nullptr) {
		return nullptr;
	}
	Node* cur1 = head1;
	Node* cur2 = head2;
	int n = 0;
	while (cur1->next != nullptr)
	{
		n++;
		cur1 = cur1->next;
	}
	while (cur2->next != nullptr)
	{
		n--;
		cur2 = cur2->next;
	}
	if (cur1 != cur2) {//说明没有相交的部分
		return nullptr;
	}
	if (n < 0)
	{
		cur1 = head2;
		cur2 = head1;
	}
	// n  :  链表1长度减去链表2长度的值
	cur1 = n > 0 ? head1 : head2; // 谁长,谁的头变成cur1
	cur2 = cur1 == head1 ? head2 : head1; // 谁短,谁的头变成cur2
	n = abs(n);
	while (n)
	{
		cur1 = cur1->next;
		n--;
	}
	while (cur1 != cur2)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	return cur1;
}

2.2. Both linked lists have rings (because it is impossible for one to have a ring and one not to have a ring)

If two linked lists intersect, they must share a ring

 

 Situation (2) is very easy to do, it is almost the same as the above method of acyclic linked list,

You can ignore this ring directly, just regard the end of the two linked lists as loop1, and then do the same as above

(1) and (2) can also be distinguished

A pointer points to loop1->next, and then traverses a circle to see if there is loop2, if there is (3) return both loop1 and loop2,

If not, (2) return nullptr directly

// 两个有环链表,返回第一个相交节点,如果不想交返回nullptr
Node* BothLoop(Node* head1, Node* loop1, Node* head2, Node* loop2) {
	Node* cur1 = nullptr;
	Node* cur2 = nullptr;
	if (loop1 == loop2)
	{
		cur1 = head1;
		cur2 = head2;
		int n = 0;
		while (cur1->next != loop1)
		{
			n++;
			cur1 = cur1->next;
		}
		while (cur2->next != loop2)
		{
			n--;
			cur2 = cur2->next;
		}
		cur1 = n > 0 ? head1 : head2;//让cur1表示长的,cur2表示短的
		cur2 = cur1 == head1 ? head2 : head1;
		n = abs(n);
		while (n) {
			cur1 = cur1->next;
			n--;
		}
		while (cur1 != cur2) {
			cur1 = cur1->next;
			cur2 = cur2->next;
		}
		return cur1;
	}
	else
	{
		cur1 = cur1->next;
		while (cur1 != loop1)
		{
			if (cur1 == loop2)
				return loop1;
		}
		return nullptr;
	}
}

Question type 5. Do not give the head node, and ask to delete a given node?

 1. Clever Method: Borrowing a Corpse to Resurrect the Soul

 But this method has many problems

1. If the copy function is difficult to call, it will hang up

2. The last node can never be deleted

So this is not acceptable. To delete a node, you must give the head node

Topic 6. The nodes in the linked list are flipped every k groups

 

ListNode* reverseKGroup(ListNode* head, int k) {
        int n = 0;
        for (ListNode *cur = head; cur; cur = cur->next)
            ++n; // 统计节点个数

        ListNode *dummy = new ListNode(0);
        dummy->next=head;
        ListNode *p0=dummy;
        ListNode *pre=nullptr,*cur=p0->next;
        while(n>=k){
            n-=k;
            for(int i=0;i<k;++i){
            ListNode* next=cur->next;
            cur->next=pre;
            pre=cur;
            cur=next;
        }
        ListNode* next=p0->next;
        p0->next->next=cur;
        p0->next=pre;
        p0=next;
        }
       
        return dummy->next;
        }

Guess you like

Origin blog.csdn.net/flyingcloud6/article/details/128690978