緒論
數據類型
數據類型是一個值的集合和定義在此集合上一組操作的總稱。
(1)原子類型:其值不可再分的數據類型,如int,char,float。
(2)結構類型:其值可以再分解爲若干成分的數據類型。
struct Student
{
long no;
char name[10];
float score;
};
(3)抽象數據類型(ADT):是指一個數學模型以及定義在該模型上的一組操作。
class Student
{
private:
long no;
char name[10];
float score;
public:
Student();
void findnumber(long no);
void findname(char name[]);
void Print();
};
數據結構及其三要素
在任何問題中,數據元素都不是孤立存在的,而是在它們之間存在着某種關係,這種數據元素之間的關係稱爲結構。數據結構是相互之間存在一種或多種特定關係的數據元素的集合。數據結構的三要素:邏輯結構、存儲結構(物理結構)和數據的運算。
邏輯結構是指數據元素之間的邏輯關係,即從邏輯關係上描述數據。它與數據的存儲無關,是獨立於計算機的。數據的邏輯結構分爲線性結構和非線性結構,線性表是典型的線性結構;集合、樹和圖是典型的非線性結構。
存儲結構是指數據結構在計算機中的表示,也稱物理結構。它包括數據元素的表示和關係。數據的存儲結構是邏輯結構用計算機語言的實現。
數據運算是對數據的處理。包括:
(1)算術運算:加減乘除等運算。
(2)邏輯運算:或、且、非等運算。
(3)關係運算:大於、小於、等於、不等於等運算。
(4)數據傳輸:輸入、輸出、賦值等運算。
算法的定義
算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰的”方法”,算法是用系統的方法描述和解決問題的策略。
五個特性
(1)有窮性(Finiteness):算法的有窮性是指算法必須能在執行有限個步驟之後終止;
(2)確切性(Definiteness):算法的每一步驟必須有確切的定義;
(3)輸入項(Input):一個算法有0個或多個輸入,以刻畫運算對象的初始情況,所謂0個輸入是指算法本身定出了初始條件;
(4)輸出項(Output):一個算法有一個或多個輸出,以反映對輸入數據加工後的結果。沒有輸出的算法是毫無意義的;
(5)可行性(Effectiveness):算法中執行的任何計算步驟都是可以被分解爲基本的可執行的操作步,即每個計算步都可以在有限時間內完成(也稱之爲有效性)。
時間複雜度和空間複雜度
同一問題可用不同算法解決,而一個算法的質量優劣將影響到算法乃至程序的效率。算法分析的目的在於選擇合適算法和改進算法。一個算法的評價主要從時間複雜度和空間複雜度來考慮。
算法的時間複雜度是指執行算法所需要的計算工作量。一般來說,計算機算法是問題規模n 的函數f(n),算法的時間複雜度也因此記做:T(n)=Ο(f(n))。因此,問題的規模n 越大,算法執行的時間的增長率與f(n) 的增長率正相關,稱作漸進時間複雜度(Asymptotic Time Complexity)。
算法的空間複雜度是指算法需要消耗的內存空間。其計算和表示方法與時間複雜度類似,一般都用複雜度的漸近性來表示。同時間複雜度相比,空間複雜度的分析要簡單得多。
簡介
瞭解緒論中的相關概念後,接下來將學習數據結構中的線性表。數據結構中的線性表分爲順序表和鏈表,本篇博文先總結順序表的操作和實現,在另一篇博文中再介紹鏈表的操作和實現。由於在數據結構中,棧和隊列可以用順序表實現,也能用鏈表實現。所以本篇先介紹順序表,再介紹棧和隊列的概念,最後給出相應的完整代碼。由於代碼比較多,所以鏈式結構的代碼在下一篇博文中附上。
線性表的定義
線性表是具有相同數據類型的n(n >= 0)個數據元素的有限序列。其中n爲表長,當n = 0時,該線性表是一個空表。若用L命名線性表,其一般表示爲:L = (a1, a2, ..., ai, ai+1, ..., an)。其中a1是唯一的”第一個”數據元素,an是唯一的”最後一個”數據元素。除第一個元素外,每個元素有且僅有一個直接前驅。除最後一個元素外,每個元素有且僅有一個直接後繼。
線性表的特點如下:
(1)表中元素的個數有限。
(2)表中元素具有邏輯上的順序性,在序列中各元素排序有其先後次序,即爲線性結構。
(3)表中元素都是數據元素,每一個元素都是單個元素。
(4)表中元素具有抽象性。即僅討論元素間的邏輯關係,不考慮元素的內容。
線性表是一種邏輯結構,表示元素之間一對一的相鄰關係。順序表和鏈表是指存儲結構。在接觸一種新的數據結構類型時,都應該分別從其邏輯結構、存儲結構和對數據的操作三方面着手。
線性表的順序存儲稱爲順序表。它用的是一組地址連續的存儲單元(數組),依次存儲線性表中的數據元素,從而使得邏輯上相鄰的兩個元素在物理位置上也相鄰。順序表的邏輯順序與其物理順序相同。順序表最主要的特點是隨機訪問。即通過首地址和元素序號可以在O(1)的時間內找到指定的元素。順序表的存儲密度高,每個結點只存儲數據元素。因爲其元素物理上相鄰,所以插入和刪除操作需要移動大量元素。其存儲類型可描述爲:
#define MaxSize 50
typedef struct{
int data[MaxSize]; //順序表元素
int length; //順序表當前長度
}SqList;
棧的定義
棧:是隻允許在一端進行插入或刪除操作的線性表。首先棧是一種線性表,但是限定這種線性表只能在一端進行插入和刪除操作。它的一個明顯的操作特性可以概括爲後進先出。
棧的順序存儲稱爲順序棧,它是利用一組地址連續的存儲單元存放自棧底到棧頂的數據元素,同時設置了一個指針(top)指向它的當前位置。棧的順序存儲類型可描述爲:
#define Maxsize 50
typedef struct{
Elemtype data[Maxsize]; //存放棧中元素
int top; //棧頂指針
}Sqstack;
棧頂指針:S.top,初始化時設置S.top = -1;棧頂元素:S.data[S.top]。
進棧操作:先判斷棧是否爲滿,若不滿,棧頂指針先加1,再送值到棧頂元素。
出棧操作:先判斷棧是否爲空,若不空,先取棧頂元素值,再將棧頂指針減1.
棧空條件:S.top == -1; 棧滿條件:S.top == Maxsize - 1; 棧長:S.top + 1。
由於順序棧的入棧操作受數組上界的約束,當對棧的最大使用空間估計不足時,有可能發生棧上溢,所以要對棧是否爲滿進行判斷。其實棧頂指針也可以設置爲S.top = 0,只不過此時的操作也要發生相應的變化。當棧頂指針設置爲-1,存放數據時要”先加後放”。而棧頂指針設置爲0,存放數據時要”先放後加”。
基本操作:
int Push(Seqstack *S,Stackelemtype e) //入棧
{
if(Isfull(*S))
{
printf("Full!");
}
S->top++;
S->elem[S->top]=e;
return 1;
}
int Pop(Seqstack *S,Stackelemtype *e) //出棧
{
if(Isempty(*S))
{
printf("Empty!");
return 0;
}
*e=S->elem[S->top];
S->top--;
return 1;
}
int Gettop(Seqstack S,Stackelemtype *e) //獲取棧頂元素
{
if(Isempty(S))
{
printf("Empty!");
return 0;
}
*e=S.elem[S.top];
return 1;
}
隊列的定義
隊列:簡稱爲隊,也是一種操作受限的線性表,只允許在表的一端進行插入,而在另外一端進行刪除。其操作特性是先進先出。
隊列的順序存儲
隊列的順序實現是指分配一塊連續的存儲單元存放隊列中的元素,並附設兩個指針front和rear分別指向隊頭元素和隊尾元素的位置。設隊頭指針指向隊頭元素,隊尾指針指向隊尾元素的下一個位置。隊列的順序存儲類型可描述爲:
#define Maxsize 50
typedef struct{
Elemtype data[Maxsize]; //存放隊列元素
int front,rear; //隊頭指針和隊尾指針
}SqQueue;
這裏需要了解循環隊列的概念,將順序隊列想象成一個環狀的空間,即把存儲隊列元素的表從邏輯上看成一個環。當隊首指針Q.front = Maxsize - 1後,再前進一個位置就自動到0,這可以利用除法取餘運算(%)來實現。
初始狀態:Q.front = Q.rear = 0;
隊首指針加1:Q.front = (Q.front + 1)%Maxsize;
隊尾指針加1:Q.rear = (Q.rear + 1)%Maxsize;
爲了區分隊空和隊滿,有三種處理方式:
(1)犧牲一個單元來區分隊空和隊滿,入隊時少用一個隊列單元,這是一種較爲普遍的做法,約定以”隊頭指針在隊尾指針的下一個位置作爲隊滿的標誌”。
隊滿條件爲:(Q.rear + 1)%Maxsize == Q.front。
對空條件爲:Q.front == Q.rear。
隊列中元素的個數:(Q.rear - Q.front + Maxsize)%Maxsize。
(2)類型中增添表示元素個數的數據成員。這樣,則隊空的條件爲Q.size == 0;隊滿的條件爲Q.size == Maxsize。這兩種情況都有Q.front == Q.rear。
(3)類型中添加tag數據成員,以區分是隊滿還是隊空。tag等於0的情況下,若因刪除導致Q.front == Q.rear則爲隊空;tag等於1的情況下,若因插入導致Q.front == Q.rear則爲隊滿。
基本操作:
int Enter(SeqQueue *Q,Queueelemtype e) //入隊
{
if((Q->rear+1)%MAXSIZE==Q->front)
{
printf("Queue is full!");
return 0;
}
Q->element[Q->rear]=e;
Q->rear=(Q->rear+1)%MAXSIZE;
return 1;
}
int Delete(SeqQueue *Q,Queueelemtype *e) //出隊
{
if(Q->front==Q->rear)
{
printf("Queue is empty!");
return 0;
}
*e=Q->element[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
return 1;
}
線性表 Linearlist.c
#include <stdio.h>
#define MAXSIZE 100
typedef int Elemtype;
typedef struct Linearlist
{
Elemtype elem[MAXSIZE];
int last;
}Seqlist;
/*這裏有一點要注意,若傳的參數是Seqlist *L,即傳的是地址,就要寫成
指針形式,L->last和L->elem[], 若傳的是Seqlist L,即一個結構體類型,
就寫成L.last和L.elem[]*/
void Initlist(Seqlist *L)
{
L->last=-1;
}
void Destroylist(Seqlist *L)
{
L->last=-1;
}
void Clearlist(Seqlist *L)
{
L->last=-1;
}
int Emptylist(Seqlist L)
{
if(L.last==-1)
{
return 1;
}
else
{
return 0;
}
}
int Lengthlist(Seqlist L)
{
if(L.last==-1)
{
return 0;
}
else
{
return (L.last+1);
}
}
int Locate(Seqlist L,Elemtype e) //傳的參數是Seqlist L
{
int i=0;
while(i<=L.last&&L.elem[i]!=e)
{
i++;
}
if(i>L.last)
{
return -1;
}
else
{
return(i+1);
}
}
int Getdata(Seqlist L,int i)
{
if(0<i&&i<=(L.last+1))
{
return L.elem[i-1];
}
else
{
return -1;
}
}
int Insertlist(Seqlist *L,int i,Elemtype e) //傳的是指針,寫成L->last和L->elem[]
{
int k;
if((i<1)||(i>L->last+2))
{
return 0;
}
for(k=L->last;k>=i-1;k--)
{
L->elem[k+1]=L->elem[k];
}
L->elem[i-1]=e;
L->last++;
return 1;
}
int Deletelist(Seqlist *L,int i,Elemtype *e)
{
int k;
if(i<1||i>L->last+1)
{
return 0;
}
*e=L->elem[i-1];
for(k=i-1;k<L->last;k++)
{
L->elem[k]=L->elem[k+1];
}
L->last--;
return 1;
}
void Traverselist(Seqlist L)
{
int i=0;
if(!Emptylist(L))
{
for(;i<=(L.last);i++)
{
printf("%4d",L.elem[i]);
}
printf("\n");
}
}
void Merge(Seqlist *LC,Seqlist *LA,Seqlist *LB)
{
int i,j,l;
i=0;
j=0;
l=0;
while(i<=LA->last&&j<=LB->last)
{
if(LA->elem[i]<LB->elem[j])
{
LC->elem[l]=LA->elem[i];
l++;
i++;
LC->last++;
}
}
while(i<=LA->last)
{
LC->elem[l]=LA->elem[i];
i++;
l++;
LC->last++;
}
while(j<=LB->last)
{
LC->elem[l]=LB->elem[j];
l++;
j++;
LC->last++;
}
}
int main()
{
int i,e=0;
Seqlist L,LA,LB,LC;
Initlist(&L);
Initlist(&LA);
Initlist(&LB);
Initlist(&LC);
for(i=1;i<=20;i++)
{
Insertlist(&L,i,i);
Insertlist(&LA,i,i+10);
Insertlist(&LB,i,i+30*2);
}
Traverselist(L);
Traverselist(LA);
Traverselist(LB);
Deletelist(&L,3,&e);
printf("Deleted elem is:%4d\n",e);
Traverselist(L);
e=Locate(L,25);
printf("Get the index of elem:%4d\n",e);
e=Getdata(L,13);
printf("Get the figure of elem:%4d\n",e);
Merge(&LC,&LA,&LB);
Traverselist(LC);
return 0;
}
順序表實現棧 Stack.c
#include <stdio.h>
#define Stacksize 50
typedef int Stackelemtype;
typedef struct Stack
{
Stackelemtype elem[Stacksize];
int top;
}Seqstack;
void Initstack(Seqstack *S)
{
S->top=-1;
}
void Clearstack(Seqstack *S)
{
S->top=-1;
}
int Isempty(Seqstack S)
{
if(S.top==-1)
{
return 1;
}
else
{
return 0;
}
}
int Isfull(Seqstack S)
{
if(S.top==Stacksize-1)
{
return 1;
}
else
{
return 0;
}
}
int Push(Seqstack *S,Stackelemtype e)
{
if(Isfull(*S))
{
printf("Full!");
}
S->top++;
S->elem[S->top]=e;
return 1;
}
int Pop(Seqstack *S,Stackelemtype *e)
{
if(Isempty(*S))
{
printf("Empty!");
return 0;
}
*e=S->elem[S->top];
S->top--;
return 1;
}
int Gettop(Seqstack S,Stackelemtype *e)
{
if(Isempty(S))
{
printf("Empty!");
return 0;
}
*e=S.elem[S.top];
return 1;
}
int main()
{
int i=1;
int c;
//定義棧
Seqstack S;
//初始化棧
Initstack(&S);
//入棧操作
printf("Input:");
while(1)
{
scanf("%d",&c);
if(c==999) break;
Push(&S,c);
}
//獲得棧頂元素
Gettop(S,&c);
printf("The top element of stack is:[%d]\n",c);
//出棧操作
printf("Pop element:");
while(Isempty(S)!=1)
{
Pop(&S,&c);
printf("[%d]",c);
}
printf("\n");
return 0;
}
順序表實現隊列 Squeue.c
#include <stdio.h>
#define MAXSIZE 50
typedef int Queueelemtype;
typedef struct Squeue
{
Queueelemtype element[MAXSIZE];
int front;
int rear;
}SeqQueue;
void Init(SeqQueue *Q)
{
Q->front=Q->rear=0;
}
int Enter(SeqQueue *Q,Queueelemtype e)
{
if((Q->rear+1)%MAXSIZE==Q->front)
{
printf("Queue is full!");
return 0;
}
Q->element[Q->rear]=e;
Q->rear=(Q->rear+1)%MAXSIZE;
return 1;
}
int Delete(SeqQueue *Q,Queueelemtype *e)
{
if(Q->front==Q->rear)
{
printf("Queue is empty!");
return 0;
}
*e=Q->element[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
return 1;
}
int Isempty(SeqQueue Q)
{
if(Q.front==Q.rear)
return 1;
else
return 0;
}
int Get(SeqQueue Q,Queueelemtype *e)
{
if(Q.front==Q.rear)
{
printf("Queue is empty!");
return 0;
}
*e=Q.element[Q.front];
return 1;
}
int main()
{
int a;
SeqQueue Q;
Init(&Q);
printf("Input:");
while(1)
{
scanf("%d",&a);
if(a==999)
{
break;
}
Enter(&Q,a);
}
Get(Q,&a);
printf("Get the element of queue:");
printf("[%d]\n",a);
printf("Numbers of queue:");
while(Isempty(Q)!=1)
{
Delete(&Q,&a);
printf("[%d]",a);
}
printf("\n");
return 0;
}