編程練習(2)——雙向鏈表反轉


本文中的雙向鏈表,具有一個首指針h,但沒有尾指針,不是循環鏈表。鏈表反轉時,要做兩件事情,一是將數據部分的pre指針和next指針交換值;二是將h指針指向反轉后的頭數據節點指針,並將新鏈表的尾數據節點指針的next(即原鏈表頭數據指針的next)置空。

上代碼:

DLinkedNode.h

#ifndef DLINKEDNODE_H_INCLUDED
#define DLINKEDNODE_H_INCLUDED
#endif // DLINKEDNODE_H_INCLUDED


#include <stdio.h>
#include <stdlib.h>

typedef int DATA;

typedef struct _dNode
{
    DATA data;
    struct _dNode *pre;
    struct _dNode *next;
}DNode,*DLinkedNode;

DLinkedNode Create();
DLinkedNode CreateN(int *N);
void PrintH(DLinkedNode h);//按節點先后順序輸出
int Reverse(DLinkedNode h);

DLinkedNode.c

#include "DLinkedNode.h"

DLinkedNode Create()
{
    DLinkedNode h;
    h=(DLinkedNode)malloc(sizeof(DNode));
    if(h==NULL)
    {
        printf("DLinkedNode:Create:分配內存失敗,程序將返回!\n");
        return NULL;
    }
    else
    {
        h->pre=NULL;
        h->next=NULL;
    }
    return h;
}
DLinkedNode CreateN(int *N)
{
    DLinkedNode h=Create();
    if(h==NULL)
    {
        printf("DLinkedNode:CreateN:分配內存失敗,程序將返回!\n");
        return NULL;
    }
    int i=1;
    DATA d;
    DNode *p=h;
    printf("\nfunc:CreateN:請輸入數據個數:\n");
    if(scanf("%d",N)==1)
    {
        if(*N<1)
        {
            printf("\nfunc:CreateN:您輸入的數字必須是大於0的整數,程序將結束!\n");
            return;
        }
        printf("\nfunc:CreateN:您打算輸入的數據個數為%d,請依次輸入數據,並按回車鍵結束\n",*N);
        while(i<=*N&&scanf("%d",&d)==1)
        {
            DNode *pTemp=(DLinkedNode)malloc(sizeof(DNode));
            if(pTemp!=NULL)
            {
                pTemp->data=d;
                pTemp->pre=p;
                pTemp->next=NULL;
                p->next=pTemp;
                p=p->next;
                i++;
            }
        }
        *N=--i;
        if(*N>=1)
        {
            printf("\nfunc:CreateN:您已經輸入%d個有效數據,以下是您剛才輸入的有效數據:\n",*N);
            PrintH(h);
        }
        else
        {
            printf("\n您輸入的有效數據為0個\n");
        }

    }
    return h;
}
void PrintH(DLinkedNode h)
{
    if(h==NULL||h->next==NULL)
    {
        printf("\nDLinkedNode:PrintH:鏈表不存在或者為空,程序將返回!\n");
        return NULL;
    }
    DNode *p=h->next;
    printf("\n");
    while(p!=NULL)
    {
        printf("%d\t",p->data);
        p=p->next;
    }
    printf("\n");
}
//反轉雙向鏈表
int Reverse(DLinkedNode h)
{
    if(h==NULL||h->next==NULL)
    {
        printf("\nDLinkedNode:Reverse:鏈表不存在或者為空,程序將返回!\n");
        return NULL;
    }
    int i=0;
    DNode *p,*temp;
    //DNode *flag;
    p=h->next;
    while(p!=NULL)
    {
        temp=p->pre;
        p->pre=p->next;
        p->next=temp;
        //flag=p;
        if(p->pre==NULL)break;//此處也可以用預先定義的一個DNode *flag,記錄p=p->pre操作之前的原始p值,結束時將h->next=flag
        else p=p->pre;
        i++;
    }
    h->next->next=NULL;
    h->next=p;
    return i;
}

核心的思路,還是利用一個游標指針p遍歷鏈表並進行指針交換操作,一個臨時指針變量temp存儲交換值。有點類似冒泡排序中的交換值的方式。理解起來也比單鏈表反轉操作要簡單。

需要注意的是,最后一次循環時,由於p會指向p->next,而p->next為NULL,故而會丟失指針位置。為了解決這個問題,可以在循環外先定義一個標記變量flag,記錄每次p指向p->next之前的值,還可以如本代碼中所用的方法,在循環中加一次判斷,如果p->next已經為NULL,則提前結束循環,不再執行p=p->next。

此外,數據節點反轉完畢后,需要將反轉后的尾指針的next置空,然后將h指向反轉后的頭數據節點p。

下面是調試代碼:

main.c

#include "DLinkedNode.h"

void testDLinkedNode();
int main()
{
    printf("\n");
    testDLinkedNode();
    return 0;
}
void testDLinkedNode()
{
    int N;
    DLinkedNode h=CreateN(&N);
    Reverse(h);
    PrintH(h);
}

如果雙向鏈表的h指針的pre指向鏈表數據節點的末尾,即雙向循環鏈表。則無需鏈表反轉操作,直接使用pre指針即可遍歷所有數據節點。但需要判斷終止條件。而這種情況下只能將h指針的數據域作為判斷。還有一種思路,給雙向鏈表一個頭指針h和尾指針t,頭指針pre為NULL,尾指針next為NULL,從而就可以很方便地進行雙向遍歷。

雙向鏈表遍歷方便,但是插入和刪除操作需要多個指針的變動(一般需要變動四個指針)。同時,內存占用也會明顯增加。因而需要考慮實際情況選用。

歡迎交流!


免責聲明!

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



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