Common algorithms of linked list

The link list node is declared as follows:

struct LinkList {
	int value;
	LinkList *next;
};

The following is the operation of single chain table without leading node.

1. Establish single chain table according to input

Insert the input node into the head of the list.

//Building a single linked list based on input: linked list head insertion
LinkList *BuildList() {
	LinkList *head = NULL;
	int data;
	int i = 0;
	while (scanf("%d", &data) != EOF) {
		//scanf("%d", &data);++i;
		LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
		if (NULL == new_node) {
			fprintf(stderr, "malloc failed");
			return head;
		}
		new_node->value = data;
		if (head == NULL) {
			new_node->next = NULL;
			head = new_node;
		}
		else {
			new_node->next = head;
			head = new_node;
		}
	}
	return head;
}

2. Insertion of linked list

When inserting the list, pay attention to the case when the list is empty.

(1) insert in the chain header

//Inserting nodes in the head of a linked list
LinkList *InsertToHead(int value, LinkList *head) {
	LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
	if (new_node == NULL) {
		fprintf(stderr, "malloc failed");
		return head;
	}
	new_node->value = value;
	new_node->next = NULL;
	if (head == NULL) {
		head = new_node;
	}
	else {
		new_node->next = head;
		head = new_node;
	}
	return head;
}

(2) insert at the end of the list

//Insert node at the end of linked list
LinkList *InsertToTail(int value, LinkList *head) {
	LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
	if (new_node == NULL) {
		fprintf(stderr, "malloc failed");
		return head;
	}
	new_node->value = value;
	new_node->next = NULL;

	if (head == NULL)
		head = new_node;
	else {
		LinkList *pnode = head;
		while (pnode->next != NULL)
			pnode = pnode->next;
		pnode->next = new_node;
	}
	return head;
}

3. Delete linked list

Note the deletion when the list has only one node.

//Delete a node
LinkList *DeletebyValue(int value, LinkList* head) {
	if (head == NULL)
		return head;
	LinkList *pToDelete = NULL;
	if (head->value == value) {
		pToDelete = head;
		head = head->next;
	}
	else {
		LinkList *p = head;
		while (p->next != NULL && p->next->value != value)
			p = p->next;
		if (p->next != NULL) {
			pToDelete = p->next;
			p->next = pToDelete->next;
		}
	}
	if (pToDelete != NULL) {
		free(pToDelete);
		pToDelete = NULL;
	}
	return head;
}

4. Find the number of nodes in a single chain table

Note to check if the list is empty. The time complexity is O (n). This operation does not need to check whether the linked list is empty. The following code will return 0 * * if the linked list is empty. * *

unsigned int Length(LinkList *head) {
	unsigned int length = 0;
	LinkList *p = head;
	while (p) {
		++length;
		p = p->next;
	}
	return length;
}

5. Print linked list elements

(1) forward printing of linked list

//Print single chain table
void PrintList(LinkList *head) {
	LinkList *p = head;
	while (p) {
		printf("%d ", p->value);
		p = p->next;
	}
	printf("\n");
}

(2) print the linked list in reverse order

//Printing single linked tables in reverse order: non recursive
void RPrintList(LinkList* head) {
	if (NULL == head)
		return;
	stack<int> list_stack;
	while (head) {
		list_stack.push(head->value);
		head = head->next;
	}
	while (!list_stack.empty()) {
		printf("%d ", list_stack.top());
		list_stack.pop();
	}
	printf("\n");
}

//Printing single linked tables in reverse order: recursion
void RPrintListRecursively(LinkList* head) {
	if (NULL == head)
		return;
	else {
		RPrintListRecursively(head->next);
		printf("%d ", head->value);
	}
}

6. Sorting elements of single chain table

Because a single chain table can only get the next element through its next pointer, the sorting method of a single chain table can only be backward traversal, for example Improvement of bubble sorting . Instead, we can't use algorithms such as insert sort, fast sort and so on. The following algorithm is based on bubble sorting. One pass sorting places the largest element in the unsorted subsequence in the head of the sorted subsequence of the linked list (this maximum element is the smallest value in the sorted subsequence).

//Bubble sort single chain table
LinkList* Sort(LinkList *head) {
	if (NULL == head)
		return head;
	int length = Length(head);
	int unorder_size = length;
	int flag = 1;
	while (flag) {
		flag = 0;
		LinkList *p = head;
		for (int i = 0; p->next && i < unorder_size; ++i) {
			if (p->value > p->next->value) {
				int temp = p->value;
				p->value = p->next->value;
				p->next->value = temp;
				flag = 1;
			}
			p = p->next;
		} 
		--unorder_size;
	}
	return head;
}

