1. 程式人生 > >資料結構---------------線性表(下篇)之單鏈表

資料結構---------------線性表(下篇)之單鏈表

單鏈表

特點:儲存空間不連續

結點(資料元素組成):資料域(儲存資料)和指標域(指標)A1

若用p來指向  則資料域為p->date   指標域為p->next


鏈式儲存結構: 單鏈表、迴圈連結串列、雙向連結串列根據連結串列結點所含指標個數、指標指向、指標連線方式可將連結串列分為單鏈表、迴圈連結串列、雙向連結串列、二叉連結串列、十字連結串列、鄰接表、鄰接多重表等

非線性結構:二叉連結串列、十字連結串列、鄰接表、鄰接多重表等

//-----------------單鏈表的儲存結構------------

typedef struct LNode
{
	ElemType date;     //結點資料域
	struct LNode *next;//結點指標域 
 } LNode,*LinkList    //LinkList 為指向結構體LNoded的指標型別 

1)對於LNode 和 *LinkList   只寫一個就可以   兩者都是結構體的型別的名稱

區別在於第二個為指標型別    也就是說  如果定義了一個LNode *p  與定義一個LinkList p 是等價的存在;

            ① p為指向單鏈表中某結點的指標,是指標變數,*p代表該結點,是結點變數;

            ②LNode *  

強調定義的是指向單鏈表中任意結點的指標變數,LinkList p 強調定義的是某個單鏈表的頭指標;

2)單鏈表是由表頭指標唯一確定的,換句話說,只要找到單鏈表的頭指標,就能順著找到這串鏈,所以單鏈表可以用頭指標(表中的第一個結點)的名字來命名,若頭指標名是L,則簡稱改連結串列為表L;

辨析   首元結點、頭結點、頭指標

首元結點是指連結串列中儲存第一個資料元素的結點

頭指標是指向連結串列中第一個結點的指標

頭結點是在第一個資料元素之前附近設立的一個結點,其指標域儲存首元結點的地址                                                                                                       作用

:1)便於首元結點的處理    2)便於空表和非空表統一處理                                                                                                                                                                                      當連結串列不設頭結點時  判空條件為L==NULL 設頭結點時  判空條件為L->next==NULL

 有頭結點時 頭指標指向頭結點   無頭結點時  頭指標就指向首元結點

特殊語句

1)p=q;

2)  p=q->next;

3)  p->next=q;

4)  p->next=q->next;

單鏈表基本操作的實現

1)初始化

【演算法描述】

 Status  InitList(LinkList &L)    // 構造一個空的單鏈表 
{
 	L=new LNode;              //生成的新結點作為頭結點,用頭指標L指向頭結點 
	L->next=NULL;             //頭結點的指標置空 
	return 1; 
} 

 Status  InitList(LinkList &L)    // 構造一個空的單鏈表 
{	
	L=NULL;             //不帶頭結點,頭指標置空 
} 

2)求連結串列長度

【演算法描述】

int ListLength(LinkList L)
{
	LinkList p;    //定義一個新指標 
	p=L->next;   //指向第一個資料元素  沒有頭結點時 p=L; 
	int k=0;     //計數器 
	while(p)     //指標不為空,迴圈繼續 
	{
		k++;
		p=p->next;//指標指向下一個結點 
	}
	return k;     //返回連結串列長度 
 } 

【演算法分析】

求連結串列長度時需要將整個鏈跑一邊  所以  時間複雜度為O(n);

3)銷燬連結串列

【演算法描述】

void DestroyList(LinkList &L)
{
 	LinkList p=L->next; //定義新節點並指向頭指標 
 	while(p!=NULL)      //指標不為空 繼續 
	 {
	 	L->next=p->next;//看下圖 
	 	delete p;       //釋放該結點 
	 	p=L->next;      //重複操作 
	  } 
	  delete L;         //釋放頭結點 
} 

L->next=p->next;    //即L的指標域存放的是下下一個結點的地址

【演算法分析】

依舊是要跑整個鏈  所以 時間複雜度為O(n);

4)取值

【演算法分析】


Status GetElem(LinkList L,int i,ElemType &e)
{   //在帶頭結點的單鏈表L中根據序號i獲得元素的值,用e返回L中第i個數據元素的值 
	LinkList p;int j;
	p=L->next;j=1;  //初始化p指向首元結點,計數器j初賦值為1 
	while(p&&j<i)   //順鏈域向後掃描,直到p為空或p指向第i個元素,一共跑i-1次 
	{
		p=p->next;  //指向下一個結點 
		++j;         
	 } 
	 if(!p||j<i) return -2;   //i值不合法i>n 或 i<=n
	  e=p->date;              //取第i個結點的資料域 
 } 

【演算法分析】

