本文中的雙向鏈表,具有一個首指針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,從而就可以很方便地進行雙向遍歷。
雙向鏈表遍歷方便,但是插入和刪除操作需要多個指針的變動(一般需要變動四個指針)。同時,內存占用也會明顯增加。因而需要考慮實際情況選用。
歡迎交流!