7. Single chain table turning

Traverse the original list from the beginning to the end. Each node is traversed and placed at the front of the new list. Note that the list is empty and has only one node. Time complexity is O (n)

//Flip single chain table
LinkList* ReverseList(LinkList *head) {
	//In fact, if judges the case when the number of nodes in the list is less than two
	//It's not necessary, because the following program contains the judgment here
	if (NULL == head || NULL == head->next){
		return head;
	}
	LinkList *reverse_head = NULL;  //Head pointer of the list after flipping
	LinkList *pcurrent = head;      //Pccurrent traversal list
	while (pcurrent) {
		LinkList* temp = pcurrent;
		pcurrent = pcurrent->next;
		temp->next = reverse_head;
		reverse_head = temp;
	}
	return reverse_head;
}

8. Find the last K node in the single chain table (k > 0)

The easiest way to think of: first count the number of nodes in a single chain table, and then find the (n-k) node. Note that the list is empty, K is 0, K is 1, and K is greater than the number of nodes in the list. The time complexity is O (n).

Another way of thinking: you only need to traverse a single chain table once.

The main idea is to use two pointers, first let the front pointer go to the positive k-th node, so that the distance difference between the front and back pointers is k-1, then the front and back pointers go forward together, when the front pointer goes to the last node, the back pointer refers to the reverse k-th node.

Note that the number of nodes in the list is less than k.

//Find the last K node of the list (k > 0)
LinkList * GetRKthNode(LinkList *head, int k) {
	if (k < 1 || NULL == head)
		return NULL;
	LinkList *first = head;
	LinkList *second = head;

	//NULL if the number of nodes is less than k
	for (int i = 1; i < k; ++i) {
		if (second->next)
			second = second->next;
		else
			return NULL;
	}

	//The two pointers move at the same time until the fast second pointer reaches the last node,
	//At this time, the slow first pointer points to the last k node
	while (second->next != NULL) { 
		first = first->next;
		second = second->next;
	}
	return first;
}

9. Find the middle node of single chain table

This question can be used for similar ideas in the previous one. It is also used to set two pointers, but here are two pointers going forward at the same time. The first one goes two steps at a time, and the second one goes one step at a time. When the first one goes to the last node, the latter one refers to the middle node: the n / 2 + 1 node.

When the number of linked list elements is even, the code returns n/2 + 1 nodes. This should be discussed: it may need to return the middle two nodes or their average values.

Note that the list is empty and the number of nodes in the list is 1 and 2. Time complexity O (n):

//Returns the intermediate node of a single linked list, when the number of linked list nodes is even
//This function returns the n/2 + 1 node of the list
LinkList *GetMiddleNode (LinkList* head) {
	//The list is empty or has only one node
	if (NULL == head || NULL == head->next)
		return head;

	LinkList *first = head;
	LinkList *second = head;
	while (second->next) {
		first = first->next;
		second = second->next;
		if (second->next) {  //It is special when the number of nodes is even
			second = second->next;
		}
	}
	return first;
}

10. Merge two ordered single chain tables

After merging, the linked list is still in order. The following code keeps the same value elements. * * * * *

This is similar to merge sort. In particular, pay attention to the case that both lists are empty, and when one of them is empty. Only space of O (1) is needed. The time complexity is O (max(len1, len2)).

//Merging two ordered single chain tables: non recursive
//The original equal elements of the linked list are retained
LinkList* MergeList(LinkList* heada, LinkList *headb) {
	if (NULL == heada)
		return headb;
	if (NULL == headb)
		return heada;

	LinkList *head_merge = NULL;

	//Compare the size of the first node, and take the smaller one as the first node after merging
	if (heada->value <= headb->value) {
		head_merge = heada;
		//Note that the order of the following two statements cannot be changed, otherwise the header will point to null
		heada = heada->next;
	}
	else {
		head_merge = headb;
		//Note that the order of the following two statements cannot be changed, otherwise the header will point to null
		headb = headb->next;
		//head_merge->next = NULL;
	}
	head_merge->next = NULL;

	LinkList *pmerge = head_merge;
	while(heada != NULL && headb != NULL) {
		if (heada->value <= headb->value) {
			pmerge->next = heada;
			heada = heada->next;
			pmerge = pmerge->next;
			
		}
		else {
			pmerge->next = headb;
			headb = headb->next;
			pmerge = pmerge->next;
		}
		pmerge->next = NULL;
	}
	if (heada)
		pmerge->next = heada;
	else if (headb)
		pmerge->next = headb;

	return head_merge;
}

