/
Linked ListProblems Linked ListProblems

Linked ListProblems - PDF document

cheryl-pisano
cheryl-pisano . @cheryl-pisano
Follow
422 views
Uploaded On 2015-08-26

Linked ListProblems - PPT Presentation

By Nick ParlanteCopyright ID: 115948

Nick ParlanteCopyright

Share:

Link:

Embed:

Download Presentation from below link

Download Pdf The PPT/PDF document "Linked ListProblems" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.


Presentation Transcript

Linked ListProblems By Nick ParlanteCopyright ©1998-2002, Nick ParlanteAbstractThis document reviews basic linked list code techniques and then works through 18The problems use the C language syntax, so they require a basic understanding of C andits pointer syntax. The emphasis is on the important concepts of pointer manipulation andFor some of the problems we present multiple solutions, such as iteration vs. recursion,dummy node vs. local reference. The specific problems are, in rough order of difficulty:ContentsSection 1 Ñ Review of basic linked list code techniques 3Section 2 Ñ 18 list problems in increasing order of difficulty 10Section 3 Ñ Solutions to all the problems 20This is document #105, Linked List Problems, in the Stanford CS Education Library. This and other free educational materials are available at http://cslibrary.stanford.edu/. This document is free to be used, reproduced, or sold so long as this notice is clearly reproduced at its beginning. Related CS Education Library DocumentsRelated Stanford CS Education library documents...¥Linked List Basics (http://cslibrary.stanford.edu/103/)Explains all the basic issues and techniques for building linked lists.¥Pointers and Memory (http://cslibrary.stanford.edu/102/)Explains how pointers and memory work in C and other languages. Startswith the very basics, and extends through advanced topics such as¥Binary Trees (http://cslibrary.stanford.edu/110/)Introduction to binary trees¥Essential C (http://cslibrary.stanford.edu/101/)Explains the basic features of the C programming language. 2¥The Great Tree List Problem (http://cslibrary.stanford.edu/109/)Presents the greatest recursive pointer problem ever devised.Why Linked Lists Are Great To StudyLinked lists hold a special place in the hearts of many programmers. Linked lists are great¥Nice Domain The linked list structure itself is simple. Many linked listoperations such as "reverse a list" or "delete a list" are easy to describe andunderstand since they build on the simple purpose and structure of the¥Complex Algorithm Even though linked lists are simple, the algorithmsthat operate on them can be as complex and beautiful as you want (Seeproblem #18). It's easy to find linked list algorithms that are complex, and¥Pointer Intensive Linked list problems are really about pointers. Thelinked list structure itself is obviously pointer intensive. Furthermore,linked list algorithms often break and re-weave the pointers in a linked list¥Visualization Visualization is an important skill in programming anddesign. Ideally, a programmer can visualize the state of memory to helpthink through the solution. Even the most abstract languages such as JavaNot to appeal to your mercenary side, but for all of the above reasons, linked listproblems are often used as interview and exam questions. They are short to state, andHow To Use This DocumentTry not to use these problems passively. Take some time to try to solveeach problem.DedicationThis Jan-2002 revision includes many small edits. The first major release was Jan 17, 3Section 1 ÑLinked List Review This section is a quick review of the concepts used in these linked list problems. For moredetailed coverage, see Link List Basics (http://cslibrary.stanford.edu/103/) where all ofLinked List Ground RulesAll of the linked list code in this document uses the "classic" singly linked list structure: pointer to the next node. The .next pointer of the last node is NULL. Theempty list is represented by a NULL head pointer. All of the nodes are allocated in theheap.For a few of the problems, the solutions present the temporary "dummy node" variation(see below), but most of the code deals with linked lists in their plain form. In the text,struct node {int data;struct node* next;};To keep thing ssimple, we will not introduce any intermediate typedefs. All pointers tonodes are declared simply as struct node*. Pointers to pointers to nodes are declaredas struct node**. Such pointers to pointers are often called "reference pointers".Basic Utility FunctionsIn a few places, the text assumes the existence of the following basic utility functions...¥int Length(struct node* head);Returns the number of nodes in the list.¥struct node* BuildOneTwoThree();Allocates and returns the list {1, 2, 3}. Used by some of the example code¥ void Push(struct node** headRef, int newData);Given an int and a reference to the head pointer (i.e. a structnode** pointer to the head pointer), add a new node at the head of thelist with the standard 3-step-link-in: create the new node, set its .next topoint to the current head, and finally change the head to point to the newnode. (If you are not sure of how this function works, the first few 4Use of the Basic Utility FunctionsThis sample code demonstrates the basic utility functions being used. Theirvoid BasicsCaller() {struct node* head;int len;head = BuildOneTwoThree();// Start with {1, 2, 3}Push(&head, 13);// Push 13 on the front, yielding {13, 1, 2, 3}// (The '&' is because head is passed�Push(&(head-next), 42);// Push 42 into the second position// yielding {13, 42, 1, 2, 3}len = Length(head);// Computes that the length is 5.}If these basic functions do not make sense to you, you can (a) go see Linked List Basics(http://cslibrary.stanford.edu/103/) which explains the basics of linked lists in detail, orLinked List Code Techniques The following list presents the most common techniques you may want to use in solvingthe linked list problems. The first few are basic. The last few are only necessary for the1. Iterate Down a ListA very frequent technique in linked list code is to iterate a pointer over all the nodes in awhile loop. The head pointer is copied into a localvariable current which then iterates down the list. Test for the end of the list withcurrent!=NULL. Advance the pointer with �current=current-next.// Return the number of nodes in a list (while-loop version)int Length(struct node* head) {int count = 0;struct node* current = head;while (current != NULL) {count++;�current = current-next;}return(count);}Alternately, some people prefer to write the loop as a for which makes the initialization,test, and pointer advance more centralized, and so harder to omit...�for (current = head; current != NULL; current = current-next) { 52. Changing a Pointer Using a Reference PointerMany list functions need to change the caller's head pointer. In C++, you can just declare¥Design the function to take a pointer to the head pointer. This is thestandard technique in C Ñ pass a pointer to the "value of interest" thatstruct node*, pass a structnode**.¥Use '&' in the caller to compute and pass a pointer to the value of interest.¥Use '*' on the parameter in the callee function to access and change thevalue of interest.The following simple function sets a head pointer to NULL by using a referenceparameter....// Change the passed in head pointer to be NULL// Uses a reference pointer to access the caller's memory// Takes a pointer to// the value of interest*headRef = NULL;// use '*' to access the value of interest}void ChangeCaller() {struct node* head1;struct node* head2;ChangeToNull(&head1);// use '&' to compute and pass a pointer toChangeToNull(&head2);// the value of interest// head1 and head2 are NULL at this point}Here is a drawing showing how the headRef pointer in ChangeToNull() points back tothe variable in the caller... Stack headRef ChangeToNull(&head1) ChangeCaller() 6Many of the functions in this document use reference pointer parameters. See the use ofPush() above and its implementation in the appendix for another example of reference3. Build Ñ At Head With Push()The easiest way to build up a list is by adding nodes at its "head end" with Push(). Thestruct node* AddAtHead() {struct node* head = NULL;int i;for (i=1; i)Push(&head, i);}// head == {5, 4, 3, 2, 1};return(head);}4. Build Ñ With Tail PointerWhat about adding nodes at the "tail end" of the list? Adding a node at the tail of a list.next fieldfrom NULL to point to the new node, such as the tail variable in the followingexample of adding a "3" node to the end of the list {1, 2}... Stack Heap 1 2 head tail 3 need a pointer to the node just before that position, so you can change its .next field.Many list problems include the sub-problem of advancing a pointer to the node before thepoint of insertion or deletion. The one exception is if the operation falls on the first node 75. Build Ñ Special Case + Tail PointerConsider the problem of building up the list {1, 2, 3, 4, 5} by appending the nodes to the�tail-next. The only "problem" with this solution is that writing separate specialcase code for the first node is a little unsatisfying. Nonetheless, this approach is a solidone for production code Ñ it is simple and runs fast.struct node* BuildWithSpecialCase() {struct node* head = NULL;struct node* tail;// Deal with the head node here, and set the tail pointerPush(&head, 1);// Do all the other nodes using 'tail'for (i=2; i) i;&#x++ {;Push(&(tail-next), i); i;&#x++ {;// add node at tail-next i;&#x++ {;tail = tail-next;// advance tail to point to last node}return(head);// head == {1, 2, 3, 4, 5};}6. Build Ñ Temporary Dummy NodeThis is a slightly unusual technique that can be used to shorten the code: Use a temporary.next field of some other node. Thatway the code for the first node is the same as for the other nodes. The tail pointer playsthe same role as in the previous example. The difference is that now it also handles thestruct node* BuildWithDummyNode() {struct node dummy;// Dummy node is temporarily the first nodestruct node* tail = &dummy; // Start the tail at the dummy. i;&#x++ {; // Build the list on dummy.next (aka tail-next)int i;dummy.next = NULL; i;&#x++ {;Push(&(tail-next), i); i;&#x++ {;tail = tail-next;}// The real result list is now in dummy.next// dummy.next == {1, 2, 3, 4, 5};} 8Some linked list implementations keep the dummy node as a permanent part of the list.For this "permanent dummy" strategy, the empty list is not represented by a NULL7. Build Ñ Local ReferencesFinally, here is a tricky way to unify all the node cases without using a dummy node at in the list instead of to the last node. All additions to the list are made byfollowing the reference pointer. The reference pointer starts off pointing to the headpointer. Later, it points to the .next field inside the last node in the list. (A detailedexplanation follows.)struct node* BuildWithLocalRef() {struct node* head = NULL;struct node** lastPtrRef= &head;// Start out pointing to the head pointerint i;for (i=1; i)Push(lastPtrRef, i);// Add node at the last pointer in the list i;&#x++ {;lastPtrRef= &((*lastPtrRef)-next);// Advance to point to the// new last pointer}// head == {1, 2, 3, 4, 5};return(head);}This technique is short, but the inside of the loop is scary. This technique is rarely used,but it's a good way to see if you really understand pointers. Here's how it works...1) At the top of the loop, lastPtrRef points to the last pointer in the list.Initially it points to the head pointer itself. Later it points to the .nextfield inside the last node in the list.2) Push(lastPtrRef, i); adds a new node at the last pointer. Thenew node becomes the last node in the list.3)  i;&#x++ {;lastPtrRef= &((*lastPtrRef)-next); Advance thelastPtrRef to now point to the .next field inside the new last nodeÑ that .next field is now the last pointer in the list.Here is a drawing showing the state of memory for the above code just before the thirdnode is added. The previous values of lastPtrRef are shown in gray... 9 Stack Heap 1 2 head lastPtrRef alternative solutions presented for some of the advanced problems. The code is shorterUnusual TechniquesBoth the temporary-stack-dummy and the local-reference-pointer techniques are a little 10Section 2 ÑLinked List Problems Here are 18 linked list problems arranged in order of difficulty. The first few are quitebasic and the last few are quite advanced. Each problem starts with a basic definition ofIt's easy to just passively sweep your eyes over the solution Ñ verifying its existencewithout lettings its details touch your brain. To get the most benefit from these problems,Great programmers can visualize data structures to see how the code and memory willinteract. Linked lists are well suited to that sort of visual thinking. Use these problems to"The will to win means nothing without the will to prepare." - Juma Ikangaa, marathoner(also attributed to Bobby Knight)1 Ñ Count() Write a Count() function that counts the number of times a given int occurs in a list. Thecode for this has the classic list traversal structure as demonstrated in Length().void CountTest() {List myList = BuildOneTwoThree();// build {1, 2, 3}int count = Count(myList, 2);// returns 1 since there's 1 '2' in the list}/* Given a list and an int, return the number of times that int occurs 112 Ñ GetNth() Write a GetNth() function that takes a linked list and an integer index and returns the datavalue stored in the node at that index position. GetNth() uses the C numbering conventionvoid GetNthTest() {struct node* myList = BuildOneTwoThree();// build {1, 2, 3}int lastNode = GetNth(myList, 2);// returns the value 3}Essentially, GetNth() is similar to an array[i] operation Ñ the client can ask forelements by index number. However, GetNth() no a list is much slower than [ ] on anarray. The advantage of the linked list is its much more flexible memory management Ñ// Given a list and an index, return the data// in the nth node of the list. The nodes are numbered from 0.3 Ñ DeleteList() Write a function DeleteList() that takes a list, deallocates all of its memory and sets itshead pointer to NULL (the empty list).void DeleteListTest() {struct node* myList = BuildOneTwoThree();// build {1, 2, 3}DeleteList(&myList);// deletes the three nodes and sets myList to NULL}Post DeleteList() Memory DrawingThe following drawing shows the state of memory after DeleteList() executes in the 12 Stack Heap 1 2 3 The DeleteList() implementation will need to use a reference parameter just like Push().next field in each node afterthe node has been deallocated.void DeleteList(struct node** headRef) {// Your code4 Ñ Pop() Write a Pop() function that is the inverse of Push(). Pop() takes a non-empty list, deletesthe head node, and returns the head node's data. If all you ever used were Push() andvoid PopTest() {struct node* head = BuildOneTwoThree();// build {1, 2, 3}int a = Pop(&head);// deletes "1" node and returns 1int b = Pop(&head);// deletes "2" node and returns 2int c = Pop(&head);// deletes "3" node and returns 3int len = Length(head);// the list is now empty, so len == 0}Pop() UnlinkPop() is a bit tricky. Pop() needs to unlink the front node from the list and deallocate it 13 Stack Heap 1 2 3 head Ironically, the unlinked node itself is not changed /* The opposite of Push(). Takes a non-empty list5 Ñ InsertNth() A more difficult problem is to write a function InsertNth() which can insert a new node atany index within a list. Push() is similar, but can only insert a node at the head end of thevoid InsertNthTest() {struct node* head = NULL;// start with the empty listInsertNth(&head, 0, 13);// build {13)InsertNth(&head, 1, 42);// build {13, 42}InsertNth(&head, 1, 5);// build {13, 5, 42}DeleteList(&head);// clean up after ourselves} 14InsertNth() is complex Ñ you will want to make some drawings to think about yoursolution and afterwards, to check its correctness./* A more general version of Push().6 Ñ SortedInsert() Write a SortedInsert() function which given a list that is sorted in increasing order, and asingle node, inserts the node into the correct sorted position in the list. While Push()void SortedInsert(struct node** headRef, struct node* newNode) {// Your code...7 Ñ InsertSort() Write an InsertSort() function which given a list, rearranges its nodes so they are sorted inincreasing order. It should use SortedInsert().// Given a list, change it to be in sorted order (using SortedInsert()).void InsertSort(struct node** headRef) { // Your code8 Ñ Append() Write an Append() function that takes two lists, 'a' and 'b', appends 'b' onto the end of 'a',and then sets 'b' to NULL (since it is now trailing off the end of 'a'). Here is a drawing of Stack Heap 1 2 a b 3 4 15It turns out that both of the head pointers passed to Append(a, b) need to be referenceparameters since they both may need to be changed. The second 'b' parameter is always Stack Heap a b 3 4 // Append 'b' onto the end of 'a', and then set 'b' to NULL.void Append(struct node** aRef, struct node** bRef) {9 Ñ FrontBackSplit() Given a list, split it into two sublists Ñ one for the front half, and one for the back half. Ifthe number of elements is odd, the extra element should go in the front list. SoHint. Probably the simplest strategy is to compute the length of the list, then use a forloop to hop over the right number of nodes to find the last node of the front half, and thencut the list at that point. There is a trick technique that uses two pointers to traverse the/* Split the nodes of the given list into front and back halves,struct node** frontRef, struct node** backRef) {// Your code... 1610 RemoveDuplicates() Write a RemoveDuplicates() function which takes a list sorted in increasing order anddeletes any duplicate nodes from the list. Ideally, the list should only be traversed once./* Remove duplicates from a sorted list.11 Ñ MoveNode() This is a variant on Push(). Instead of creating a new node and pushing it onto the givenlist, MoveNode() takes two lists, removes the front node from the second list and pushesvoid MoveNodeTest() {struct node* a = BuildOneTwoThree();// the list {1, 2, 3}struct node* b = BuildOneTwoThree();MoveNode(&a, &b);// a == {1, 1, 2, 3}}/* Take the node from the front of the source, and move it to12 Ñ AlternatingSplit() Write a function AlternatingSplit() that takes one list and divides up its nodes to maketwo smaller lists. The sublists should be made from alternating elements in the original/* Given the source list, split its nodes into two shorter lists.struct node** aRef, struct node** bRef) {// Your code 1713Ñ ShuffleMerge() Given two lists, merge their nodes together to make one list, taking nodes alternatelybetween the two lists. So ShuffleMerge() with {1, 2, 3} and {7, 13, 1} should yield {1, 7,/* Merge the nodes of the two lists into a single list taking a node14 Ñ SortedMerge() Write a SortedMerge() function that takes two lists, each of which is sorted in increasingorder, and merges the two together into one list which is in increasing order./* Takes two lists sorted in increasing order, and15 Ñ MergeSort() (This problem requires recursion) Given FrontBackSplit() and SortedMerge(), it's prettyeasy to write a classic recursive MergeSort(): split the list into two smaller lists,void MergeSort(struct node* headRef) {// Your code... 1816 Ñ SortedIntersect() Given two lists sorted in increasing order, create and return a new list representing theintersection of the two lists. The new list should be made with its own memory Ñ the/* Compute a new sorted list that represents the intersection17 Ñ Reverse() Write an iterative Reverse() function that reverses a list by rearranging all the .nextpointers and the head pointer. Ideally, Reverse() should only need to make one pass of thelist. The iterative solution is moderately complex. It's not so difficult that it needs to bevoid ReverseTest() {struct node* head;head = BuildOneTwoThree();Reverse(&head);DeleteList(&head);// clean up after ourselves} Stack Heap 1 2 3 Iterate through the main list. Move each node to the front of the result list as you go. It's 19the existing node instead of allocating a new node. You can use MoveNode() to do mostof the work, or hand code the pointer re-arrangement."3 Pointers" HintThis strategy is not as good as the "Push" strategy, but it's the first one I thought of.next pointer of the middle node during the iteration.Add code to take care of the empty list and to adjust the head pointer itself./* Reverse the given linked list by changing its .next pointers and18 Ñ RecursiveReverse() (This problem is difficult and is only possible if you are familiar with recursion.) There isa short and efficient recursive solution to this problem. As before, the code should only/* Recursively reverses the given linked list by changing its .nextThe Tree-List Recursion ProblemOnce you are done with these problems, see the best and most complex list recursion 20Section 3 Ñ Solutions 1 Ñ Count() Solution A straightforward iteration down the list Ñ just like Length().int Count(struct node* head, int searchFor) {struct node* current = head;int count = 0;while (current != NULL) {�if (current-data == searchFor) count++;�current = current-next;}return count;}Alternately, the iteration may be coded with a for loop instead of a while...int Count2(struct node* head, int searchFor) {struct node* current;int count = 0;�for (current = head; current != NULL; current = current-next) {�if (current-data == searchFor) count++;}return count;}2 Ñ GetNth() Solution Combine standard list iteration with the additional problem of counting over to find theright node. Off-by-one errors are common in this sort of code. Check it carefully against aint GetNth(struct node* head, int index) {struct node* current = head;int count = 0;// the index of the node we're currently looking atwhile (current != NULL) {�if (count == index) return(current-data);count++;}assert(0);// if we get to this line, the caller was asking// for a non-existent element so we assert fail.} 213 Ñ DeleteList() Solution Delete the whole list and set the head pointer to NULL. There is a slight complicationinside the loop, since we need extract the .next pointer before we delete the node, sinceafter the delete it will be technically unavailable.void DeleteList(struct node** headRef) {struct node* current = *headRef;// deref headRef to get the real headstruct node* next;while (current != NULL) {�next = current-next;// note the next pointerfree(current);// delete the nodecurrent = next;// advance to the next node}*headRef = NULL;// Again, deref headRef to affect the real head back// in the caller.}4 Ñ Pop() Solution Extract the data from the head node, delete the node, advance the head pointer to point atthe next node in line. Uses a reference parameter since it changes the head pointer.int Pop(struct node** headRef) {struct node* head;int result;head = *headRef;assert(head != NULL);�result = head-data;// pull out the data before the node is deleted�*headRef = head-next;// unlink the head node for the caller// Note the * -- uses a reference-pointer// just like Push() and DeleteList().free(head);// free the head nodereturn(result);// don't forget to return the data from the link}5 Ñ InsertNth() Solution This code handles inserting at the very front as a special case. Otherwise, it works byrunning a current pointer to the node before where the new node should go. Uses a for 22void InsertNth(struct node** headRef, int index, int data) {// position 0 is a special case...if (index == 0) Push(headRef, data);struct node* current = *headRef;int i;for (i=0; i)assert(current != NULL);// if this fails, index was too big&#xinde;&#xx-1;;&#x i++;&#x {00;current = current-next;}assert(current != NULL);// tricky: you have to check one last time&#xinde;&#xx-1;;&#x i++;&#x {00;Push(&(current-next), data);// Tricky use of Push() --// The pointer being pushed on is not// in the stack. But actually this works}}6 Ñ SortedInsert() Solution The basic strategy is to iterate down the list looking for the place to insert the new node.That could be the end of the list, or a point just before a node which is larger than the new// Uses special case code for the head endvoid SortedInsert(struct node** headRef, struct node* newNode) {// Special case for the head end���if (*headRef == NULL || (*headRef)-data = newNode-data) {�newNode-next = *headRef;*headRef = newNode;}else {// Locate the node before the point of insertionstruct node* current = *headRef;�current = current-next;}��newNode-next = current-next;}}// Dummy node strategy for the head endvoid SortedInsert2(struct node** headRef, struct node* newNode) {struct node dummy;struct node* current = &dummy;���&#xnewN;&#xode-;while (current-next!=NULL && current-next-data)&#xnewN;&#xode-;current = current-next;}&#xnewN;&#xode-;&#xnewN;&#xode-;newNode-next = current-next;&#xnewN;&#xode-;current-next = newNode; 23*headRef = dummy.next;}// Local references strategy for the head endvoid SortedInsert3(struct node** headRef, struct node* newNode) {struct node** currentRef = headRef;�&#xnewN;&#xode-;while (*currentRef!=NULL && (*currentRef)-data)&#xnewN;&#xode-;currentRef = &((*currentRef)-next);}&#xnewN;&#xode-;newNode-next = *currentRef;// Bug: this line used to have&#xnewN;&#xode-;// an incorrect (*currRef)-next*currentRef = newNode;}7 Ñ InsertSort() Solution Start with an empty result list. Iterate through the source list and SortedInsert() each of itsnodes into the result list. Be careful to note the .next field in each node before movingit into the result list.// Given a list, change it to be in sorted order (using SortedInsert()).void InsertSort(struct node** headRef) {struct node* result = NULL;// build the answer herestruct node* current = *headRef;// iterate over the original liststruct node* next;while (current!=NULL) {�next = current-next;// tricky - note the next pointer before we change itSortedInsert(&result, current);current = next;}*headRef = result;}8 Ñ Append() Solution The case where the 'a' list is empty is a special case handled first Ñ in that case the 'a'head pointer needs to be changed directly. Otherwise we iterate down the 'a' list until we�(current-next != NULL), and then tack on the 'b'list there. Finally, the original 'b' head is set to NULL. This code demonstrates extensiveuse of pointer reference parameters, and the common problem of needing to locate thevoid Append(struct node** aRef, struct node** bRef) {struct node* current;if (*aRef == NULL) {// Special case if a is empty*aRef = *bRef;}else {// Otherwise, find the end of a, and append b therecurrent = *aRef;�while (current-next != NULL) {// find the last node�current = current-next;} 24�current-next = *bRef;// hang the b list off the last node}*bRef=NULL;// NULL the original b, since it has been appended above}Append() Test and DrawingThe following AppendTest() code calls Append() to join two lists. What does memoryvoid AppendTest() {struct node* a;struct node* b;// set a to {1, 2}// set b to {3, 4}Append(&a, &b);}As an example of how reference parameters work, note how reference parameters inAppend() point back to the head pointers in AppendTest()... Stack Heap 1 2 a b 3 4 bRef Append(&a, &b) Two solutions are presented...// Uses the "count the nodes" strategyvoid FrontBackSplit(struct node* source,struct node** frontRef, struct node** backRef) {int len = Length(source);int i; 25if (len )*frontRef = source;*backRef = NULL;}else {int hopCount = (len-1)/2;//(figured these with a few drawings)for (i = 0; i)&#xhopC;&#xount;&#x; i+;&#x+ {0;current = current-next;}// Now cut at current*frontRef = source;}}// Uses the fast/slow pointer strategyvoid FrontBackSplit2(struct node* source,struct node** frontRef, struct node** backRef) {struct node* fast;struct node* slow;&#xhopC;&#xount;&#x; i+;&#x+ {0;if (source==NULL || source-next==NULL) {// length *frontRef = source;*backRef = NULL;}else {slow = source;&#x 2 c; ses;fast = source-next;// Advance 'fast' two nodes, and advance 'slow' one nodewhile (fast != NULL) {&#x 2 c; ses;fast = fast-next;if (fast != NULL) {&#x 2 c; ses;slow = slow-next;&#x 2 c; ses;fast = fast-next;}}// 'slow' is before the midpoint in the list, so split it in two// at that point.}}10 Ñ RemoveDuplicates() Solution Since the list is sorted, we can proceed down the list and compare adjacent nodes. Whenadjacent nodes are the same, remove the second one. There's a tricky case where the node// Remove duplicates from a sorted listvoid RemoveDuplicates(struct node* head) {struct node* current = head; 26if (current == NULL) return;// do nothing if the list is empty// Compare current node with next node�while(current-next!=NULL) {���if (current-data == current-next-data) {��struct node* nextNext = current-next-next;�free(current-next);}else {�current = current-next;// only advance if no deletion}}}11 Ñ MoveNode() Solution The MoveNode() code is most similar to the code for Push(). It's short Ñ just changing acouple pointers Ñ but it's complex. Make a drawing.void MoveNode(struct node** destRef, struct node** sourceRef) {struct node* newNode = *sourceRef;// the front source nodeassert(newNode != NULL);�*sourceRef = newNode-next;// Advance the source pointer�newNode-next = *destRef;// Link the old dest off the new node*destRef = newNode;// Move dest to point to the new node}12 Ñ AlternatingSplit() Solution The simplest approach iterates over the source list and use MoveNode() to pull nodes offthe source and alternately put them on 'a' and b'. The only strange part is that the nodesAlternatingSplit()void AlternatingSplit(struct node* source,struct node** aRef, struct node** bRef) {struct node* a = NULL;// Split the nodes to these 'a' and 'b' listsstruct node* b = NULL;struct node* current = source;while (current != NULL) {MoveNode(&a, ¤t);// Move a node to 'a'if (current != NULL) {MoveNode(&b, ¤t);// Move a node to 'b'}}*aRef = a;} 27AlternatingSplit() Using Dummy NodesHere is an alternative approach which builds the sub-lists in the same order as the sourcevoid AlternatingSplit2(struct node* source,struct node** aRef, struct node** bRef) {struct node aDummy;struct node* aTail = &aDummy;// points to the last node in 'a'struct node bDummy;struct node* bTail = &bDummy;// points to the last node in 'b'struct node* current = source;aDummy.next = NULL;bDummy.next = NULL;while (current != NULL) {�MoveNode(&(aTail-next), ¤t);// add at 'a' tail�aTail = aTail-next;// advance the 'a' tailif (current != NULL) {�MoveNode(&(bTail-next), ¤t);�bTail = bTail-next;}}*aRef = aDummy.next;*bRef = bDummy.next;}13 SuffleMerge() Solution There are four separate solutions included. See Section 1 for information on the variousdummy node and reference techniques. SuffleMerge() Ñ Dummy Node Not Using MoveNode()struct node* ShuffleMerge(struct node* a, struct node* b) {struct node dummy;struct node* tail = &dummy;while (1) {if (a==NULL) {// empty list cases�tail-next = b;break;}else if (b==NULL) {�tail-next = a;break;}else {// common case: move two nodes to tail�tail-next = a;tail = a; 28�tail-next = b;tail = b;}}return(dummy.next);}SuffleMerge() Ñ Dummy Node Using MoveNode()Basically the same as above, but use MoveNode().struct node* ShuffleMerge(struct node* a, struct node* b) {struct node dummy;struct node* tail = &dummy;while (1) {if (a==NULL) {�tail-next = b;break;}else if (b==NULL) {�tail-next = a;break;}else {�MoveNode(&(tail-next), &a);�tail = tail-next;}}return(dummy.next);}SuffleMerge() Ñ Local ReferencesUses a local reference to get rid of the dummy nodes entirely.struct node* ShuffleMerge(struct node* a, struct node* b) {struct node* result = NULL;struct node** lastPtrRef = &result;while (1) {if (a==NULL) {*lastPtrRef = b;break;}else if (b==NULL) {*lastPtrRef = a;break;}else {MoveNode(lastPtrRef, &a);�lastPtrRef = &((*lastPtrRef)-next); 29}}return(result);}SuffleMerge() Ñ RecursiveThe recursive solution is the most compact of all, but is probably not appropriate forstruct node* ShuffleMerge(struct node* a, struct node* b) {struct node* result;struct node* recur;if (a==NULL) return(b);// see if either list is emptyelse if (b==NULL) return(a);else {// it turns out to be convenient to do the recursive call first --��// otherwise a-next and b-next need temporary storage.��recur = ShuffleMerge(a-next, b-next);// one node from a�a-next = b;// one from b�b-next = recur;// then the restreturn(result);}}14 Ñ SortedMerge() Solution SortedMerge() Using Dummy NodesThe strategy here uses a temporary dummy node as the start of the result list. The pointer always points to the last node in the result list, so appending new nodes is easy.The dummy node gives tail something to point to initially when the result list is empty.This dummy node is efficient, since it is only temporary, and it is allocated in the stack.The loop proceeds, removing one node from either 'a' or 'b', and adding it to tail. Whenwe are done, the result is in dummy.next.struct node* SortedMerge(struct node* a, struct node* b) {struct node dummy;// a dummy first node to hang the result onstruct node* tail = &dummy;// Points to the last result node --�// so tail-next is the place to adddummy.next = NULL;while (1) {if (a == NULL) {// if either list runs out, use the other list�tail-next = b;break;}else if (b == NULL) {�tail-next = a;break;} 30�&#x= b-;if (a-data )&#x= b-;MoveNode(&(tail-next), &a);}else {&#x= b-;MoveNode(&(tail-next), &b);}&#x= b-;tail = tail-next;}return(dummy.next);}SortedMerge() Using Local ReferencesThis solution is structurally very similar to the above, but it avoids using a dummy node.lastPtrRef, that always points to the lastpointer of the result list. This solves the same case that the dummy node did Ñ dealingwith the result list when it is empty. If you are trying to build up a list at its tail, either thedummy node or the struct node** "reference" strategy can be used (see Section 1 forstruct node* SortedMerge2(struct node* a, struct node* b) {struct node* result = NULL;struct node** lastPtrRef = &result;// point to the last result pointerwhile (1) {if (a==NULL) {*lastPtrRef = b;break;}else if (b==NULL) {*lastPtrRef = a;break;}&#x= b-;&#x= b-;if (a-data )MoveNode(lastPtrRef, &a);}else {MoveNode(lastPtrRef, &b);}&#x= b-;lastPtrRef = &((*lastPtrRef)-next);// tricky: advance to point to// the next ".next" field}return(result);}SortedMerge() Using RecursionMerge() is one of those nice recursive problems where the recursive solution code isstruct node* SortedMerge3(struct node* a, struct node* b) {struct node* result = NULL; 31// Base casesif (a==NULL) return(b);// Pick either a or b, and recur�&#x= b-;if (a-data )result = a;&#x= b-;&#x= b-;result-next = SortedMerge3(a-next, b);}else {result = b;&#x= b-;&#x= b-;result-next = SortedMerge3(a, b-next);}return(result);}15 Ñ MergeSort() Solution The MergeSort strategy is: split into sublists, sort the sublists recursively, merge the twosorted lists together to form the answer.void MergeSort(struct node** headRef) {struct node* head = *headRef;struct node* a;// Base case -- length 0 or 1�if ((head == NULL) || (head-next == NULL)) {return;}FrontBackSplit(head, &a, &b);// Split head into 'a' and 'b' sublists// We could just as well use AlternatingSplit()MergeSort(&a);// Recursively sort the sublistsMergeSort(&b);*headRef = SortedMerge(a, b);// answer = merge the two sorted lists together}(Extra for experts) Using recursive stack space proportional to the length of a list is notrecommended. However, the recursion in this case is ok Ñ it uses stack space which islog of the length of the list. For a 1000 node list, the recursion willonly go about 10 deep. For a 2000 node list, it will go 11 deep. If you think about it, youcan see that doubling the size of the list only increases the depth by 1.16 Ñ SortedIntersect() Solution The strategy is to advance up both lists and build the result list as we go. When thecurrent point in both lists are the same, add a node to the result. Otherwise, advance// This solution uses the temporary dummy to build up the result liststruct node* SortedIntersect(struct node* a, struct node* b) {struct node dummy; 32struct node* tail = &dummy;dummy.next = NULL;while (a!=NULL && b!=NULL) {��if (a-data == b-data) {��Push((&tail-next), a-data);�tail = tail-next;}�&#x b-0;else if (a-data )// advance the smaller list&#x b-0;a = a-next;}else {&#x b-0;b = b-next;}}return(dummy.next);}// This solution uses the local referencestruct node* SortedIntersect2(struct node* a, struct node* b) {struct node* result = NULL;struct node** lastPtrRef = &result;// Advance comparing the first nodes in both lists.// When one or the other list runs out, we're done.&#x b-0;&#x b-0;if (a-data == b-data) {// found a node for the intersection&#x b-0;Push(lastPtrRef, a-data);&#x b-0;lastPtrRef = &((*lastPtrRef)-next);}&#x b-0;&#x b-0;else if (a-data )// advance the smaller list&#x b-0;a=a-next;}else {&#x b-0;b=b-next;}}return(result);}17 Ñ Reverse() Solution This first solution uses the "Push" strategy with the pointer re-arrangement hand codedinside the loop. There's a slight trickyness in that it needs to save the value of the/* Iterative list reverse. 33 Move/insert each node to the front of the result list -- like a Push of the node.struct node* result = NULL;struct node* current = *headRef;while (current != NULL) {�next = current-next;// tricky: note the next node�current-next = result;// move the node onto the resultresult = current;current = next;}*headRef = result;}Here's the variation on the above that uses MoveNode() to do the work...static void Reverse2(struct node** headRef) {struct node* result = NULL;struct node* current = *headRef;while (current != NULL) {MoveNode(&result, ¤t);}*headRef = result;}Finally, here's the back-middle-front strategy...// Reverses the given linked list by changing its .next pointers and// its head pointer. Takes a pointer (reference) to the head pointer.if (*headRef != NULL) {// special case: skip the empty list/* Plan for this loop: move three pointers: front, middle, backstruct node* middle = *headRef;// the main pointer�struct node* front = middle-next;// the two other pointers (NULL ok)struct node* back = NULL;while (1) {�middle-next = back;// fix the middle nodeif (front == NULL) break;// test if done 34back = middle;// advance the three pointersmiddle = front;�front = front-next;}*headRef = middle;// fix the head pointer to point to the new front}}18 Ñ RecursiveReverse() Solution Probably the hardest part is accepting the concept that theRecursiveReverse(&rest) does in fact reverse the rest. Then then there's a trickto getting the one front node all the way to the end of the list. Make a drwaing to see howthe trick works.void RecursiveReverse(struct node** headRef) {struct node* first;struct node* rest;if (*headRef == NULL) return;// empty list base casefirst = *headRef;// suppose first = {1, 2, 3}�rest = first-next;// rest = {2, 3}if (rest == NULL) return;// empty rest base caseRecursiveReverse(&rest);// Recursively reverse the smaller {2, 3} case// after: rest = {3, 2}��first-next-next = first;// put the first elem on the end of the list�first-next = NULL;// (tricky step -- make a drawing)*headRef = rest;// fix the head pointer}The inefficient soluition is to reverse the last n-1 elements of the list, and then iterate allthe way down to the new tail and put the old head node there. That solution is very slow 35Appendix Basic Utility Function ImplementationsHere is the source code for the basic utility functions.Length()// Return the number of nodes in a listint count = 0;struct node* current = head;while (current != NULL) {count++;�current=current-next;}return(count);}Push()// Given a reference (pointer to pointer) to the headstruct node* newNode =(struct node*) malloc(sizeof(struct node));// allocate node�newNode-data = newData;// put in the data�newNode-next = (*headRef);// link the old list off the new node(*headRef) = newNode;// move the head to point to the new node}BuildOneTwoThree()// Build and return the list {1, 2, 3}struct node* head = NULL;// Start with the empty listPush(&head, 3);// Use Push() to add all the dataPush(&head, 2);Push(&head, 1);return(head);}