Linked List Basics
来源:互联网 发布:武汉大学网络 编辑:程序博客网 时间:2024/05/18 03:03
Linked List Basics1
mixture:混合
terminology:术语
idiom:典型风格
hallmarks:标志,特征,特点
intensive:加强的
deliberate:故意的
Memory drawings should distinguish heap memory from local stack memory. Reminder: malloc() allocates memory in the heap which is only be deallocated by deliberate calls to free(). In contrast, local stack variables for each function are automatically allocated when the function starts and deallocated when it exits.
Basic Utility Function Implementations
//Basic Utility Function Implementations//Length()// 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);}//Push()// Given a reference (pointer to pointer) to the head// of a list and an int, push a new node on the front of the list.// Creates a new node with the int, links the list off the .next of the// new node, and finally changes the head to point to the new node.void Push(struct node** headRef, int newData) { struct 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* BuildOneTwoThree() { struct node* head = NULL; // Start with the empty list Push(&head, 3); // Use Push() to add all the data Push(&head, 2); Push(&head, 1); return(head);}
Count()
//Write a Count() function that counts the number of times a given int occurs in a list. //The code for this has the classic list traversal structure as demonstrated in 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;}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;}
GetNth()
//Write a GetNth() function that takes a linked list and an integer index and returns the data value stored in the node at that index position. GetNth() uses the C numbering convention that the first node is index 0, the second is index 1, ... and so on.int GetNth(struct node* head, int index) { struct node* current = head; int count = 0; // the index of the node we're currently looking at while (current != NULL) { if (count == index) return(current->data); count++; current = current->next; } assert(0); // if we get to this line, the caller was asking // for a non-existent element so we assert fail.}
DeleteList()
//Write a function DeleteList() that takes a list, deallocates all of its memory and sets its head pointer to NULL (the empty list).void DeleteList(struct node** headRef) { struct node* current = *headRef; // deref headRef to get the real head struct node* next; while (current != NULL) { next = current->next; // note the next pointer free(current); // delete the node current = next; // advance to the next node } *headRef = NULL; // Again, deref headRef to affect the real head back // in the caller.}
Pop()
//Write a Pop() function that is the inverse of Push(). Pop() takes a non-empty list, deletes the head node, and returns the head node's data.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 node return(result); // don't forget to return the data from the link}
InsertNth()
//A more difficult problem is to write a function InsertNth() which can insert a new node at any index within a list.void InsertNth(struct node** headRef, int index, int data) { // position 0 is a special case... if (index == 0) Push(headRef, data); else { struct node* current = *headRef; int i; for (i=0; i<index-1; i++) { assert(current != NULL); // if this fails, index was too big current = current->next; } assert(current != NULL); // tricky: you have to check one last time Push(&(current->next), data); // Tricky use of Push() -- // The pointer being pushed on is not // in the stack. But actually this works // fine -- Push() works for any node pointer. }}
SortedInsert()
//Write a SortedInsert() function which given a list that is sorted in increasing order, and a single node, inserts the node into the correct sorted position in the list.// 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 insertion struct node* current = *headRef; while (current->next!=NULL && current->next->data<newNode->data) { current = current->next; } newNode->next = current->next; current->next = newNode; }}// Dummy node strategy for the head endvoid SortedInsert2(struct node** headRef, struct node* newNode) { struct node dummy; struct node* current = &dummy; dummy.next = *headRef; while (current->next!=NULL && current->next->data<newNode->data) { current = current->next; } newNode->next = current->next; current->next = newNode; *headRef = dummy.next;}// Local references strategy for the head endvoid SortedInsert3(struct node** headRef, struct node* newNode) { struct node** currentRef = headRef; while (*currentRef!=NULL && (*currentRef)->data<newNode->data) { currentRef = &((*currentRef)->next); } newNode->next = *currentRef; // Bug: this line used to have // an incorrect (*currRef)->next *currentRef = newNode;}
InsertSort()
//Write an InsertSort() function which given a list, rearranges its nodes so they are sorted in increasing order. It should use SortedInsert().// Given a list, change it to be in sorted order (using SortedInsert()).void InsertSort(struct node** headRef) { struct node* result = NULL; // build the answer here struct node* current = *headRef; // iterate over the original list struct node* next; while (current!=NULL) { next = current->next; // tricky - note the next pointer before we change it SortedInsert(&result, current); current = next; } *headRef = result;}
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').void 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 there current = *aRef; while (current->next != NULL) { // find the last node current = current->next; } current->next = *bRef; // hang the b list off the last node } *bRef=NULL; // NULL the original b, since it has been appended above}
FrontBackSplit()
//Given a list, split it into two sublists — one for the front half, and one for the back half. If the number of elements is odd, the extra element should go in the front list.// Uses the "count the nodes" strategyvoid FrontBackSplit(struct node* source, struct node** frontRef, struct node** backRef) { int len = Length(source); int i; struct node* current = source; if (len < 2) { *frontRef = source; *backRef = NULL; } else { int hopCount = (len-1)/2; //(figured these with a few drawings) for (i = 0; i<hopCount; i++) { current = current->next; } // Now cut at current *frontRef = source; *backRef = current->next; current->next = NULL; }}// Uses the fast/slow pointer strategyvoid FrontBackSplit2(struct node* source, struct node** frontRef, struct node** backRef) { struct node* fast; struct node* slow; if (source==NULL || source->next==NULL) { // length < 2 cases *frontRef = source; *backRef = NULL; } else { slow = source; fast = source->next; // Advance 'fast' two nodes, and advance 'slow' one node while (fast != NULL) { fast = fast->next; if (fast != NULL) { slow = slow->next; fast = fast->next; } } // 'slow' is before the midpoint in the list, so split it in two at that point. *frontRef = source; *backRef = slow->next; slow->next = NULL; }}
RemoveDuplicates()
//Write a RemoveDuplicates() function which takes a list sorted in increasing order and deletes any duplicate nodes from the list. Ideally, the list should only be traversed once.// Remove duplicates from a sorted listvoid RemoveDuplicates(struct node* head) { struct node* current = head; if (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); current->next = nextNext; } else { current = current->next; // only advance if no deletion } }}
MoveNode()
//This is a variant on Push(). Instead of creating a new node and pushing it onto the given list, MoveNode() takes two lists, removes the front node from the second list and pushes it onto the front of the first.void MoveNode(struct node** destRef, struct node** sourceRef) { struct node* newNode = *sourceRef; // the front source node assert(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}
AlternatingSplit()
//Write a function AlternatingSplit() that takes one list and divides up its nodes to make two smaller lists.void AlternatingSplit(struct node* source, struct node** aRef, struct node** bRef) { struct node* a = NULL; // Split the nodes to these 'a' and 'b' lists struct 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; *bRef = b;}void 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' tail if (current != NULL) { MoveNode(&(bTail->next), ¤t); bTail = bTail->next; } } *aRef = aDummy.next; *bRef = bDummy.next;}
ShuffleMerge()
//Given two lists, merge their nodes together to make one list, taking nodes alternately between the two lists.//SuffleMerge() — Dummy Node Not Using MoveNode()struct node* ShuffleMerge(struct node* a, struct node* b) { struct node dummy; struct node* tail = &dummy; dummy.next = NULL; 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; a = a->next; tail->next = b; tail = b; b = b->next; } } 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; dummy.next = NULL; 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; MoveNode(&(tail->next), &b); tail = tail->next; } } return(dummy.next);}//SuffleMerge() — Local References//Uses 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); MoveNode(lastPtrRef, &b); lastPtrRef = &((*lastPtrRef)->next); } } return(result);}//SuffleMerge() — Recursive//The recursive solution is the most compact of all, but is probably not appropriate for production code since it uses stack space proportionate to the lengths of the lists.struct node* ShuffleMerge(struct node* a, struct node* b) { struct node* result; struct node* recur; if (a==NULL) return(b); // see if either list is empty else 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); result = a; // one node from a a->next = b; // one from b b->next = recur; // then the rest return(result); }}
SortedMerge()
//Write a SortedMerge() function that takes two lists, each of which is sorted in increasing order, and merges the two together into one list which is in increasing order.struct node* SortedMerge(struct node* a, struct node* b) { struct node dummy; // a dummy first node to hang the result on struct node* tail = &dummy; // Points to the last result node -- // so tail->next is the place to add // new nodes to the result. dummy.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; } if (a->data <= b->data) { MoveNode(&(tail->next), &a); } else { MoveNode(&(tail->next), &b); } tail = tail->next; } return(dummy.next);}//SortedMerge() Using Local Referencesstruct node* SortedMerge2(struct node* a, struct node* b) { struct node* result = NULL; struct node** lastPtrRef = &result; // point to the last result pointer while (1) { if (a==NULL) { *lastPtrRef = b; break; } else if (b==NULL) { *lastPtrRef = a; break; } if (a->data <= b->data) { MoveNode(lastPtrRef, &a); } else { MoveNode(lastPtrRef, &b); } lastPtrRef = &((*lastPtrRef)->next); // tricky: advance to point to // the next ".next" field } return(result);}//SortedMerge() Using Recursionstruct node* SortedMerge3(struct node* a, struct node* b) { struct node* result = NULL; // Base cases if (a==NULL) return(b); else if (b==NULL) return(a); // Pick either a or b, and recur if (a->data <= b->data) { result = a; result->next = SortedMerge3(a->next, b); } else { result = b; result->next = SortedMerge3(a, b->next); } return(result);}
MergeSort()
//(This problem requires recursion) Given FrontBackSplit() and SortedMerge(), it's pretty easy to write a classic recursive MergeSort(): split the list into two smaller lists, recursively sort those lists, and finally merge the two sorted lists together into a single sorted list.void MergeSort(struct node** headRef) { struct node* head = *headRef; struct node* a; struct node* b; // 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 sublists MergeSort(&b); *headRef = SortedMerge(a, b); // answer = merge the two sorted lists together}
SortedIntersect()
//Given two lists sorted in increasing order, create and return a new list representing the intersection of the two lists.// This solution uses the temporary dummy to build up the result liststruct node* SortedIntersect(struct node* a, struct node* b) { struct node dummy; struct node* tail = &dummy; dummy.next = NULL; // Once one or the other list runs out -- we're done while (a!=NULL && b!=NULL) { if (a->data == b->data) { Push((&tail->next), a->data); tail = tail->next; a = a->next; b = b->next; } else if (a->data < b->data) { // advance the smaller list a = a->next; } else { 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. while (a!=NULL && b!=NULL) { if (a->data == b->data) { // found a node for the intersection Push(lastPtrRef, a->data); lastPtrRef = &((*lastPtrRef)->next); a=a->next; b=b->next; } else if (a->data < b->data) { // advance the smaller list a=a->next; } else { b=b->next; } } return(result);}
Reverse()
//Write an iterative Reverse() function that reverses a list by rearranging all the .next pointers and the head pointer./*Iterative list reverse.Iterate through the list left-right.Move/insert each node to the front of the result list --like a Push of the node.*/static void Reverse(struct node** headRef) { struct node* result = NULL; struct node* current = *headRef; struct node* next; while (current != NULL) { next = current->next; // tricky: note the next node current->next = result; // move the node onto the result result = 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.void Reverse(struct node** headRef) { if (*headRef != NULL) { // special case: skip the empty list /* Plan for this loop: move three pointers: front, middle, back down the list in order. Middle is the main pointer running down the list. Front leads it and Back trails it. For each step, reverse the middle pointer and then advance all three to get the next node. */ struct 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 node if (front == NULL) break; // test if done back = middle; // advance the three pointers middle = front; front = front->next; } *headRef = middle; // fix the head pointer to point to the new front }}
RecursiveReverse()
//RecursiveReverse()void RecursiveReverse(struct node** headRef) { struct node* first; struct node* rest; if (*headRef == NULL) return; // empty list base case first = *headRef; // suppose first = {1, 2, 3} rest = first->next; // rest = {2, 3} if (rest == NULL) return; // empty rest base case RecursiveReverse(&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}
- http://cslibrary.stanford.edu/
[^2]: http://cslibrary.stanford.edu/103/ ↩
阅读全文
0 0
- Linked List Basics
- Linked List
- linked list
- linked list
- Linked List
- Linked List
- Linked List
- linked list
- Linked List
- Linked List
- Linked list
- Linked List
- linked list 倒转linked list
- Linked List->single linked list
- Linked List/Singly-Linked List/Doubly-Linked List
- LeetCode Linked List Cycle && Linked List CycleII
- leetcode Linked List Cycle & Linked List Cycle ||
- Linked List Cycle II Linked List Cycle
- Pointers and Memory
- HTML标签
- Unity的 Stats 窗口, Batched、SetPass、Draw Call 等
- 关于Zigbee_CC2530解决串口显示头几个乱码
- pycharm激活码
- Linked List Basics
- 1分钟教会你二进制撩妹(汉)读心术
- MySQL-聚簇索引
- Python第三课 字符串(2)
- day2
- 11.17
- Map转换为JAVABean的方法
- Javascript 2
- 关于NavicatPremium导入CSV文件了乱码