最好情況O(1) 最壞O(n)     所以  演算法時間複雜度為 O(n);

5)查詢

【演算法描述】

 LNode *LocateEle(LinkList L,ElemType e)
 {//在帶頭結點的單鏈表L中查詢元素為e的元素 返回地址
     LinkList p;
	 p=L->next;              //初始化,p指向首元結點 
 	 while(p && p->date!=e)  //順鏈域向後掃描,直到p為空或p所指結點資料等於e
	  p=p->next;             //p指向下一個結點 
	return p;                // 查詢成功返回值為e的結點的地址,查詢失敗p為NULL 
  } 
 

【演算法分析】

最好情況O(1) 最壞O(n)     所以  演算法時間複雜度為 O(n);

6)插入

插入分兩種情況 1)p之後插入新節點s  

                           2)p之前插入新節點s      帶頭結點   不帶頭結點 

1) p之後插入新節點s 

 先1後2

【演算法描述】

 void ListAfterInsert(LinkList &L,LNode *p,LNode *s)
 {//在單鏈表L中p結點之後插入一個結點s 
 	s->date =e;
 	s->next =NULL;  //給結點賦初值
	
	s->next =p->next;  //第一步 s的指標域存放p下一個結點的地址
	p->next =s;        //第二步 p的指標域存放s的 
  } 

【演算法分析】

不需要遍歷  時間複雜度為O(1);

 2)p之前插入新節點s      

            帶頭結點 

          【演算法描述】

 void ListAfterInsert(LinkList &L,LNode *p,LNode *s)
 {//在帶頭結點的單鏈表L中p結點之前插入結點s
     LinkList q=L;    //定義新指標並賦予頭指標
	 while(q->next!=p) //判斷是否為p結點的前驅
	    q=q->next;     //指向下一個結點
	q->next=s;         //將s的地址賦給q的指標域 
	s->next=p;         //將p的地址賦給s的指標域 
  } 

           不帶頭結點    比帶頭指標多一種判斷

            【演算法描述】

 void ListAfterInsert(LinkList &L,LNode *p,LNode *s)
 {//在不帶頭結點的單鏈表L中p結點之前插入結點s
    if(p==L)          //判斷是否為頭指標
	 {
	 	s->next =L;   //s指標域存放頭指標地址
		L=s;          //s為頭指標 
      }
    else
	 { 
        LinkList q=L;    //定義新指標並賦予頭指標 
	    while(q->next!=p) //判斷是否為p結點的前驅
	        q=q->next;     //指向下一個結點
	    q->next=s;         //將s的地址賦給q的指標域 
    	s->next=p;         //將p的地址賦給s的指標域 
	 } 
  } 

         【演算法分析】

            要找到插入點之前的結點 所以要遍歷  時間複雜度為O(n);

6)刪除

    帶頭結點

【演算法描述】

 void ListDelete(LinkList &L,LNode *p,Elemtype &e)
 {//在帶頭結點的單鏈表L中刪除p結點 並用e返回p的資料元素 
     LinkList q=L;    //定義新指標並賦予頭指標
	 while(q->next!=p) //判斷是否為p結點的前驅
	    q=q->next;     //指向下一個結點
   	q->next=p->next;   //將p下個結點的地址賦給q的指標域 
	e=p->date;         //e 儲存p的資料元素   
	delete p;          //釋放記憶體 
  }  
 

      不帶頭結點

  【演算法描述】

  void ListDelete(LinkList &L,LNode *p,Elemtype &e)
 {//在帶頭結點的單鏈表L中刪除p結點 並用e返回p的資料元素 
     if(p==L)          //判斷是否為頭指標
	 	L=L->next;     //L的下個結點的地址賦給L 
      
     else
	  { 
         LinkList q=L;    //定義新指標並賦予頭指標
	     while(q->next!=p) //判斷是否為p結點的前驅
	        q=q->next;     //指向下一個結點
   	    q->next=p->next;   //將p下個結點的地址賦給q的指標域
	  } 
	e=p->date;         //e 儲存p的資料元素 	  
	delete p;          //釋放記憶體 
  }  

【演算法分析】

需要找到待刪除結點之前的結點  遍歷  時間複雜度為O(n);

單鏈表的簡單應用(int)