//Merging two ordered single chain tables: recursion
//The original equal elements of the linked list are retained
LinkList* MergeListRecursively(LinkList *heada, LinkList *headb) {
	if (NULL == heada)
		return headb;
	if (NULL == headb)
		return heada;
	
	LinkList *head_merge = NULL;
	if (heada->value <= headb->value) {
		head_merge = heada;
		head_merge->next = MergeListRecursively(heada->next, headb);
	}
	else {
		head_merge = headb;
		head_merge->next = MergeListRecursively(heada, headb->next);
	}
	return head_merge;
}

11. Judge whether there is a ring in a single chain table

Two pointers are also used here. If there is a link in a linked list, that is to say, to traverse it with a pointer, it will never reach the end. Therefore, we can use two pointers to traverse. One pointer takes two steps at a time and one pointer takes one step at a time. If there is a ring, the two pointers will surely meet in the ring. The time complexity is O (n).

//Judge whether there is a link in the list
bool HasCircle(LinkList *head) {
	if (NULL == head)
		return false;
	LinkList *first = head;
	LinkList *second = head;
	while (first && second->next) {
		second = second->next->next;
		first = first->next;
		if (first == second)
			return true;
	}
	return false;
}

12. Judge whether two single chain tables intersect

If two linked lists intersect at a certain node, all nodes after the intersecting node are shared by two linked lists. In other words, if two linked lists intersect, the last node must be shared. First, traverse the first linked list, remember the last node, and then traverse the second linked list. At the last node, compare it with the last node of the first linked list. If it is the same, it will intersect, otherwise it will not. The time complexity is O(len1+len2), because only one extra pointer is needed to save the last node address, and the space complexity is O(1).

//Judge whether two linked lists intersect
bool IsCross(LinkList* heada, LinkList *headb) {
	if (NULL == heada || NULL == headb)
		return false;
	LinkList* taila = heada;
	LinkList* tailb = headb;
	while (taila->next)
		taila = taila->next;
	while (tailb->next)
		tailb = tailb->next;
	return taila == tailb;
}

13. Find the first node where two single chain tables intersect

Traverse the first linked list, calculate the length len1, and save the address of the last node.

My idea is to assume that there is a version field in the linked list with an initial value of 0; first traverse the first linked list, then + 1 for the version field in each linked list node, and then + 1 for each version field in the second linked list. When the value is found to be 2, the first node will be found.

Traverse the second linked list, calculate the length len2, and check whether the last node is the same as the last node of the first linked list. If not, do not intersect and end.
If len1 is greater than len2, the first link list will traverse len1-len2 nodes first. At this time, the distance between the current nodes of the two links list and the first intersecting node will be equal, and then the two links will traverse backward together to know that the addresses of the two nodes are the same.

Time complexity, O(len1+len2).

//Find the first node where two linked lists intersect
LinkList* GetFirstCrossNode(LinkList* heada, LinkList* headb) {
	if (NULL == heada || NULL == headb)
		return NULL;

	int lengtha = 1;
	LinkList* taila = heada;
	while (taila->next) {
		++lengtha;
		taila = taila->next;
	}

	int lengthb = 1;
	LinkList* tailb = headb;
	while (tailb->next) {
		++lengthb;
		tailb = tailb->next;
	}

	//Two linked lists do not intersect
	if (taila != tailb)
		return NULL;

	LinkList* plong = heada; //Point to a long list
	LinkList* pshort = headb;//Point to a small list
	int diff;
	if (lengthb > lengtha) {
		diff = lengthb - lengtha;
		plong = headb;
		pshort = heada;
	}

	//The long list moves forward first to align the two lists
	for (int i = 0; i < diff; ++i)
		plong = plong->next;

	while (plong && pshort && plong != pshort) {
		plong = plong->next;
		pshort = pshort->next;
	}
	return plong;
}

14. Find the first node in a single chain table that has a ring

First, judge whether there is a ring, if there is no end. Break at a node in the ring (of course, the original linked list cannot be broken at the end of the function), so two intersecting single linked lists are formed, and the first node entering the ring is transformed into the first node intersecting the two single linked lists.

