常用語法:
- 申請內存,並轉換成Person類
Person *p = (Person *) malloc(sizeof(Person));
- 定義結構體
typedef struct {
int age;
char *name;
}Person, *PersonP,Persons[20];
如果定義的時候就是類,那麼想表示指針就得這麼寫:Person *p
如果定義的時候就是指針,那麼想表示指針就得這麼寫: Personp p
- 賦值運算
Person p;
P.age = 12;
PersonP p1 = &p;
p1 -> age = 12;
- 定義常量
#define MAX_VALUE 255;
- 入參中 &l
表示對l進行的修改可以拿到外面去
- 創建數組
int *p = (int *)malloc(5 * sizeof(int));
第1 章 緒論
考試內容:
數據結構在程序設計中的作用;數據結構的主要內容;數據結構的基本概念;算法及算法分析。
考試要求:
1. 理解數據結構的基本概念;算法設計;掌握算法的時間和空間複雜度。
2. 掌握數據結構的定義;算法的描述方法。
數據結構包括:邏輯結構、存儲結構、數據運算
算法描述方式:自然語言描述、僞代碼、程序設計語言、流程圖、SD圖
時間複雜度:語句被執行的次數
空間複雜度:算法消耗的存儲空間
第2章 線性表
考試內容:
線性表的邏輯結構;線性表的順序存儲結構及實現;線性表的鏈接存儲結構及實現;順序表和鏈表的比較。
考試要求:
1. 掌握線性表的概念;線性表的順序存儲結構、鏈式存儲結構及其常用算法。
2. 掌握線性表的邏輯結構;線性表的存儲結構及操作的實現;鏈式存儲結構及其常用算法;雙向循環鏈表。
常用方法:
initList(&l);
length(l);
locateElem(l,e);
getElem(l,i);
listInsert(&l,i,e);
listDelete(&l,i,&e);
printList(l);
empty(l);
destroyList(&l);
線性表包括:順序表和鏈表
#define MAX_SIZE 50
typedef struct{
ElementType data[MAX_SIZE];
int length;
}SqList;
看15頁的圖
typedef struct LNode{
ElementType data;
struct LNode *next;
}LNode,*LinkList;
看29頁的圖
順序表和鏈表的比較:
1.順序表可以順序存取,也可以隨機存取;鏈表只能順序存取
2.順序表的底層是數組,鏈表的底層是鏈表。相鄰元素順序表物理存儲位置相鄰,鏈表存儲位置不相鄰;
3.按值查找,順序表無序時,順序表和鏈表都是o(n)
順序表有序時,順序表爲o(log2n),鏈表爲o(n)
按位置查找,順序表o(1),鏈表o(n)
- 空間分配: 鏈表多了指針的位置,所以存儲密度小
雙向循環鏈表
看35頁的圖
第3章 棧和隊列
考試內容: 基本運算。
2. 掌握遞歸的編程實現;循環隊列和鏈隊列的基本運算。
棧的名字Stack 後進先出
initStack(&s);
stackEmpty(s);
push(&s,x)
pop(&s,&x)
getTop(s,&x)
destroyStack(&s)
#define MAX_SIZE 50
typedef struct{
ElementType data[MAX_SIZE];
int top;
}SqStack;
top是棧頂的指針,初始值是-1,滿棧條件是s.top == MAX_SIZE-1;
入棧:top先+1,然後s.data[s.top] = x
出棧:x = s.data[s.top],s.top再-1
看66頁的圖
棧的表達式求值: 一棵樹的中置表示(人類計算方式)、後置表示(計算機計算方式)
中置表示如何轉變成後置表示?
當前是數字就進棧,當前是符號就出棧兩個,並進行計算,結果進棧
遞歸可以用棧來轉換
隊列的名字Queue 先進先出
initQueue(&q)
queueEmpty(q)
enQueue(&q)
deQuque(&q)
getHead(q)
#define MAX_SIZE 50
typedef struct{
ElementType data[MAX_SIZE];
int front;
int rear;
}SqQueue;
初始時front和rear都是0,隊尾進元素,隊首出元素
入棧,rear位置添加,rear+1
出棧,front位置出值,front+1
看78頁的圖
循環隊列:使用取餘%實現的
初始:front = rear = 0
添加一個元素 rear = (rear + 1)%MAX_SIZE
取出一個元素 front = (front + 1)%MAX_SIZE
隊列長度:(rear-front + MAX_SIZE)%MAX_SIZE
判斷隊列空還是滿?1.犧牲一個隊列單元;2.增加元素個數字段;3.增加tag標識上一步是添加元素還是取出元素
看79頁的圖
鏈隊列
typedef struct{
ElementType data;
LNode *next;
}LinkNode;
typedef struct LinkQuquq{
LinkNode *front;
LinkNode *rear;
}LinkQueue;
看81頁的圖
第4章 字符串和多維數組
考試內容:
字符串;多維數組;矩陣的壓縮存儲
考試要求:
1. 瞭解串的邏輯結構,存儲結構。
2.掌握串定義和存儲方法;串的操作。
字符串的模式匹配KMP算法
- 根據模求next數組
尋找當前位數前面的字符,首尾匹配,獲得匹配數+1
如果前面的字符爲空,則填寫0
2.用模和被匹配字符串進行匹配,不匹配了就去找next數組對應的值,從這一位繼續匹配
第5章 樹和二叉樹
考試內容
樹的邏輯結構;樹的存儲結構;二叉樹的邏輯結構;二叉樹的存儲結構及實現; 二叉樹遍歷的非遞歸算法;樹、森林與二叉樹的轉換。
考試要求:
1.瞭解樹的基本概念;
2.理解二叉樹的性質和存儲結構;掌握遍歷、構造二叉樹和線索二叉樹;
3.理解樹的存儲結構和遍歷;掌握集合的一種表示方法;
4.掌握哈夫曼樹及其應用;
樹的術語:祖先、子孫、孩子、雙親、堂兄弟、度、葉子結點、終端結點、分支結點、非終端結點、根結點、深度、高度、有序樹、無序樹
二叉樹的形態:空二叉樹、只有根結點、只有左子樹、只有右子樹、左右子樹都有
特殊的二叉樹:滿二叉樹、完全二叉樹、二叉排序樹
計算:n0 = n2 + 1;度爲0的結點 = 度爲2的結點 + 1
存儲結構:順序存儲和鏈式存儲,順序存儲浪費空間,一般爲鏈式存儲
typedef struct BiTNode{
ElementType data;
BiTNode *left;
BiTNode *right;
}BiTNode,*BiTree;
二叉樹的遍歷:前序遍歷、中序遍歷、後序遍歷、層序遍歷
遍歷方式需要爲: 遞歸和非遞歸兩種
void preOrder(BiTree b){
if(b){
visit(b);
preOrder(b->lchild);
preOrder(b->rchild);
}
}
前序遍歷:構造棧,棧頂就是需要被訪問的元素
void preOrder(BiTree b){
initStack(s);
BiTree p =b;
while(p || !emptyStack(s)){
If(p){
visit(p);
push(s,p);
p = p->lchild;
}else{
pop(s,p);
p = p->rchild;
}
}
}
中序遍歷
void inOrder(BiTree b){
initStack(s);
BiTree p = b;
While(p || !stackEmpty(s)){
If(p){
push(s,p);
p = p->lchild;
}else{
pop(s,p);
visit(p);
p = p->rchild;
}
}
}
後續遍歷
void postOrder(BiTree b){
initStack(s);
BiTree p = b;
BiTree r = NULL;
while(p || !emptyStack(s)){
if(p){
#有左子樹就直接進棧
push(s,p);
p = p->lchild;
}else{
getTop(s,p)
If(p->rchild && r != p->rchild){
#存在右子樹且右子樹沒有訪問過
p = p->rchild;
push(p);
p = p->lchild;
}else{
#不存在右子樹,或者右子樹已經被訪問過
pop(s,p);
visit(p);
r = p;
p = NULL;
}
}
}
}
層序遍歷
void levelOrder(BiTree b){
initQueue(q);
enQueue(q,b);
while(!emptyQueue(q)){
BiTree p = deQueue(q);
visit(p);
If(p->lchild){
enQueue(q,p->lchild);
}
If(p->rchild){
enQueue(q,p->rchild)
}
}
}
樹和森林和二叉樹的轉化:左孩子,右兄弟
構造二叉樹的方式:
構造二叉樹的意思是說:根據兩種遍歷的結果,獲得二叉樹的邏輯結構
- 先序遍歷+中序遍歷
- 後序遍歷+中序遍歷
- 層序遍歷+中序遍歷
查看142的案例
線索二叉樹:如果當前結點沒有左孩子,左孩子變成直接前驅;如果當前結點沒有右孩子,右孩子變成直接後繼
ltag=0,lchild表示左孩子
ltag =1,lchild指針表示前驅
rtag=0,rchild指針表示右孩子
rtag=1,rchild指針表示後繼
typedef struct ThreadNode{
ElementType data;
int ltag;
int rtag;
struct ThreadNode *lchild;
struct ThreadNode *rchild;
}ThreadNode,*ThreadTree;
前序線索二叉樹、中序線索二叉樹、後序線索二叉樹
如何畫出中序線索二叉樹?
- 中序排序
- 尋找空指針,指向直接前驅或者直接後繼
代碼實現中序線索二叉樹(1.線索化;2.遍歷)
void inThread(ThreadTree &p,ThreadTree &pre){
if(p!=NULL){
inThread(p->lchild,pre);
if(p->lchild == NULL){
#第一個的左結點也被處理了
p->ltag = 1;
p->lchild = pre;
}
if(pre!=NULL && pre->rchild == NULL){
pre->rtag = 1;
pre->rchild = p;
}
pre = p;
inThread(p->rchild,pre);
}
}
//然後最後一個結點進行特殊處理
void createInThread(ThreadThree t){
ThreadTree pre = NULL;
If(t!=NULL){
inThread(T,pre);
pre->rchild = NULL;
pre->rtag = 1;
}
}
//中序線索二叉樹的遍歷
求中序遍歷的第一個結點
ThreadNode * firstNode(ThreadNode *p){
while(p->ltag == 0){
p = p->lchild;
}
return p;
}
ThreadNode *nextNode(ThreadNode *p){
if(p->rtag == 0){
return firstNode(p->rchild)
}else{
return p->rchild;
}
}
void inOrder(ThreadNode *t){
for(ThreadNode *p = firstNode(t) ; p!=NULL ; p=nextNode(p)){
visit(p);
}
}
哈夫曼樹:帶權路徑長度最短的樹
- 將結點和權排在一起
- 選擇權值最小的兩個結點,連接起來,生成一個新的結點和權值
- 重複上面的步驟
查看190頁的圖
哈夫曼編碼:用哈夫曼樹的編碼(可變長度)
左0右1
查看190頁的圖
集合:並查集
S表示森林,Root1表示以Root1爲根的樹,Root2表示以Roo2爲根的樹
並:將Root1和Root2合併變爲Root1:Root2作爲Root1的孩子
查:找到元素x的根是誰
初始化:讓S中所有的元素各自爲一個子集
void init(int s[]){
for(int i=0;i<max_size;i++){
s[i] = -1;
}
}
void find(int s[],int x){
while(s[x] >=0){
x = s[x];
}
return x;
}
void union(int s[],int root1,int root2){
s[root2] = root[1]
}
第6章 圖
考試內容:
圖的邏輯結構;圖的存儲結構及實現;最小生成樹;最短路徑;有向無環圖及其應用。
考試要求:
1.理解圖的基本概念;圖的存儲結構;
2.掌握圖的遍歷及應用{最小生成樹,最短路徑等};拓撲排序和關鍵路徑。
圖G=(V,E);V表示頂點,E表示邊
V={1,2,3}表示有三個頂點
E={<1,2>,<2,1>}表示有1到2,2到1的有向邊
E={(1,2)}表示1到2的無向邊
連通、連通圖、連通分量的概念:
頂點A和頂點B之間有路徑存在,則稱A和B連通;
圖中所有的頂點都是連通的,則稱圖爲連通圖;
一個圖中的極大連通子圖,叫做連通分量。
圖的存儲結構:臨接矩陣法、臨接表法、十字鏈表、臨接多重表
鄰接矩陣
typedef struct {
vertexType vex[MAX_SIZE];
edgeType edge[MAX_SIZE][MAX_SIZE];
int vexnum;
int arcnum;
}MGraph;
臨接表法
邊結點
typedef struct ArcNode{
int adjvex;
struct ArcNode *next;
}ArcNode;
頂點
Typedef struct VNode{
VertexType data;
ArcNode *first;
}VNode,AdjList[MAX_SIZE];
圖
Typedef struct {
AdjList vertexList;
int vexNum;
int arcNum;
}AlGraph;
十字鏈表:(有方向圖)
VNode有data、in、out
ArcNode有inData、outData、in、out
臨接多重表:(無方向圖)
VNode有data、next
ArcNode有inData、in、outData、out
圖的基本操作:
getEdgeValue(G,x,y);
setEdgeValue(G,x,y);
insertVertex(G,x);
deleteVertex(G,x);
addEdge(G,x,y);
removeEdge(G,x,y);
neighbors(G,x);
firstNeighbor(G,x)
nextNeighbor(G,x,y);
adjacent(G,x,y);
圖的遍歷:廣度優先和深度優先
下面是廣度優先的算法,需要一個visited[]數組和一個queue來做輔助
bool visited[MAX_NUM] = false;
void bfsMain(Graph g){
for(int i=0;i<g.vertexNum;i++){
visited[i] = false;
}
for(i=0;i<g.vertexNum;i++){
if(!visited[i]){
bfs(g,i)
}
}
}
void bfs(Graph g,int v){
initQueue(q);
visit(v);
visited[v]=true;
enqueue(v);
while(!emptyQueue(q)){
dequeue(q,v);
for(int i = firstNeighbor(g,v);i>0;i=nextNeighbor(g,v,i)){
if(!visited(i)){
visit(i);
visited[i]=true;
enQueue(i);
}
}
}
}
廣度優先算法分析:
時間複雜度是O(V+E)或者O(V平方);
空間複雜度是O(V)
深度優先:
bool visited[MAX_SIZE];
void dfsMain(Graph g){
for(int i=0;i<g.vertexNum;i++){
visited[i] = false;
for(i=0;i<g.vertexNum;i++){
dfs(g,i);
}
}
}
void dfs(Graph g,int v){
visit(v);
visited[v]=true;
for(int w = firstNeighbor(g,v);w>0;w=nextNeighbor(g,v,w)){
if(!visited[w]){
dfs(g,v);
}
}
}
深度優先算法分析:
時間複雜度是O(V+E)或者O(V平方);
空間複雜度是O(V)
最小生成樹的算法:普里姆和克魯斯卡爾
普里姆:1.任選一點;2.尋找當前圖形的權最短的邊的未連接的點,加入進來;無限循環
克魯斯卡爾:每次都找最小權值的邊,查看當前的邊的兩端點是否在樹內,不在則加入進來
看書239頁
最短路徑的算法:迪傑斯特拉和弗洛伊德
迪傑斯特拉P241頁
求頂點1到其餘頂點的距離:
做表,縱座標是頂點2,3,4,5;集合
橫左邊是第一輪,第二輪,第三輪。。。
每次把上一輪確定進入集合的點加入到已知路徑的中間;求得最短路徑並加入集合
弗洛伊德看總結的案例
迪傑斯特拉和弗洛伊德的算法都是貪心算法,需要先求出當前的路徑中經過的點,再把下一個點加入進來,需要整合計算
拓撲結構
給一個有向的樹形結構,依次寫出沒有指向它的頂點。
關鍵路徑
有四個量:事件最早發生時間、事件最遲發生時間、活動最早開始時間、活動最遲結束時間。
- 畫圖,圓圈和方塊
- 圓圈畫出事件的最早發生時間和最遲發生時間
- 方塊畫出活動的最早開始時間和最遲結束時間
- 做表
第7章 查找技術
考試內容
查找的基本概念、查找算法的性能;線性表的查找技術;樹表的查找技術;散列表的查找技術
考試要求
1掌握順序查找、折半查找和索引查找的方法
2.掌握二叉排序樹的構造方法和二叉平衡樹的建立方法
3.掌握哈希表的構造方法,哈希表在查找不成功時的平均查找長度的計算方法
順序查找: 一般線性表的順序查找、有序表的順序查找、有序表的二分查找
typedef struct SSTable{
ElementType *data;
int length;
}SSTable;
int search(SSTable ss,ElementType key){
ss.data[0] = key;
for(int i = ss.length;ss.data[i]!=key;i--){
//哨兵 + 從後往前找
}
return i;
}
二分查找
int binarySearch(SeqList l,ElementType key){
int low = 0;int high = l.length-1;
int middle;
while(high >= low){
middle = ( low + high ) / 2;
if(l[middle] == key){
return middle;
}
if(l[middle] > key){
high = middle -1;
}else{
low = middle + 1;
}
}
return -1;
}
索引查找又叫做分塊查找
散列表:包括hash函數和處理衝突的方式
常用的散列函數:直接地址法、除留餘數法、數字分析法、平方取中法
處理衝突的方法:開放地址法,拉鍊法
散列表的性能比較:ASL和α
求ASL:1.先寫出具體的數組;2.寫出某個數查詢的次數;3.ASL等於平均查找次數
α裝填因子 = 表中記錄數/數組的長度
二叉排序樹和二叉平衡樹
二叉排序樹:中序排序,左子樹均小於父結點,右子樹均大於父結點
int search(BiTree t,ElementType key){
while(t!=null && t->data!=key){
if(t->data < key){
t = t->rchild;
}else{
t = t->rchild;
}
}
return t;
}
int insertBSTNode(BiTree &t,ElementType key){
if(t == NULL){
t = (BiTree *)malloc(sizeof(BiTree));
t->data = key;
t>lchild = NULL;
t->rchild = NULL;
return 1;
}
if(key == t->data){
return 0;
}else if(key > t->data){
return insertBSTNode(t->rchild,key);
}else if(key < t->data){
return insertBSTNode(t->lchild,key);
}
}
二叉排序樹的刪除操作:
1.如果刪除葉子結點,則直接刪除
2.如果刪除的結點只有1個左孩子或者右孩子,則刪除該結點後孩子頂替
3.如果刪除的結點有左孩子和右孩子,則將直接前驅結點或者直接後繼結點頂替
平衡二叉樹
概念:結點的左子樹和右子樹高度差不超過1
插入:LL LR RL RR四種情況
P186頁 RL和LR都是以RL和LR爲父結點,LL和RR是以L和R爲父節點
第8章 排序技術
考試內容:
排序的基本概念、排序算法的性能;插入排序;交換排序;選擇排序;歸併排序; 分配排序;各種排序方法的比較。
考試要求:
1.掌握各類排序的原理和特徵;
2. 掌握排序的各種算法實現和應用
排序的口訣:
選擇插入歸交基(排序的種類分爲選擇、插入、交換、歸併、基數)
插入裏有直半希(插入排序包括:直接插入、折半、希爾)
交換冒泡快排好(交換排序包括:冒泡、快排)
選出一堆簡單慄(選擇排序包括:簡單選擇、堆排)
穩定:
穩穩的幸福,雞毛插歸殼(插入、歸併)
額外的存儲:
有序插,無序快
O(nlog2n):
快以nlog2n歸堆(快排,歸併、堆排)
插入排序:將數分成三部分:已經排好序的、正在排序的、未排序的
快排:在一堆成績裏面第一次排序就可以得到張碩士的成績排名,需要i,j,pivot三個指針,i在最左邊,pivot在最左邊,j在最有邊。從j開始運動,j向i的方向運動,i向j的方向運動。當j碰到比pivot小的數就停止運動,且將該值放到i上,i碰到比pivot大的數就停止運動,且將該值放到j上。一直運動直到i和j位置相等,此時將pivot賦值到ij位置上。Pivot的排名得以確定。
空間複雜度O(log2n)
堆排序:
堆排序分爲大根堆和小根堆,大根堆就是父節點比子節點大
1.建堆
只處理分支結點的逆層序排序 O(n)
2輸出堆結點,然後將最底層的結點放到根節點,然後建堆
3新增結點放到最底層,然後逆序排序
歸併排序:
1先把兩兩一組排序
2.再把四個一組排序
3.再把八個一組排序
空間複雜度O(n)
基數排序:面向數字進行排序
比如n個三位數字的排序:
需要三次分配三次收集
每次分配需要n次,每次收集需要d(10)次