今天課間的時候偶然看到了一個面試題:單鏈表的逆置,看了題解感覺乖乖的,貌似和以前看的版本不搭,於是重新進行了一番探究
單鏈表的逆置分為兩種方法:頭插法和就地逆置法,這兩種方法雖然都能夠達到逆置的效果,但還是有着不小的差別
頭插法
算法思路:依次取原鏈表中的每一個節點,將其作為第一個節點插入到新鏈表中,指針用來指向當前節點,p為空時結束。
核心代碼
void reverse(node*head) { node*p; p=head->next; head->next=NULL; while(p) { q=p; p=p->next; q->next=head->next; head->next=q; } }
以上面圖為例子,說白了就是不斷的將1后面的節點插入到head后面,即為頭插法
完整代碼

#include<stdio.h> #include<malloc.h> typedef struct node { int data; struct node*next; }node; node*creat() { node*head,*p,*q; char ch; head=(node*)malloc(sizeof(node)); q=head; ch='*'; puts("單鏈表尾插法,?結束"); while(ch!='?') { int a; scanf("%d",&a); p=(node*)malloc(sizeof(node)); p->data=a; q->next=p; q=p; ch=getchar(); } q->next=NULL; return(head); } void print(node*a) { puts("print "); a=a->next; while(a!=NULL) { printf("%d ",a->data); a=a->next; } } void reverse(node*head) { node*p,*q; p=head->next; head->next=NULL; while(p) { q=p; p=p->next; q->next=head->next; head->next=q; } } main() { node*a; a=creat(); print(a); reverse(a); puts("\nhaved reversed:"); print(a); return 0; }
程序截圖
就地逆置法
//單鏈表定義 typedef struct ListNode{ int m_nValue; ListNode* pNext; };
//單鏈表逆置實現
ListNode* ReverseList(ListNode* pHead) { if (pHead == NULL || pHead->pNext == NULL) { retrun pHead; } ListNode* pRev = NULL; ListNode* pCur = pHead; while(pCur != NULL) { ListNode* pTemp = pCur; // 步驟① pCur = pCur->pNext; // 步驟② pTemp->pNext = pRev; // 步驟③ pRev = pTemp; } return pRev; }
具體流程參見博客:http://www.cnblogs.com/dhls231/p/4773555.html
鏈表的翻轉是程序員面試中出現頻度最高的問題之一,常見的解決方法分為遞歸和迭代兩種。最近在復習的時候,發現網上的資料都只告訴了怎么做,但是根本沒有好好介紹兩種方法的實現過程與原理。所以我覺得有必要好好的整理一篇博文,來幫忙大家一步步理解其中的實現細節。
我們知道迭代是從前往后依次處理,直到循環到鏈尾;而遞歸恰恰相反,首先一直迭代到鏈尾也就是遞歸基判斷的准則,然后再逐層返回處理到開頭。總結來說,鏈表翻轉操作的順序對於迭代來說是從鏈頭往鏈尾,而對於遞歸是從鏈尾往鏈頭。
具體實現可以參考這個博主:https://blog.csdn.net/FX677588/article/details/72357389
整體實現代碼:
#include<iostream> using namespace std; struct node{ int val; struct node* next; node(int x) :val(x){} }; /***非遞歸方式***/ node* reverseList(node* H) { if (H == NULL || H->next == NULL) //鏈表為空或者僅1個數直接返回 return H; node* p = H, *newH = NULL; while (p != NULL) //一直迭代到鏈尾 { node* tmp = p->next; //暫存p下一個地址,防止變化指針指向后找不到后續的數 p->next = newH; //p->next指向前一個空間 newH = p; //新鏈表的頭移動到p,擴長一步鏈表 p = tmp; //p指向原始鏈表p指向的下一個空間 } return newH; } /***遞歸方式***/ node* In_reverseList(node* H) { if (H == NULL || H->next == NULL) //鏈表為空直接返回,而H->next為空是遞歸基 return H; node* newHead = In_reverseList(H->next); //一直循環到鏈尾 H->next->next = H; //翻轉鏈表的指向 H->next = NULL; //記得賦值NULL,防止鏈表錯亂 return newHead; //新鏈表頭永遠指向的是原鏈表的鏈尾 } int main() { node* first = new node(1); node* second = new node(2); node* third = new node(3); node* forth = new node(4); node* fifth = new node(5); first->next = second; second->next = third; third->next = forth; forth->next = fifth; fifth->next = NULL; //非遞歸實現 node* H1 = first; H1 = reverseList(H1); //翻轉 //遞歸實現 node* H2 = H1; //請在此設置斷點查看H1變化,否則H2再翻轉,H1已經發生變化 H2 = In_reverseList(H2); //再翻轉 return 0; }