链表系列三:操作单链表
1 template<class T> 2 void swap(myList<T> list, ListNode<T>* node1, ListNode<T>* node2) 3 { 4 if(node1 == node2) 5 return; 6 if(node1->next == node2) 7 { 8 ListNode<T>* prev = list.FindPrev(node1); 9 node1->next = node2->next; 10 node2->next = node1; 11 prev->next = node2; 12 } 13 else if(node2->next == node1) 14 { 15 ListNode<T>* prev = list.FindPrev(node2); 16 node2->next = node1->next; 17 node1->next = node2; 18 prev->next = node1; 19 } 20 else 21 { 22 ListNode<T>* prev1 = list.FindPrev(node1); 23 ListNode<T>* prev2 = list.FindPrev(node2); 24 ListNode<T>* temp = node1->next; 25 node1->next = node2->next; 26 node2->next = temp; 27 prev1->next = node2; 28 prev2->next = node1; 29 } 30 }
(1)找出单链表倒数第k个元素
笨方法:先遍历整个单链表获得链表的长度n,然后再遍历第二次获得链表的第n-(k-1)个元素即得到单链表的倒数第k个元素,这样一来就需要遍历两次链表。
改进的方法:遍历一次单链表,单在一次遍历中使用两个指针,其差距为k,这样一来当第一个指针达到最后一个元素时,第二个指针就刚好指向倒数第k个元素,具体的实现如下:
1 template<class T> 2 ListNode<T>* FindKthNode(myList<T> list, int k) 3 { 4 ListNode<T>* ptr1 = list.head; 5 ListNode<T>* ptr2 = list.head; 6 7 for(int i = 0; i < k; i++) 8 { 9 ptr1 = ptr1->next; 10 if(ptr1 == NULL) 11 return NULL; 12 } 13 14 while(ptr1 != NULL) 15 { 16 ptr1 = ptr1->next; 17 ptr2 = ptr2->next; 18 } 19 return ptr2; 20 }
(2)单链表反转
单链表反转就是将每一个结点中指向下一个元素的指针改为指向下一个元素,最后头结点指向原链表中最后一个结点。
1 template<class T> 2 void ReverseList(myList<T> list) 3 { 4 if(list.isEmpty()) 5 return; 6 7 //只有一个结点时不需要反转 8 if(list.head->next->next != NULL) 9 { 10 ListNode<T>* prev = list.head->next; 11 ListNode<T>* curr = prev->next; 12 ListNode<T>* pnext = NULL; 13 prev->next = NULL; 14 15 while(curr->next != NULL) 16 { 17 pnext = curr->next; 18 curr->next = prev; 19 20 prev = curr; 21 curr = pnext; 22 } 23 curr->next = prev; 24 list.head->next = curr; 25 } 26 }
还可以减少局部变量,用更简单的方法实现:
1 void reverse(test* head) 2 { 3 test* pe = head; 4 test* ps = head->next; 5 while(ps) 6 { 7 pe->next = ps->next; 8 ps->next = head; 9 head = ps; 10 ps = pe->next; 11 } 12 }
(3)从头到尾输出单链表
对此问题,有两种比较直接的方法:反转单链表和压栈。反转单链表使得原来的单链表发生了改变,操作比较多;压栈需要额外的维护栈操作。一个至少在代码量少比较简洁的方法是利用递归:
1 template<class T> 2 void ReversePrintRecu(ListNode<T>* node) 3 { 4 if(node->next != NULL) 5 { 6 ReversePrintRecu(node->next); 7 } 8 cout<<node->data<<endl; 9 } 10 11 template<class T> 12 void ReversePrint(myList<T> list) 13 { 14 if(!list.isEmpty()) 15 { 16 ListNode<T>* node = list.head->next; 17 ReversePrintRecu(node); 18 } 19 }
引申:如果要求定义一个函数不适用任何变量求一个字符串的长度,那么也可以使用递归的方式实现:
1 int RecuStrLength(char* str) 2 { 3 return ((*str) != '\0' ? 1 + RecuStrLength(++str) : 0); 4 }
(4)寻找单链表的中间结点
如果链表的长度已知,那么只需要遍历一半的结点就找到了中间结点。在长度未知的情况下,可以通过两个指针来避免两次遍历链表:第一个指针一次走两步,第二个指针则一次走一步,分别称为快指针和慢指针,那么当快指针遍历到最后一个结点时,慢指针刚好遍历到中间结点:
1 template<class T> 2 void FindMidNode(myList<T> list) 3 { 4 if(!list.isEmpty()) 5 { 6 ListNode<T>* ptr1 = list.head->next; 7 ListNode<T>* ptr2 = list.head->next; 8 while(ptr1->next != NULL && ptr1->next->next != NULL) 9 { 10 ptr1 = ptr1->next->next; 11 ptr2 = ptr2->next; 12 } 13 14 if(ptr1->next != NULL)//偶数个结点 中间结点有两个 15 { 16 ptr2->PrintNode(); 17 ptr2->next->PrintNode(); 18 } 19 else //奇数个结点 中间结点有一个 20 ptr2->PrintNode(); 21 } 22 }
(5)单链表排序
a. 冒泡排序:比较相邻两个结点的数据大小,小的往上冒,大的往下沉,一趟比较下来,会有一个大的数据沉到正确的位置,若链表长度为n,那么n-1趟下来后各个元素都会在正确的位置。由于链表不能像数组那样可以随机访问,所以每一趟的比较都是n-1次,总共的比较次数为(n-1)*(n-1):
1 template<class T> 2 void BubbleSort(myList<T> list) 3 { 4 if(!list.isEmpty()) 5 { 6 ListNode<T>* curr = list.head->next; 7 ListNode<T>* node = list.head->next->next; 8 for(; node != NULL; node = node->next) 9 { 10 for(curr = list.head->next; curr->next != NULL; curr = curr->next) 11 { 12 if(curr->data > curr->next->data) 13 { 14 T temp = curr->next->data; 15 curr->next->data = curr->data; 16 curr->data = temp; 17 } 18 } 19 } 20 } 21 }
(6)交换单链表中的任意两个结点
交换单链表中的任意两个结点需要修改两个结点前驱结点的指针以及它们自身的指针,但是如果两个结点为相邻结点则需要特殊处理:
1 template<class T> 2 void swap(myList<T> list, ListNode<T>* node1, ListNode<T>* node2) 3 { 4 if(node1 == node2) 5 return; 6 if(node1->next == node2) 7 { 8 ListNode<T>* prev = list.FindPrev(node1); 9 node1->next = node2->next; 10 node2->next = node1; 11 prev->next = node2; 12 } 13 else if(node2->next == node1) 14 { 15 ListNode<T>* prev = list.FindPrev(node2); 16 node2->next = node1->next; 17 node1->next = node2; 18 prev->next = node1; 19 } 20 else 21 { 22 ListNode<T>* prev1 = list.FindPrev(node1); 23 ListNode<T>* prev2 = list.FindPrev(node2); 24 ListNode<T>* temp = node1->next; 25 node1->next = node2->next; 26 node2->next = temp; 27 prev1->next = node2; 28 prev2->next = node1; 29 } 30 }