单链表的逆置,心中永远的记忆。
单链表的逆置可以有多种实现方法,本文算是对逆置问题的一个总结:
首先是使用三指针方法实现。三指针就是使用三个指针分别记录操作每个结点的前驱结点,本身结点,后驱结点。通过while循环不断的调整顺序,然后逐点后移进行操作。这中国三指针的方式应该是最简单的方式,也应该优先考虑的。
LNode* ReverseList(LNode* head) { if (head == NULL) return NULL; if (head->next == NULL) return head; LNode* pre = NULL; LNode* Cur = head; LNode* Nex = NULL; while (Cur!=NULL) { Nex = Cur->next; Cur->next = pre; pre = Cur; Cur = Nex; } return pre; }因为当年对于单链表的逆置操作,大牛提示我可以考虑递归解决,但是对于递归的方法,自己也是迷糊,要说不明白吧还略懂一二,要说明白吧自己搞还整不动。今天费劲九牛二虎之力总算写了一个解法。
LNode* ReverList(LNode* head,LNode*& headl) { if (head == NULL) return NULL; if (head->next == NULL) { headl = head;//返回结点头 return head; } if (head->next != NULL) { LNode* Node = head; //保留上一个结点 LNode* TempNode = ReverList(head->next,headl); TempNode->next = Node; //逆置 Node->next = NULL; //这句必须有,否则会产生循环链表 return Node;//返回逆置链表的尾结点 } }上面的实现根据以下几点:
1. 对于链表操作要有安全检查,判断头结点是否为空。
2. 递归实现就要有结束点,对于本文的结束点就是寻找到最后一个结点,同时也可理解为链表只有一个结点时直接返回即可。
3.关于递归,返回了最后的结点,必须实现逆置,逆置即改变结点指针的指向,因此还必须保存前一个结点(LNode* Node = head),使用当前结点指向前一个结点。
4.前一个结点的next必须为NULL,否则最后退出时,会产生循环链表。如果输出的时候会输出3->2->1->2->.......->1->........的死循环(对于此问题的解决,可以递归设置两个参数(pre,cur),调用的时候pre=NULL)
5. 在每层递归结束是都要返回逆链表的尾节点,为上一层递归的逆置做准备。
6. 逆链表的第一个结点就是原链表的尾节点,但是对于递归的退出自己仍旧很迷惑,因此添加了head1用来存储逆链表的头结点,使用的引用类型。
总结:很不理想。
收获:递归其实就是一个进栈出栈的过程,进入递归下一层就是新的入栈过程,在新的栈里面可以保存已知的各种信息,就像此题目,不仅可以保存head,还可以保存head->next.当底层函数出栈返回到此栈后,这些保存的信息仍然是可以使用的,而自己最开始只是想到保存一个结点head,head->next需要通过返回值指定,明显多此一举,同时也是对递归的理解不够深刻造成的。
对于链表的每个结点都要明确的它的next,防止出现断链或者循环。对于测试用例可以包括NULL,只有一个结点等特殊情形。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
由于使用引用的方式很不好,也没见递归会使用引用的方式返回,因此参考了踏雪无痕的博客,对自己的程序进行了反思,发现可以在进入递归前就存储好当前结点与下一结点。让递归的返回值始终都是逆链表的首结点。
LNode* ReverList1(LNode* head) { if (head == NULL) return NULL; if (head->next == NULL) { return head; } LNode* Node = head; //保留上一个结点 LNode* Nex = head->next; LNode* HeadNode = ReverList1(Nex); Nex->next = Node; //逆置 Node->next = NULL; //这句必须有,否则会产生循环链表 return HeadNode;//返回逆置链表的尾结点 }本递归的特点:
1.在进入递归时就保存当前结点与下一结点。使用下一结点进行递归结束判断。
2.递归返回值始终携带的是原链表的尾结点,逆链表的首结点。递归的返回过程都没有影响。
3.逆置操作时使用的是在进入递归前就存储好的相邻结点。对返回的首结点没影响。可以考虑为入栈的过程:在每次进栈的时候就存储好当前结点与下一结点,递归就是不断的入栈,递归的返回就是不断的出栈。逆置就是使用存储好的相邻结点来完成逆置操作。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
进栈的时候直接不保存,直接使用
LNode* Re(LNode* head) { if (head ==NULL) return NULL; if (head->next == NULL) return head; LNode* Node = Re(head->next); head->next->next = head; head->next = NULL; return Node; }