第1章 數(shù)據(jù)結(jié)構(gòu)緒論
第2章 算法
第3章 線性表
第1章 數(shù)據(jù)結(jié)構(gòu)緒論
- 程序設計 = 數(shù)據(jù)結(jié)構(gòu) + 算法
邏輯結(jié)構(gòu)與物理結(jié)構(gòu)
邏輯結(jié)構(gòu)
- 集合結(jié)構(gòu)
- 線性結(jié)構(gòu)
- 樹形結(jié)構(gòu)
- 圖形結(jié)構(gòu)
物理結(jié)構(gòu)
- 物理結(jié)構(gòu):是指數(shù)據(jù)的邏輯結(jié)構(gòu)在計算機中的存儲形式。
- 順序存儲結(jié)構(gòu):把數(shù)據(jù)元素存放在地址連續(xù)的存儲單元里,其數(shù)據(jù)間的邏輯關系和物理關系是一致的。
- 鏈式存儲結(jié)構(gòu):把數(shù)據(jù)元素存放在任意的存儲單元里,這組存儲單元可以是連續(xù)的,也可以是不連續(xù)的。
第2章 算法
算法是解決特定問題求解步驟的描述,在計算機中表現(xiàn)為指令的有限序列,并且每條指令表示一個或多個操作。
算法的特性
- 輸入輸出:有零個或多個輸入,至少有一個或多個輸出。
- 有窮性
- 確定性
- 可行性
算法設計的要求
- 正確性
- 可讀性
- 健壯性
- 時間效率高和存儲量低
算法時間復雜度
定義:在進行算法分析時,語句總的執(zhí)行次數(shù)T(n)是關于問題規(guī)模n的函數(shù),進而分析T(n)隨n的變化情況并確定T(n)的數(shù)量級。算法的時間復雜度,也就是算法的時間度量,記作:T(n) = O(f(n))。它表示隨問題規(guī)模n的增大,算法執(zhí)行時間的增長率和f(n)的增長率相同,稱作算法的漸進時間復雜度,簡稱為時間復雜度。其中f(n)是問題規(guī)模n的某個函數(shù)。
這樣用大寫O()來體現(xiàn)算法時間復雜度的記法,稱之為大O記法。
推導大O階
- 用常數(shù)1取代運行時間中的所有加法常數(shù)
- 在修改后的運行次數(shù)函數(shù)中,只保留最高階項。
- 如果最高階項存在且不是1,則去除與這個項相乘的常數(shù)。
得到的結(jié)果就是大O階。
常用的時間復雜度所消耗的時間從小到大依次是:
O(1) < O(logn) < O(n) < O(nlogn) < O(n(2)) < O(n(3)) < O(2(2)) < O(n!) < O(n(n))
算法的空間復雜度
算法的空間復雜度通過計算算法所需的存儲空間實現(xiàn),算法空間復雜度的計算公式記作:S(n) = O(f(n)),其中,n為問題的規(guī)模,f(n)為語句關于n所占存儲空間的函數(shù)。
第3章 線性表
線性表(List):零個或多個數(shù)據(jù)元素的有限序列。
a(i-1)是a(i)的直接前驅(qū)元素,a(i+1)是a(i)的直接后繼元素。
線性表的抽象數(shù)據(jù)類型定義
ADT 線性表(List)
Data
線性表的數(shù)據(jù)對象集合為{a1,a2,...,an},每個元素的類型均為DataType。其中,除第一個元素a1外,每一個元素有且只有一個直接前驅(qū)元素,除了最后一個元素an外,每一個元素有且只有一個直接后繼元素。數(shù)據(jù)元素之間的關系是一對一的關系。
Operation
InitList(*L):初始化操作,建立一個空的線性表L。
ListEmpty(L):若線性表為空,返回true,否則返回false。
ClearList(*L):將線性表清空。
GetElem(L,i,*e):將線性表L中的第i個位置元素返回給e。
LocaEleme(L,i,*e):將線性表L中查找與定位值e相等的元素,如果查找成功,返回該元素在表中序號表示成功;否則,返回0表示失敗。
ListInsert(*L,i,e):在線性表L中的第i個位置插入新元素e。
ListDelete(*L,i,e):在線性表L中的第i個位置元素,并用e返回其值。
ListLength(L):返回線性表L的元素個數(shù)。
endADT
線性表的順序存儲結(jié)構(gòu)
順序存儲定義:指的是用一段地址連續(xù)的存儲單元依次存儲線性表的數(shù)據(jù)元素。
順序存儲方式:一維數(shù)組來實現(xiàn)順序存儲結(jié)構(gòu)。
線性表的順序存儲的結(jié)構(gòu)代碼:
#define MAXSIZE 20 /*存儲空間初始分配量*/
typedef int ElemType;/*ElemType類型根據(jù)實際情況而定,這里假設為int*/
typedef struct
{
ElemType data[MAXSIZE];/*數(shù)組存儲數(shù)據(jù)元素,最大值為MAXSIZE*/
int length;/*線性表當前長度*/
}SQList;
順序存儲結(jié)構(gòu)需要三個屬性:
- 存儲空間的起始位置:數(shù)組data,它的存儲位置就是存儲空間的存儲位置。
- 線性表的最大存儲容量:數(shù)組長度MaxSize。
- 線性表的當前長度:Length。
用數(shù)組存儲順序表意味著要分配固定長度的數(shù)組空間,由于線性表中可以進行插入好人刪除操作,因此分配的數(shù)組空間要大于等于當前線性表的長度。
存儲器中的每個存儲元素都有自己的編號,這個編號成為地址。
順序存儲結(jié)構(gòu)的插入與刪除
- 獲得元素操作
實現(xiàn)GetElem操作,將線性表L中的第i個位置元素值返回。就程序而言,只要i的數(shù)值在數(shù)組下標范圍內(nèi),就是把數(shù)組第i-1下標的值返回即可。
#define ok 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼,如OK等
初始條件:順序線性表L已存在,1<= i <= ListLength(L)
操作結(jié)果:用e返回L中第i個數(shù)據(jù)元素的值
Status GetElem (SqList L, int i, ElemType *e)
{
if(L.length==0 || i>L.length)
return ERROR;
*e=L.data[i-1];
return OK;
}
插入操作
插入算法的思路:
- 如果插入位置不合理,拋出異常;
- 如果線性表長度大于等于數(shù)組長度,則拋出異常或動態(tài)增加容量;
- 從最后一個元素開始向前遍歷到第i個位置,分別將它們都向后移動一個位置;
- 將要插入元素填入位置i處;
- 表長加1;
代碼實現(xiàn):
初始條件:順序線性表L已存在,1<= i <= ListLength(L)
操作結(jié)果:在L中第i個位置之前插入新的數(shù)據(jù)元素e,L的長度加1
Status ListInsert(SqLsit *L, int i,ElemType e)
{
int k;
if (L->length == MAXSIZE)/*順序線性表已經(jīng)滿*/
return ERROR;
if(i<1 || i>L->length+1)/*當i不在范圍內(nèi)時*/
return ERROR;
if(i<=L->length)/*若插入數(shù)據(jù)位置不在表尾*/
{
for(k=L->length-1;k>=i-1;k--)/*將要插入位置后數(shù)據(jù)元素向后移動一位*/
L->data[k+1] = L->data[k];
}
L->data[i-1] = e;/*將新元素插入*/
L-length++;
return OK;
}
刪除操作
算法思路:
- 如果刪除位置不合理,拋出異常;
- 取出刪除元素;
- 從刪除元素位置開始遍歷到最后一個元素位置,分別將它們都向前移動一個位置;
- 表長減1.
線性表順序存儲結(jié)構(gòu)的優(yōu)缺點
優(yōu)點
- 無須為表示表中元素之間的邏輯關系而增加額外的存儲空間
- 可以快速的存取表中任一位置元素
缺點
- 插入和刪除操作需要移動大量元素
- 當線性表長度變化較大時,難以確定存儲空間的容量
- 造成存儲空間的"碎片"
線性表的鏈式存儲結(jié)構(gòu)
為了表示每個數(shù)據(jù)元素ai與其直接后繼數(shù)據(jù)元素ai+1之間的邏輯關系,對數(shù)據(jù)元素ai來說,除了存儲其本身的信息之外,還需存儲一個指示其直接后繼的信息(即直接后繼的存儲位置)。我們把存儲數(shù)據(jù)元素信息的域稱為數(shù)據(jù)域,把存儲直接后繼位置的域稱為指針域。指針域中存儲的信息稱為指針或鏈。
數(shù)據(jù)域 + 指針域 = 結(jié)點
鏈表中第一個結(jié)點的存儲位置叫做頭指針,最后一個結(jié)點指針為“空”,用NULL或^表示。
頭指針與頭結(jié)點的異同
頭指針
- 頭指針是指鏈表指向第一個結(jié)點的指針,若鏈表有頭結(jié)點,則是指向頭結(jié)點的指針
- 頭指針具有標示作用,所以常用頭指針冠以鏈表的名字
- 無論鏈表是否為空,頭指針均不為空。頭指針是鏈表的必要元素。
頭結(jié)點
- 頭結(jié)點是為了操作的統(tǒng)一和方便而設立的,放在第一元素的結(jié)點之前,其數(shù)據(jù)域一般無意義(也可存放鏈表的長度)
- 有了頭結(jié)點,對在第一元素結(jié)點前插入結(jié)點和刪除第一結(jié)點,其操作與其他結(jié)點的操作系統(tǒng)就統(tǒng)一了
- 頭結(jié)點不一定是鏈表必須要素
單鏈表的讀取
獲取鏈表第i個數(shù)據(jù)的算法思路
- 聲明一個結(jié)點p指向鏈表第一個結(jié)點,初始化j從1開始;
- 當j<i時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一結(jié)點,j累加1;
- 若到鏈表末尾p為空,則說明第i個元素不存在;
- 否則查找成功,返回結(jié)點p的數(shù)據(jù)。
單鏈表的插入與刪除
單鏈表第i個數(shù)據(jù)插入結(jié)點的算法思路
- 聲明一節(jié)點p指向鏈表第一個結(jié)點,初始化j從1開始;
- 當j<i時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一結(jié)點,j累加1;
- 若到鏈表末尾p為空,則說明第i個元素不存在;
- 否則查找成功,在系統(tǒng)中生成一個空結(jié)點s;
- 將數(shù)據(jù)元素e賦值給s->data;
- 單鏈接的插入標準語句s -> next = p -> next; p ->next = s;
- 返回成功。
單鏈表第i個數(shù)據(jù)刪除結(jié)點的算法思路
- 聲明一節(jié)點p指向鏈表第一個結(jié)點,初始化j從1開始;
- 當j<i時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一個結(jié)點,j累加1;
- 若到鏈表末尾p為空,則說明第i個元素不存在;
- 否則查找成功,將欲刪除的結(jié)點p->next賦值給q;
- 單鏈表的刪除標準語句p->next = q->next;
- 將q結(jié)點中的數(shù)據(jù)賦值給e,作為返回;
- 釋放q結(jié)點;
- 返回成功。
對于插入或刪除數(shù)據(jù)越頻繁的操作,單鏈表的效率優(yōu)勢就越是明顯
單鏈表的整表創(chuàng)建
單鏈表整表創(chuàng)建的算法思路:
- 聲明一結(jié)點p和計數(shù)器變量i;
- 初始化一空鏈表L;
- 讓L的頭結(jié)點的指針指向NULL,即建立一個帶頭結(jié)點的單鏈表;
- 循環(huán)
- 生成一新結(jié)點賦值給p;
- 隨機生成一數(shù)字賦值給p的數(shù)據(jù)域p->datd;
- 將p插入到頭結(jié)點與前一新結(jié)點之間。
單鏈表的整表刪除
單鏈表整表刪除的算法思路如下:
- 聲明一結(jié)點p和q;
- 將第一個結(jié)點賦值給p;
- 循環(huán)
- 將下一結(jié)點賦值給q;
- 釋放p;
- 將q賦值給p。