數組作爲存放同類數據的集合,給我們在程序設計時帶來很多的方便,增加了靈活性。但數組也同樣存在一些弊病。如數組的大小在定義時要事先規定,不能在程序中進行調整,這樣一來,在程序設計中針對不同問題有時需要3 0個大小的數組,有時需要5 0個數組的大小, 難於統一。我們只能夠根據可能的最大需求來定義數組,常常會造成一定存儲空間的浪費。 我們希望構造動態的數組,隨時可以調整數組的大小,以滿足不同問題的需要。鏈表就是我們需要的動態數組。它是在程序的執行過程中根據需要有數據存儲就向系統要求申請存儲空間,決不構成對存儲區的浪費。
下圖是單鏈表的結構圖:
單鏈表有一個頭節點h e a d,指向鏈表在內存的首地址。鏈表中的每一個節點的數據類型爲結構體類型,節點有兩個成員:整型成員(實際需要保存的數據)和指向下一個結構體類型節點的指針即下一個節點的地址(事實上,此單鏈表是用於存放整型數據的動態數組)。鏈表按此結構對各節點的訪問需從鏈表的頭找起,後續節點的地址由當前節點給出。無論在表中訪問那一個節點,都需要從鏈表的頭開始,順序向後查找。鏈表的尾節點由於無後續節點,其指針域爲空,寫作爲N U L L。
鏈表中的各節點在內存的存儲地址不是連續的,其各節點的地址是在需要時向系統申請分配的,系統根據內存的當前情況,既可以連續分配地址,也可以跳躍式分配地址。
單鏈表的創建過程有以下幾步:
1 ) 定義鏈表的數據結構。
2 ) 創建一個空表。
3 ) 利用m a l l o c ( )函數向系統申請分配一個節點。
4 ) 將新節點的指針成員賦值爲空。若是空表,將新節點連接到表頭;若是非空表,將新 節點接到表尾。
5 ) 判斷一下是否有後續節點要接入鏈表,若有轉到3 ),否則結束。
下面直接上代碼:
//鏈表結構體定義
typedef struct ListNode
{
int data;
struct ListNode *next;
}ListNode;
int n=0;//全部變量,用來存放鏈表個數
//創建鏈表
ListNode *createListNode()
{
ListNode *head;
ListNode *p1,*p2;
p1=(ListNode*)malloc(sizeof(ListNode));
p2=(ListNode*)malloc(sizeof(ListNode));
head=NULL;
printf("請輸入要插入鏈表的數據:\n");
scanf("%d",&p1->data);
while(p1->data!=0)
{
++n;//當data不爲0時,則鏈表就增加一個數據
if(n==1)
{
head=p1;
}
else
{
p2->next=p1;
}
p2=p1;//p1,p2指向同一個內存
p1=(ListNode*)malloc(sizeof(ListNode));
printf("請輸入要插入鏈表的數據:\n");
scanf("%d",&p1->data);
}
return head;
}
現在鏈表建立成功,那麼如何對單鏈表倒置呢?下面介紹兩種方法來對單鏈表進行倒置:
(1)遞歸方法:即將當前節點放入到下一節點之後
ListNode* reverse(ListNode *node)
{
if(node==NULL)
{
return node;
}
if(node->next==NULL)
{
return node;
}
ListNode *preNode=reverse(node->next);
ListNode *temp=node->next;
temp->next=node;
node->next=NULL;
return preNode;
}
(2)非遞歸方法:即互相交換值
ListNode *Reverse(ListNode *node)
{
if(node==NULL)
{
return node;
}
ListNode *prev=NULL;
while(node->next!=NULL)
{
ListNode *tmp=node->next;
node->next=prev;
prev=node;
node=tmp;
}
}
(3)利用棧來實現:每經過一個結點的時候,把該節點放到一個棧中。當遍歷完整個鏈表以後,再從棧頂開始逐個輸出節點的值,此時輸出的節點的順序已經翻轉過來了。
void PrintListReversingly_Iterator(ListNode* pHead)//利用棧實現(後進先出)
{
stack<ListNode*> nodes;
ListNode* pNode=pHead;
while(pNode!=NULL)
{
nodes.push(pNode);
pNode=pNode->m_pNext;
}
while(!nodes.empty())
{
pNode=nodes.top();
printf("%d\t",pNode->m_nValue);
nodes.pop();
}
}
這個鏈表倒置問題雖然代碼很簡短,但是理解起來並不容易,希望大家可以在紙上畫畫圖加深理解。
下面是完整的代碼
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<stack>
using namespace std;
typedef struct ListNode{
int m_nValue;
ListNode* m_pNext;
}ListNode;
int n=0;
ListNode* CreateListNode()
{
ListNode* phead;
ListNode* p1,*p2;
p1=(ListNode*)malloc(sizeof(ListNode));
p2=(ListNode*)malloc(sizeof(ListNode));
phead=NULL;
printf("請輸入要插入鏈表的數據\n");
scanf("%d\n",&p1->m_nValue);
while(p1->m_nValue!=0)
{
++n;
if(n==1)
{
phead=p1;
}
else{
p2->m_pNext=p1;
}
p2=p1;
p1=(ListNode*)malloc(sizeof(ListNode));
printf("請輸入要插入鏈表的數據\n");
scanf("%d\n",&p1->m_nValue);
}
free(p1);
p1=NULL;
p2->m_pNext=NULL;
printf("鏈表輸入結束(END)\n");
return phead;
}
void PrintListNode(ListNode *phead)
{
ListNode* temp=phead;
while(temp!=NULL)
{
printf("%d\n",temp->m_nValue);
temp=temp->m_pNext;
}
printf("鏈表打印結束(END)\n");
}
ListNode* reverse(ListNode* node)//鏈表倒置(遞歸方法)
{
if(node==NULL)
{
return node;
}
if(node->m_pNext==NULL)
{
return node;
}
ListNode *preNode=reverse(node->m_pNext);
ListNode* temp=node->m_pNext;
temp->m_pNext=node;
node->m_pNext=NULL;
return preNode;
}
ListNode *Reverse(ListNode* node)//非遞歸方法
{
if(node==NULL)
{
return node;
}
ListNode* prev=NULL;
while(node->m_pNext!=NULL)
{
ListNode* tmp=node->m_pNext;
node->m_pNext=prev;
prev=node;
node=tmp;
}
return prev;
}
void PrintListReversingly_Iterator(ListNode* pHead)//利用棧實現(後進先出)
{
stack<ListNode*> nodes;
ListNode* pNode=pHead;
while(pNode!=NULL)
{
nodes.push(pNode);
pNode=pNode->m_pNext;
}
while(!nodes.empty())
{
pNode=nodes.top();
printf("%d\t",pNode->m_nValue);
nodes.pop();
}
}
int main()
{
ListNode* newNode=CreateListNode();
//ListNode* reverse_Node=reverse(newNode);
//PrintListNode(reverse_Node);
PrintListReversingly_Iterator(newNode);
system("pause");
return 0;
}