#include<iostream>
using namespace std;
typedef struct Lnode
{
	int date;
	struct Lnode *next;
}Lonode,*linklist;
void Getelem(linklist L,int i,int e)//取值 
{
	Lnode *p;
    p=L->next;
	int j=1;
	while(p&&j<i)
	{
		p=p->next;
		++j;
	}
	e=p->date;
	cout<<"第"<<i<<"個元素的值為:"<<e<<endl<<endl;
}
void Locateelem(linklist L,int e)//查詢 
{
	Lnode *p;
	int i=1;
	p=L->next;
	while(p&&p->date!=e)
	{
		p=p->next;
		i++;
	 } 
	 cout<<"查詢的元素在該連結串列的第"<<i<<"個位置"<<endl;
	 cout<<"查詢元素的地址為:"<<p<<endl<<endl; 
}
void Maxelem(linklist L)//取最大值 
{
	Lnode *p;
	p=L->next->next;
	int max=L->next->date;
	
	while(p)
	{
		max=max>p->date?max:p->date;
		p=p->next;
	}
	cout<<"該連結串列中的最大值為:"<<max<<endl<<endl; 
}
void linkInsert(linklist &L,int i,int e)//插入 
{
	linklist p;
	p=L;
	int j=0;
	while(p&&j<i-1)
	{
		p=p->next;
		++j;
	 } 
	 linklist s;
	 s=new Lnode;
	 s->date =e;
	 s->next=p->next;
	 p->next=s;
	 cout<<"元素"<<e<<"已成功插入!!!"<<endl<<endl; 
}
void linkdelete(linklist &L,int i)//刪除 
{
	linklist p;
	p=L;
	int j=0;
	while(p&&j<i-1)
	{
		p=p->next;
		++j;
	}
	linklist q;
	q=p->next;
	p->next=q->next;
	delete q;
	
	cout<<"第"<<i<<"位置上的元素已成功刪除!!!"<<endl<<endl; 
}
void creatlist_h(linklist &L,int n)//頭插法
{
	cout<<"請倒敘輸入資料:";
	Lnode *p;
    L=new Lnode;
	L->next=NULL;
  for(int i=n;i>0;i--)
  {
	  p=new Lnode;
	  cin>>p->date;
	  p->next=L->next;
	  L->next=p;
  }
}
void creatlist_d(linklist &L,int n)//尾插法
{
	cout<<"請輸入資料:";
	L=new Lnode;
	L->next=NULL;
	Lnode *p;
	linklist r;
	r=L;
	for(int i=0;i<n;i++)
	{
		p=new Lnode;
        cin>>p->date;
		p->next=NULL;
		r->next=p;
		r=p;
	}
}

	
void traverse(linklist L)//遍歷
{
	cout<<"該單鏈表的資料為:";
     Lnode *p=L->next;
	
	while(p)
	{
		cout<<p->date<<" ";
		p=p->next;
	}
	cout<<endl;
}
int main()
{
    	linklist L;
     	int n;
	
	    int m;
		while(1)
		{
		cout<<"1,建立"<<endl;
		cout<<"2,取值"<<endl;
		cout<<"3,查詢"<<endl;
		cout<<"4,取最大值"<<endl;
		cout<<"5,插入"<<endl;
		cout<<"6,刪除"<<endl;
		cout<<"7,遍歷"<<endl;
		cout<<"0,結束"<<endl;
        cout<<"請輸入您的操作:";
		  cin>>m;
		
			if(m==1)
			{
	   	        int p;
    	        cout<<"1,頭插法"<<endl;
    	        cout<<"2,尾插法"<<endl;
	        	cout<<"0,返回上一步 "<<endl<<endl;
	            cout<<"輸入你的選擇:";
    	        cin>>p;
    	        while(p)
    	        {
    	          if(p==1)
		            {
                      cout<<"連結串列長度:";
    	              cin>>n;
    	              creatlist_h(L,n);
	        	     }
    	          else if(p==2)
		            {
                      cout<<"連結串列長度:";
    	              cin>>n;
    	              creatlist_d(L,n); 
		            }
		            cout<<"請輸入你的選擇:";
					cin>>p; 
                 }
			}
			else if(m==2)
			{
				int i,e=0;
				cout<<"請輸入待取值的位置:";
				cin>>i;
				Getelem(L,i,e);
			}
			else if(m==3)
			{
				int e;
				cout<<"請輸入需查詢的元素:";
				cin>>e;
				Locateelem(L,e); 
			}
			else if(m==4)
			{
				Maxelem(L);
			}
			else if(m==5)
			{
				int i,e;
				cout<<"請輸入插入元素的位置:";
				cin>>i;
				cout<<endl<<"請輸入帶插入元素:";
				cin>>e;
				linkInsert(L,i,e); 
			} 
			else if(m==6)
			{
				int i;
				cout<<"請輸入要刪除元素的位置:";
				cin>>i;
				linkdelete(L,i); 
			}
			else if(m==7)
			{
				traverse(L);
			}
			else if(m==0)
			{
				return 0;
			}
			else
			{
				cout<<"輸入錯誤!!!"<<endl; 
			}
			cout<<endl;
		 } 
		
	return 0;
}

 

over~~~~~~~~~~~~

才怪ヽ( ̄▽ ̄)ノ

下篇  順序表和連結串列的比較