單鏈表的逆置(頭插法和就地逆置)


今天課間的時候偶然看到了一個面試題:單鏈表的逆置,看了題解感覺乖乖的,貌似和以前看的版本不搭,於是重新進行了一番探究

單鏈表的逆置分為兩種方法:頭插法和就地逆置法,這兩種方法雖然都能夠達到逆置的效果,但還是有着不小的差別

頭插法

      

算法思路:依次取原鏈表中的每一個節點,將其作為第一個節點插入到新鏈表中,指針用來指向當前節點,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;
 }
View Code

程序截圖 

      

就地逆置法

//單鏈表定義 
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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM