學會線性表,一篇就夠了

線性表是最常用最典型的線性結構。

簡易目錄:

線性表:

   邏輯特徵

   線性表的類型定義

   存儲結構

         順序存儲表示

             元素存儲位置的計算

             順序表的基本操作實現

             順序表各算法時間複雜度的計算

             C++中的參數傳遞

        鏈式存儲表示

             單鏈表

             雙向鏈表

             循壞鏈表

       單鏈表,循壞鏈表和雙向鏈表的時間效率的比較

       順序表和鏈表的比較

       線性表的合併

           用順序表實現

           用鏈表實現

 

可以看到,線性表是最基礎,也是最簡單的了。所以我們從線性表開始學習。

定義:具有相同特性的數據元素的一個有限序列,例如:

邏輯特徵

線性表的類型抽象數據類型,也稱數據類型)定義

上述的基本操作是邏輯結構上定義的運算,那麼如何實現這些基本運算,就要先確定其存儲結構。

線性表的存儲以及在存儲結構上各操作的實現

線性表的基本操作

操作算法中用到的預定義變量和類型

線性表的存儲結構有四種,其中最基本兩種的存儲結構:順序存儲結構和鏈式存儲結構

線性表的順序存儲表示(也稱爲順序映像):

定義:把邏輯上相鄰的數據元素存儲在物理上相鄰的存儲單元中。

也就是說線性表順序存儲我們可以用一維數組來表示,但由於數組長度不可動態定義

即:

//錯誤寫法 
int n;
scanf("%d",&n);
int a[n];

這樣是不可行的,所以要想用一維數組來表示,就必須再定義一個長度,像以上一樣用結構體,在裏面定義一個長度就好了。

元素存儲位置的計算

我們一般用的都是第二個公式,因爲一般第一個元素的地址(即基地址)我們都知道。

順序表的基本操作實現:

#include<stdio.h>
#include<iostream>
using namespace std;
//1.malloc(m):開闢m字節長度的地址空間,並返回這段空間的首地址
//2.sizeof(x):計算變量x的長度
//3.free(p)函數:釋放指針p所指變量的存儲空間,即徹底刪除一個變量
//以上3個函數都需用到下面的這個頭文件 
#include<stdlib.h> 
//函數的結果狀態代碼 
#define OVERFLOW -2   
#define OK 1   
#define TRUE   1
#define FALSE  0
#define ERROR  0   
//定義 
#define MAXSIZE 100
typedef struct{
	int *elem; //(數組動態分配)//int elem[MAXSIZE];(數組靜態分配)  
	int length;
}SqList;
int InitList(SqList &L);//初始化操作,建立一個空的線性表L 
void DestroyList(SqList &L);//銷燬線性表L 
void ClearList(SqList &L);//清空線性表L 
int ListInsert(SqList &L,int i,int e);//在線性表L中第i個位置插入新元素e 
int ListDelete(SqList &L,int i,int &e);// 刪除L中第i個位置的元素,並用e返回其值
bool ListEmpty(SqList L);//判斷線性表是否爲空,若爲空,返回true,否則,返回false 
int ListLength(SqList L);//返回線性表L中的元素個數 
int LocateElem(SqList L,int e);//L中查找與給定值e相等的元素,若成功,則返回該元素在表中的序號,否則返回0 
int GetElem(SqList L,int i,int &e);//將線性表L中第i個位置的元素返回給e 
int main()
{
	SqList L;//創建線性表L 
	int m=InitList(L);
	if(m)
	cout<<"成功創建空表L"<<endl;
	ListInsert(L,1,3);
	cout<<"已成功插入"<<endl;
	cout<<"插入後的長度:"<<L.length<<endl;
	ListInsert(L,2,6);
	cout<<"已成功插入"<<endl;
	cout<<"插入後的長度:"<<L.length<<endl;
	int e; 
	ListDelete(L,1,e);
	cout<<"被刪除的元素:"<<e<<endl; 
	cout<<"刪除後的長度:"<<L.length<<endl;
	ListEmpty(L);
	if(ListEmpty(L)) 
	cout<<"線性表L爲空"<<endl;
	else
	cout<<"線性表L非空"<<endl;
	int n; 
	ListLength(L);
	cout<<"此時線性表的長度:"<<ListLength(L)<<endl;
	int a=6;
	LocateElem(L,a);
	cout<<"該元素的序號爲:"<<LocateElem(L,a)<<endl;
	GetElem(L,1,e);
	cout<<"線性表L的第1個元素:"<<e<<endl; 
	ClearList(L);
	cout<<"線性表L已清空"<<endl; 
	DestroyList(L);
	cout<<"線性表L已銷燬"<<endl; 
	return 0;
}
//初始化(即建立一個空的順序表) 
int InitList(SqList &L)
{
 
    L.elem=new int[MAXSIZE];                       // 用C++來爲順序表分配空間
	// L.elem=(int*)malloc(sizeof(int)*MAXSIZE);   //用C語言來爲順序表分配空間 
	if(!L.elem) exit(OVERFLOW);                    //exit表程序終止  OVERFLOW可看作“溢出” 
	L.length=0;
	return OK;
}
//銷燬線性表L
void DestroyList(SqList &L)
{
	if(L.elem) delete L.elem;
	 
}
//清空線性表L 
void ClearList(SqList &L)
{
	L.length=0;
}
//在線性表L中第i個位置插入新元素e 
int ListInsert(SqList &L,int i,int e)
{
	if(i<1||i>L.length+1) return ERROR;//插入位置不合法,可以插入在第1到n個位置,也可以插入到表最後(即第n+1個位置) 
 	if(L.length==MAXSIZE) return ERROR;//當前存儲空間已滿 
	for(int j=L.length-1;j>=i-1;j--)
 	{
 	    L.elem[j+1]=L.elem[j];	      //插入位置及以後的元素後移 
	 }
	 L.elem[i-1]=e;                   //將新元素放到第i個位置上,即下標爲i-1的位置上 
	 L.length++;
//平均移動次數n/2,時間複雜度O(n) 
}
//刪除L中第i個位置的元素,並用e返回其值
int ListDelete(SqList &L,int i,int &e)
{
     if( (i<1) || (i>L.length) )       return ERROR;
     e = L.elem[i-1];
     for(int j=i ;j<=L.length-1 ;j++)
        L.elem[j-1]=L.elem[j];        //被刪除元素之後的元素都前移
     --L.length;                      
     return OK;
//平均移動次數(n-1)/2,時間複雜度O(n) 
 }
//判斷線性表是否爲空
bool ListEmpty(SqList L)
{
	if(L.length==0) return true;
	else
	return false;
}
//返回線性表L中的元素個數 
int ListLength(SqList L)
{
	return(L.length);
}
//在L中查找與e相等的元素,並返回該元素在表中的序號
int LocateElem(SqList L,int a)
{
	for(int i;i<L.length;i++)
	if(L.elem[i]==a) return i+1;  //查找成功,返回序號 
	return 0;                    //查找失敗,返回0 
//平均查找次數(n+1)/2,時間複雜度O(n) 
}
//將線性表L中第i個位置的元素返回給e
int GetElem(SqList L,int i,int &e)
{
   	if(i < 1 || i > L.length)  return ERROR;  //i值不合理
   	e=L.elem[i-1];                           
    return OK;
//時間複雜度O(1) 
}

運行結果

順序表各算法時間複雜度的計算:

1.插入:

所以時間複雜度就爲O(n)。(如果不懂,可以參考第一章緒論關於時間複雜度的分析)

2.刪除:

所以時間複雜度爲O(n)

3.查找:

時間複雜度O(n)

同理:

若查找的是第一個元素,則查找1次就行了

若查找的是第n個元素或查找不成功,則都需要查找n次

平均查找次數:(1/n)*((1+n)*n)/2)=(n+1)/2

 C++中的參數傳遞:(主要解釋代碼實現中&的含義)

 1.指針變量做參數:

#include<iostream>
using namespace std;
void swap(int *m,int *n);
void swap2(int *x,int *y);
int main()
{
	int a=3,c=3;
	int b=5,d=5;
	swap(&a,&b);
	swap2(&c,&d);
	cout<<a<<" "<<b<<endl;
	cout<<c<<" "<<d<<endl; 
	return 0;
}
//形參變化影響實參 
void swap(int *m,int *n)
{
	int t;
	t=*m;
	*m=*n;
	*n=t;
}
//形參變化不影響實參 
void swap2(int *x,int *y) 
{
	int *t;
	t=x;
	x=y;
	y=t;
}

運行結果:

2.數組名作參數:

#include<iostream>
using namespace std;
void sub(char b[]);
int main()
{
   char a[10]="hello";
   sub(a);
   cout<<a<<endl; 	
}
void sub(char b[])
{
	b[0]='w';
}

 運行結果:

 3.引用類型做參數:

