π Blog home page: πΌ Hello, everyone. My name is classmate ZhangπΌ
π Welcome to praise π Collection π Leaving a message. π Welcome to discuss! π
π΅ This article was originally written by [Hello, my name is classmate Zhang] and started at CSDN πππ
β¨ Boutique column (updated from time to time) [data structure + algorithm] [notes][C language programming learning]
βοΈ Boutique article recommendation
[advanced learning notes of C language] III. detailed explanation of string function (1) (sorting out liver explosion and hematemesis, recommended collection!!!)
[notes on basic learning of C language] + [notes on advanced learning of C language] summary (persistence is the only way to gain!)
preface |
Why write brush notes?
The process of blogging is also a sort and summary of the process of brushing questions. It is a time-consuming but effective method.
When the blog you share helps others, it will bring you extra happiness and happiness.
(the happiness of topic brushing + the happiness of blog is simply double the reward. Double the happiness has wood and QAQ π)
Topic content |
Give you the head node of a single linked list. Please judge whether the linked list is a palindrome linked list.
If yes, return true; Otherwise, false is returned.
Original question link (click to jump)
Train of thought analysis |
Palindrome structure
The numbers from front to back and from back to front are the same
From front to back: 1 2 2 1
From back to front: 1 2 2 1
A symmetric linked list has a palindrome structure
If it is a singular number of nodes, the middle node does not need to be considered. If other nodes are symmetrical, it must be palindrome structure
For example, 1 2 3 1 2 is also a palindrome structure
Here, with the help of the idea of finding the penultimate node of the lin k ed list.
As long as the linked list
The first node = the penultimate node
The second node = the penultimate node
...
The palindrome structure is the same as the middle node. Otherwise, it is not palindrome structure.
There are many execution / termination conditions for the end of the cycle, because we require the length of the linked list in advance.
Therefore, the number of steps that can be passed through the loop: step < = 2 / length
Of course, it can also be based on the relationship between the kth and the penultimate kth: K < = length-k-1
In addition, you can also use the relationship between node pointers: cur - > next= end || cur != end
Function implementation |
bool isPalindrome(struct ListNode* head){ struct ListNode* tail = head; int length = 0;//Find the length of the linked list while(tail){ tail = tail->next; length++; } int k = 1;//k in sequence, starting from 1 struct ListNode* cur = head,*end; while(k <= length-k){//The penultimate K is the order length-k end = head; for(int i = 0; i < length-k; i++){ end = end->next;//Find the penultimate k through end } if(cur->val != end->val)//If the two are different, false is returned return false; cur = cur->next; k++; } return true;//All nodes have been compared. If they are the same, true is returned }
Note: it is given in the title that the number of nodes in the linked list is not 0, so the empty linked list does not need to be considered. For the case of only one node, the program can still cover it, so it does not need to be treated as a separate case.
Through the pre submission of Leetcode's execution code and test examples, it is found that the program can pass successfully. But when we formally submit, there will be problems beyond the time limit.
Once the time limit is exceeded, we can usually consider two situations
1) The conditions for the end of some loop bodies in the program are incorrect, resulting in the program entering an endless loop.
2) The time complexity of the program algorithm is too high to meet the expected requirements, resulting in running timeout.
(at this time, some students may ask, "monsters, why can you think of me, but I can't think of it?" Zhang replied: Don't ask, asking is to brush more questions and debug more codes with errors. With experience, it's too difficult for me... Every day is in the dead of night, and there is no code at night. "Well, just kidding, in short, more practice, practice produces true knowledge, and practice is the source of knowledge ~)
Because the program can pass the test case, it shows that the program cannot obviously loop. We can click on the test cases that exceed the time limit, and then we can see... A lot of... Numbers, that is, the test input n is very large.
The time complexity of the program is O(n^2) and the space complexity is O(1). When there is a large amount of data, because of the time complexity of O(n^2), the running time of the program will take a long time, and naturally it will not pass the test cases.
Train of thought analysis |
After finding the problem, we should think about how to deal with it. To optimize time complexity, we will think of the way of exchanging space for time. That is, first traverse the original linked list and insert its content copy header into the new linked list, so the content of the new linked list is actually the content of the original linked list from the back to the front. Then, the palindrome structure is determined by comparing whether the contents of the two linked lists are the same
When we traverse the linked list replication node for the first time, we can also calculate the length of the linked list. When comparing the two linked lists later, we only need to compare the first 2/length nodes.
Function implementation |
bool isPalindrome(struct ListNode* head){ struct ListNode* cur1 = head; struct ListNode* newhead = NULL; int length = 0; while(cur1){ //Insert the copy node into the new linked list of newhead struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); node->val = cur1->val; if(newhead == NULL){ newhead = node; node->next = NULL; } else{ node->next = newhead; newhead = node; } length++; cur1 = cur1->next; } //Compare the two linked lists to determine the palindrome structure cur1 = head; struct ListNode* cur2 = newhead; int step = length/2; while(step--){ if(cur1->val != cur2->val) return false; cur1 = cur1->next; cur2 = cur2->next; } return true; }
After submitting the program, Leetcode passed successfully, but we can see that the execution time and memory consumption of the program are very large. The reasons are as follows:
(1) We actually traverse the linked list twice, but the point is that we use malloc to open up new nodes to form a new linked list, which takes a long time.
(2) Using malloc to open up new nodes to form a new linked list will also occupy a lot of memory space, resulting in large memory consumption.
Note that after the new head of the new linked list is used, the nodes in the new linked list should be released, because such nodes are applied by malloc from the heap. Failure to release will lead to memory leakage. If the development program uses this code. It will cause the computer or mobile phone to use less and less memory, and the program will run more and more slowly!
bool isPalindrome(struct ListNode* head){ struct ListNode* cur1 = head; struct ListNode* newhead = NULL; int length = 0; while(cur1){ //Insert the copy node into the new linked list of newhead struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); node->val = cur1->val; if(newhead == NULL){ newhead = node; node->next = NULL; } else{ node->next = newhead; newhead = node; } length++; cur1 = cur1->next; } //Compare the two linked lists to determine the palindrome structure cur1 = head; struct ListNode* cur2 = newhead; int step = length/2; while(step--){ if(cur1->val != cur2->val) return false; cur1 = cur1->next; cur2 = cur2->next; } //Release the newhead linked list to prevent memory leakage cur2 = newhead; while(cur2){ struct ListNode* next = cur2->next; free(cur2); cur2 = next; } return true; }
The above method can also be slightly improved. The original method is to copy the new node into the linked list, and then compare whether the contents of the two linked lists are consistent. In fact, the final analysis is to compare whether the two values are the same. Therefore, we can copy the val value in the original linked list to an array, and the size of the array can be determined according to the length. malloc opens up an array space at one time, which can reduce consumption, and then directly compare whether the values are the same in the array. (of course, array and linked list can also be used for comparison, just compare the val of the node in front of the linked list with the val behind the array)
Is there any way to improve it so that the running time of the program is very short and the memory consumption is very small?
Fast and slow pointer method |
(1) Find the midpoint
(2) Reverse the front half or the back half
(3) Compare and judge whether it is palindrome structure
(4) Restore linked list
For the reverse linked list, please refer to: [link list of Leetcode notes] 206 Reverse linked list
Algorithm diagram |
Function implementation |
//Iterative method struct ListNode* reverseList(struct ListNode* head,struct ListNode* middle){ if(!head)//First judge whether the linked list is empty return NULL; struct ListNode* prev = NULL; struct ListNode* cur = head; while( cur != middle){ struct ListNode* next = cur->next; cur->next = prev; prev = cur; cur = next; } return prev; } bool isPalindrome(struct ListNode* head){ struct ListNode* fast,*slow; fast = slow = head; while(fast && fast->next){ slow = slow->next; fast = fast->next->next; } head = reverseList(head, slow);//Reverse the first half //Judge whether the node is singular or even by whether fast is empty, and determine the starting point of the following comparison struct ListNode* cur1 = head,*cur2 = slow; if(fast != NULL){ cur2 = cur2->next; } while(cur1 && cur1 != slow){ if(cur1->val != cur2->val) return false; cur1 = cur1->next; cur2 = cur2->next; } //reduction struct ListNode* mark = head; head = reverseList(head,NULL); if(mark && mark->next) mark->next = slow; return true; }