//Find the first node of the linked list when the link is known
LinkList* GetFirstCircleNode(LinkList* head) {
	if (NULL == head)
		return NULL;

	//Judge whether two linked lists have rings or not. If there is no ring, return to null
	LinkList *first = head;
	LinkList *second = head;
	while (first && second->next) {
		first = first->next;
		second = second->next->next;
		if (first == second)
			break;
	}
	if (NULL == first || NULL == second->next)
		return NULL;

	//This node in the ring at the time of meeting is regarded as the assumed tail node,
	//The original linked list is transformed into two intersecting single linked lists, and the first common node is the required one
	LinkList* assumed_tail = first;
	LinkList* head1 = head;
	LinkList* head2 = assumed_tail->next;

	LinkList *pa = head1;
	int length1 = 1;
	while (pa != assumed_tail) {
		pa = pa->next;
		++length1;
	}

	LinkList* pb = head2;
	int length2 = 1;
	while (pb != assumed_tail) {
		pb = pb->next;
		++length2;
	}
	pa = head1;
	pb = head2;
	if (length1 > length2) {
		int diff = length1 - length2;
		while (diff--)
			pa = pa->next;
	}
	else {
		int diff = length2 - length1;
		while (diff--)
			pb = pb->next;
	}
	while (pa != pb) {
		pa = pa->next;
		pb = pb->next;
	}
	return pa;
}

15. Delete the node pointed by a pointer, time complexity O(1)

For deleting a node, our common idea is to make the previous node of the node point to the next node of the node. In this case, we need to traverse to find the previous node of the node, and the time complexity is O(n). For the linked list, each node structure in the linked list is the same, so we can copy the data of the next node of this node to this node, and then delete the next node.

Pay attention to the situation of the last node. At this time, you can only use common methods to operate. Find the previous node first, but the overall average time complexity is still O(1). The reference code is as follows:

//Delete a node pointed by a pointer, time complexity O(1)
void DeleteTheNode(LinkList *head, LinkList *to_delete) {
	if (NULL == to_delete || NULL == head)
		return;

	//The last node to delete
	if (NULL == to_delete->next) {
		if (head == to_delete) { //The only node in the list to be deleted
			head = NULL;
			free(to_delete);
			to_delete = NULL;  //Prevent hanging pointer
		}
		else {             //There are multiple nodes in the linked list. To delete the tail node
			LinkList* p = head;
			while (p->next != to_delete)
				p = p->next;
			p->next = NULL;
			free(to_delete);
			to_delete = NULL;    //Prevent hanging pointer
		}
	} 
	else { //The end node is not to be deleted
		LinkList *pnext = to_delete->next;
		to_delete->value = pnext->value;
		to_delete->next = pnext->next;
		free(pnext);
		pnext = NULL;
	}
}

16. Some test codes

int main() {

	/*LinkList *head = BuildList();
	head = InsertToHead(9, head);
	head = InsertToTail(100, head);
	head = DeletebyValue(2, head);
	printf("length: %d\n", Length(head));
	PrintList(head);
	head = Sort(head);
	printf("list1: ");PrintList(head);*/

	/*head = ReverseList(head);
	PrintList(head);*/

	/*LinkList* kth = GetRKthNode(head, 1);
	if (kth)
	printf("1th: %d\n", kth->value);*/

	/*LinkList *mid = GetMiddleNode(head);
	if (mid)
		printf("mid : %d\n", mid->value);*/

	/*RPrintListRecursively(head);
	printf("\n");
	RPrintList(head);*/

	//LinkList *head = BuildList();
	//LinkList *headb = BuildList();
	//printf("list2: ");PrintList(headb);
	//LinkList *head_merge = MergeList(head, headb);
	////LinkList *head_merge = MergeListRecursively(head, headb);
	//printf("list merge: ");PrintList(head_merge);

	/*
	//LinkList* head = (LinkList*)malloc(sizeof(LinkList));
	//head->next = head;
	LinkList *head = BuildList();
	LinkList *temp = head;
	while (temp->next)
		temp = temp->next;
	temp->next = head;
	if (HasCircle(head)) {
		printf("yes\n");
	}
	else
		printf("no\n");*/
	
	/*LinkList *head = BuildList();
	LinkList *temp = head;
	while (temp->next)
		temp = temp->next;
	LinkList* headb = BuildList();
	temp->next = headb->next->next;
	LinkList* p = GetFirstCrossNode(head, headb);
	if (p)
		printf("%d\n", p->value);*/
}

github address of the complete source code: https://github.com/liyangddd/algorithms/blob/master/3list_none_head_node.cpp

Reference resources:

http://blog.csdn.net/walkinginthewind/article/details/7393134

Tags: Programming less github

Posted on Tue, 05 Nov 2019 22:52:41 -0500 by raku