#include<iostream>
using namespace std;
void swap(int &m,int &n);
int main()
{
	int a=3;
	int b=5;
	swap(a,b);
	cout<<a<<" "<<b<<endl;
}
void swap(int &m,int &n)
{
	int t;
	t=m;
	m=n;
	n=t;
}

運行結果:

可見,我們要修改原來的值時,可用& ,就如順序表代碼實現時,用&L和L的區別就在於L是否被修改。

線性表的鏈式存儲表示

定義:

單鏈表:

頭結點的作用:

頭結點的數據域:

單鏈表的特點:

 單鏈表的基本操作以及代碼實現:(帶頭結點)

#include<iostream>
using namespace std;
//函數的結果狀態代碼 
#define OK 1
#define ERROR  0   
typedef int ElemType ;//ElemType就相當於int 
//定義
typedef struct LNode{    //聲明結點的類型和指向結點的指針類型 
	ElemType data;            //結點的數據域 
	struct LNode *next;  //結點的指針域 ,指向的仍然是這樣的一個結構體 
}LNode,*LinkList;        //LinkList爲指向結構體Lnode的指針類型 
int InitList(LinkList &L);//單鏈表的初始化,即構造一個空表 
void CreatList(LinkList &L,int n);//頭插法建立單鏈表
void CreatList2(LinkList &L,int n);//尾插法建立單鏈表
int DestroyList(LinkList &L);//銷燬單鏈表L 
int ClearList(LinkList &L);//清空單鏈表L 
int ListInsert(LinkList &L,int i,int e);//在單鏈表L中第i個元素之前插入新元素e 
int ListDelete(LinkList &L,int i,int &e);// 刪除L中第i個數據元素,並用e返回其值
bool ListEmpty(LinkList L);//判斷單鏈表是否爲空,若爲空,返回true,否則,返回false 
int ListLength(LinkList L);//求單鏈表的表長 
int LocateElem(LinkList L,int e);//L中查找與給定值e相等的元素,若成功,則返回該元素在表中的序號,否則返回0 
LNode *LocateElem2(LinkList L,int e);//L中查找與給定值e相等的元素,若成功,則返回該元素的地址,否則返回NULL 
int GetElem(LinkList L,int i,int &e);//將單鏈表L中第i個位置的元素返回給e 
int main()
{
	LinkList L; //或LNode *L; 
	int m=InitList(L);
	if(m)
	cout<<"成功創建空表L"<<endl;
	CreatList(L,2);
	cout<<"頭插法創建單鏈表成功"<<endl; 
	CreatList2(L,2);
	cout<<"尾插法創建單鏈表成功"<<endl; 
	ListInsert(L,1,6);
	cout<<"已成功插入"<<endl;
	ListInsert(L,2,9);
	cout<<"已成功插入"<<endl;
	int e; 
	ListDelete(L,1,e);
	cout<<"被刪除的元素:"<<e<<endl; 
	ListEmpty(L);
	if(ListEmpty(L)) 
	cout<<"單鏈表L爲空"<<endl;
	else
	cout<<"單鏈表L非空"<<endl;
	ListLength(L);
	cout<<"此時單鏈表的長度:"<<ListLength(L)<<endl;
	int a=9;
	LocateElem(L,a);
	cout<<"該元素的位置爲:"<<LocateElem(L,a)<<endl;
	int b=9;
	LocateElem2(L,b);
	cout<<"該元素的地址爲:"<<LocateElem2(L,b)<<endl;
	GetElem(L,1,e);
	cout<<"單鏈表L的第1個元素:"<<e<<endl;
	ClearList(L);
	cout<<"單鏈表L已清空"<<endl; 
	DestroyList(L);
	cout<<"單鏈表L已銷燬"<<endl; 
	return 0;
}
//單鏈表的初始化,即構造一個空表 
int InitList(LinkList &L)
{
   L=new LNode;  //或L=(LinkList)malloc(sizeof(LNode));	
   L->next=NULL;
   return OK;
} 
//頭插法建立單鏈表
void CreatList(LinkList &L,int n)
{
   	for(int i=n;i>0;--i)
   	{
   		LinkList p;
   		p=new LNode;
   		cin>>p->data;
   		p->next=L->next;
   		L->next=p;
	}
} 
//尾插法建立單鏈表
void CreatList2(LinkList &L,int n)
{
	LinkList r;//尾指針
	r=L;//尾指針指向頭結點
	for(int i=0;i<n;++i)
	{
		LinkList p;
		p=new LNode;
		cin>>p->data;
		p->next=NULL;
		r->next=p;
		r=p;
	 } 
} 
//判斷鏈表是否爲空 ,若L爲空表,則返回1,否則返回0
bool ListEmpty(LinkList L)
{
	if(L->next) //非空 
	return 0;
	else
	return 1;
}
//銷燬單鏈表L 
int DestroyList(LinkList &L)//(從頭指針開始,依次釋放所有結點) 
{
   LinkList p;//或LNode *p;
   while(L)
   {
     p=L;
     L=L->next;
	 delete p;
   }	
   return OK;
} 
//清空單鏈表L (鏈表依然存在,但無元素,頭指針和頭結點仍然存在) 
int ClearList(LinkList &L)//(依次釋放所有結點,並將頭結點指針域設置爲空) 
{
	LNode *p,*q;
	p=L->next;//從首元結點開始 
	while(p)
	{
		q=p->next;
		delete p;
		p=q;
	}
	L->next=NULL;
	return OK;
}
//在單鏈表L中第i個元素之前插入新元素e 
int ListInsert(LinkList &L,int i,int e)
{
  LinkList p;
  p=L;
  int j=0;
  while(p&&j<i-1)
  {
  	p=p->next;
  	++j;
  }	
  if(!p||j>i-1) return ERROR;
  LinkList s;
  s=new LNode;
  s->data=e;
  s->next=p->next;
  p->next=s;
  return OK;
} 
// 刪除L中第i個數據元素,並用e返回其值
int ListDelete(LinkList &L,int i,int &e)
{
  LinkList p,q;
  p=L;
  int j=0;
  while(p->next&&j<i-1) //尋找第i個結點,並令p指向其前驅 
  {
  	p=p->next;
	++j;
  }	
  if(!p->next||j>i-1) return ERROR;
  q=p->next;//保存被刪結點的地址以備釋放 
  p->next=q->next;//改變被刪節點前驅結點的指針域 
  e=q->data;
  delete q;
  return OK;
} 
//求單鏈表的表長 
int ListLength(LinkList L)
{
  LinkList p;
  p=L->next;
  int i=0;
  while(p)
  {
  	i++;
  	p=p->next;//遍歷單鏈表,統計結點數 
  }	
  return i;
} 
//L中查找與給定值e相等的元素,若成功,則返回該元素在表中的序號,否則返回0 
int LocateElem(LinkList L,int e)
{
	LinkList p;
	int j=1;
	p=L->next;
	while(p&&p->data!=e)//循環結束條件:p爲空時或找到時 
	{
		p=p->next;
		j++;
	}
    if(p) return j;//若找到了,返回其位置
    else
    return 0;//若沒找到,返回0
}
//L中查找與給定值e相等的元素,若成功,則返回該元素的地址,否則返回NULL 
LNode *LocateElem2(LinkList L,int e)
{
	LinkList p;
	p=L->next;
	while(p&&p->data!=e)//循環結束條件:p爲空時或找到時 
	{
		p=p->next;
	}
	return p;//若找到了,則直接返回地址,若沒找到,則此時p爲空了已經,返回空就行 
}
//將單鏈表L中第i個位置的元素返回給e
int GetElem(LinkList L,int i,int &e)
{
  LinkList p;
  p=L->next;
  int j=1;
  while(p&&j<i)//p不爲空,j還沒到i 
  {
  	p=p->next;
  	++j;
  }	
  if(!p||j>i) return ERROR;//沒有找到,元素不存在 
  e=p->data;
  return OK;
} 

運行結果:

算法時間複雜度分析:

 

雙向鏈表

定義

 雙向鏈表的操作以及實現:(除插入和刪除操作,其他的操作與上述單鏈表的操作基本一致)

雙向鏈表的插入:

s->prior=p->prior  //將p的前驅結點地址存到s的前指針域

p->prior->next=s  //將p的前驅結點的指針域指向s

s->next=p  //給s的next賦值p

p->prior=s  //將p的前指針域指向s

雙向鏈表的刪除: 

p->prior->next=p->next  //讓指針變量p的前驅結點的指針域指向其後繼結點

p->next->prior=p->prior  //將指針變量p的前驅結點的地址存到其後繼結點的前指針域

循壞鏈表:

定義:

循壞鏈表的合併:

帶尾指針循壞鏈表的合併的操作:

單鏈表,循壞鏈表和雙向鏈表的時間效率的比較:

順序表和鏈表的比較:

線性表的合併:

有序表合併--用順序表實現:

有序表的合併--用鏈表實現:

 

 

發佈了22 篇原創文章 · 獲贊 26 · 訪問量 8492
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章