第1章 概論
數據結構討論的是數據的邏輯結構、存儲方式以及相關操作的實現等問題。本章講述數據結構的基本概念及相關術語,介紹數據結構、數據類型和抽象數據類型之間的聯繫,介紹了算法的特點及算法的時間與空間複雜性。
1.1數據結構
1.1.1數據結構
隨着計算機軟、硬件的發展,計算機的應用範圍在不斷擴大,計算機所處理的數據的數量也在不斷擴大,計算機所處理的數據已不再是單純的數值數據,而更多的是非數值數據。
需要處理的數據並不是雜亂無章的,它們一定有內在的聯繫,只有弄清楚它們之間的本質的聯繫,才能使用計算機對大量的數據進行有效的處理。
使用計算機處理用戶信息表中的數據時,必須弄清楚下面3個問題:
1) 數據的邏輯結構(各結點的關係)
2) 數據的存儲結構(存入計算機中的結構)
3) 數據的運算集合(相關運算,如刪除、添加等)
數據結構就是指按一定的邏輯結構組成的一批數據,使用某種存儲結構將這批數據存儲於計算機中,並在這些數據上定義了一個運算集合。
1.1.2數據的邏輯結構
例如,有5個人,分別記爲a,b,c,d,e,其中a是b的父親,b是c的父親,c是d的父親,d是e的父親。
如果只討論他們之間所存在的父子關係,則可以用二元組形式化地予以表達:B=(K,R)
其中:K={a,b,c,d,e} R={r} r={<a, b>,<b,c>, <c, d>,<d,e>}
1.1.3數據的存儲結構
對於一個數據結構B=(K,R),必須建立從結點集合到計算機某個存儲區域M的一個映象,這個映象要直接或間接地表達結點之間的關係R。數據在計算機中的存儲方式稱爲數據的存儲結構。
數據的存儲結構主要有4種:
1) 順序存儲
順序存儲通常用於存儲具有線性結構的數據。將邏輯上相鄰的結點存儲在連續存儲區域M的相鄰的存儲單元中,使得邏輯相鄰的結點一定是物理位置相鄰。
2) 鏈式存儲
鏈式存儲方式是給每個結點附加一個指針段,一個結點的指針所指的是該結點的後繼的存儲地址,因爲一個結點可能有多個後繼,所以指針段可以是一個指針,也可以是一個多個指針。
3) 索引存儲
在線性結構中,設開始結點的索引號爲1,其它結點的索引號等於其前繼結點的索引號加1,則每一個結點都有唯一的索引號,索引號就是根據結點的索引號確定該結點的存儲地址。
4) 散列存儲
散列存儲的思想是構造一個從集合K到存儲區域M的一個函數h,該函數的定義域爲K,值域爲M,K中的每個結點ki在計算機中的存儲地址由h(ki)確定。
1.1.4數據的運算集合
對於一批數據,數據的運算是定義在數據的邏輯結構之上的,而運算的具體實現就依賴於數據的存儲結構。
數據的運算集合要視情況而定,一般而言,數據的運算包括插入、刪除、檢索、輸出、排序等。
1.2數據類型和抽象數據類型
在程序設計中,數據和運算是兩個不可缺少的因素。所有的程序設計活動都是圍繞着數據和其上的相關運算而進行的。從機器指令、彙編語言中的數據沒有類型的概念,到現在的面向對象程序設計語言中抽象數據類型概念的出現,程序設計中的數據經歷了一次次抽象,數據的抽象經歷了三個發展階段:
Ø從無類型的二進制數到基本數據類型的產生
Ø從基本數據類型到用戶自定義類型的產生
Ø從用戶自定義類型到抽象數據類型的出現
1.2.1數據類型
數據類型(或簡稱類型)反映了數據的取值範圍以及對這類數據可以施加的運算。
1.2.2數據結構
數據結構包括三個方面的內容:一組數據中各數據之間的邏輯關係;這組數據在計算機中的存儲方式;對這組數據所能施加的運算的集合。數據結構是數據存在的形式。所有的數據都是按照數據結構進行分類的。簡單數據類型對應於簡單的數據結構;構造數據類型對應於複雜的數據結構。
1.2.3抽象數據類型
抽象數據類型是與表示無關的數據類型,是一個數據模型及定義在該模型上的一組運算。對一個抽象數據類型進行定義時,必須給出它的名字及各運算的運算符名,即函數名,並且規定這些函數的參數性質。一旦定義了一個抽象數據類型及具體實現,程序設計中就可以像使用基本數據類型那樣,十分方便地使用抽象數據類型。
1.2.4抽象數據類型的描述和實現
抽象數據類型的描述包括給出抽象數據類型的名稱、數據的集合、數據之間的關係和操作的集合等方面的描述。抽象數據類型的設計者根據這些描述給出操作的具體實現,抽象數據類型的使用者依據這些描述使用抽象數據類型。
抽象數據類型描述的一般形式如下:
ADT 抽象數據類型名稱 { 數據對象: …… 數據關係: …… 操作集合: 操作名1: …… …… 操作名n: }ADT抽象數據類型名稱
1.3 算法和算法分析
1.3.1算法
爲了求解某問題,必須給出一系列的運算規則,這一系列的運算規則是有限的,表達了求解問題方法和步驟,這就是一個算法。
算法具有五個基本特徵:
①有窮性,算法的執行必須在有限步內結束。
②確定性,算法的每一個步驟必須是確定的無二義性的。
③輸入, 算法可以有0個或多個輸入。
④輸出, 算法一定有輸出結果。
⑤可行性,算法中的運算都必須是可以實現的。
算法具有有窮性,程序不需要具備有窮性。一般的程序都會在有限時間內終止,但有的程序卻可以不在有限時間內終止,如一個操作系統在正常情況下是永遠都不會終止的。
1.3.2算法的時間和空間複雜性
一個算法的優劣主要從算法的執行時間和所需要佔用的存儲空間兩個方面衡量,算法執行時間的度量不是採用算法執行的絕對時間來計算的,因爲一個算法在不同的機器上執行所花的時間不一樣,在不同時刻也會由於計算機資源佔用情況的不同,使得算法在同一臺計算機上執行的時間也不一樣,所以對於算法的時間複雜性,採用算法執行過程中其基本操作的執行次數,稱爲計算量來度量。
算法中基本操作的執行次數一般是與問題規模有關的,對於結點個數爲n的數據處理問題,用T(n)表示算法基本操作的執行次數。
在評價算法的時間複雜性時,不考慮兩算法執行次數之間的細小區別,而只關心算法的本質差別。爲此,引入一個所謂的O() 記號,則T1(n)=2n=O(n),T2(n)=n+1=O(n)。
一個函數f(n)是O(g(n))的,則一定存在正常數c和m,使對所有的n>m,都滿足f(n)<c*g(n)。
算法的時間複雜性不僅和問題的規模大小有關,還與問題數據的初始狀態有關。
這樣就有了算法在最好、最壞以及在平均狀態下的時間複雜性的概念。
①算法在最好情況下的時間複雜性是指算法計算量的最小值。
②算法在最壞情況下的時間複雜性是指算法計算量的最大值。
③算法的平均情況下的時間複雜性是指算法在所有可能的情況下的計算量經過加權計算出的平均值。
第2章 線性表及其順序存儲
順序表:
順序表類型的描述如下:
ADT sequence_list{ 數據集合K:K={k1, k2,…, kn},n≥0,K中的元素是datatype類型 數據關係R:R={r},r={ <ki, ki+1>| i=1,2,…,n-1} 操作集合: (1) void init_sequence_list(sequence_list *slt) 順序表的初始化------置空表 (2) void insert_sequence_list(sequence_list *slt,datatype x) 後部插入值爲x結點 (3) void print_sequence_list(sequence_list slt) 打印順序表的各結點值 (4) int is_empty_sequence_list(sequence_list slt) 判斷順序表是否爲空 (5) int find_num_sequence_list(sequence_list slt,datatype x) 查找值爲x結點位置 (6) int get_data_pos(sequence_list slt,int i) 取得順序表中第i個結點的值 (7) void insert_pos_sequence_list(sequence_list *slt,int position,datatype x) (8) void delete_pos_sequence_list(sequence_list *slt,int position) } ADT sequence_list;
// sequlist.h 頭文件 #define MAXSIZE 100 typedef int datatype; typedef struct{ datatype a[MAXSIZE]; int size; }sequence_list;
// seqlinit.c 初始化--置空表 void init_sequence_list(sequence_list *slt) { slt->size = 0; }
// seqlinse.c 後部插入值爲x結點 void insert_sequence_list(sequence_list *slt, datatype x) { if(slt->size==MAXSIZE) { printf("sequence_list is full!"); exit(1); } slt->size=slt->size+1; slt->a[slt->size]=x; }
// seqlprin.c 打印各結點值 void print_sequence_list(sequence_list slt) { int i; if(!slt.size) printf("the sequence_list is empty!\n"); else for(i=0; i<slt.size; i++) printf("%5d", slt.a[i]); }
// seqlempt.c 判斷順序表是否爲空 int is_empty_sequence_list(sequence_list slt) { return(slt.size==0?0:1); }
// seqlfind.c 查找表中值爲x的結點位置 int find_num_sequence_list(sequence_list slt, datatype x) { int i = 0; while(slt.a[i]!=x&&i<slt.size) i++; return(i<slt.size?i:-1); }
// seqlget.c 取得順序表中第i個結點的值 int get_data_pos(sequence_list slt, int i) { if(i<0||i>=slt.size) { printf("wrong node!\n"); exit(1); } else return slt.a[i]; }
// 在順序表的position位置插入值爲x的結點 void insert_pos_sequence_list(sequence_list *slt, int position, datatype x) { int i; if(slt->size==MAXSIZE) { printf("the sequence_list is full!can't insert!\n"); exit(1); } if(position<0||position>slt->size) { printf("wrong position!\n"); exit(1); } for(i=slt->size; i>position; i--) slt->a[i]=slt->a[i-1]; slt->a[position] = x; slt->size++; }
// 刪除順序表中第position位置的結點 void delet_pos_sequence_list(sequence_list *slt, int position) { int i; if(slt->size==0) { printf("the sequence_list is empty!\n"); exit(1); } if(position<0||position>=slt->size) { printf("wrong position!\n"); exit(1); } for(i=position; i<slt->size-1; i--) slt->a[i] = slt->a[i+1]; slt->size--; }
順序棧及其實現:
棧類型的描述如下:
ADT sequence_stack { 數據集合K:K={k1, k2,…, kn},n≥0,K中的元素是datatype類型 數據關係R:R={r} r={ <ki, ki+1>| i=1,2,…,n-1} 操作集合: (1) void init_sequence_stack(sequence_stack *st) (順序存儲)初始化 (2) int is_empty_stack(sequence_stack st) 判斷棧(順序存儲)是否爲空 (3) void print_sequence_stack(sequence_stack st) 打印棧(順序存儲)的結點值 (4) datatype get_top(sequence_stack st) 取得棧頂(順序存儲)結點值 (5) void push(sequence_stack *st,datatype x) 棧(順序存儲)的插入操作 (6) void pop(sequence_stack *st) 棧(順序存儲)的刪除操作 } ADT sequence_stack
設定一個足夠大的一維數組存儲棧,數組中下標爲0的元素就是棧底,對於棧頂,可以設一個指針top指示它。
設定top所指的位置是下一個將要插入的結點的存儲位置,這樣,當top=0時就表示是一個空的棧。
// seqstack.h #define MAXSIZE 100 typedef int datatype; typedef struct{ datatype a[MAXSIZE]; int top; }sequence_stack;
// seqsinit.c void init_sequence_stack(sequence_stack *st) { st->top = 0; }
// seqsempt.c int is_empty_stack(sequence_stack st) { return(st.top?0:1); }
// seqsfirs.c 取得棧頂結點值 datatype get_top(sequence_stack st) { if(is_empty_stack(st)) { printf("the stack is empty!\n"); exit(); } else return st.a[st.top-1]; }
// seqspush.c 棧的插入操作 void push(sequence_stack *st, datatype x) { if(st->top==MAXSIZE) { printf("the sequence stack if full!\n"); exit(1); } st->a[st->top] = x; st->top++; }
// seqspop.c 棧的刪除操作 void pop(sequence_stack *st) { if(st->top==0) { printf("the seqence stack is empty!\n"); exit(1); } st->top--; }
棧的應用——括號匹配
設一個表達式中可以包含三種括號:小括號、中括號和大括號,各種括號之間允許任意嵌套,如小括號內可以嵌套中括號、大括號,但是不能交叉。
int match_kuohao(char c[]) { int i=0; sequence_stack s; init_sequence_stack(&s); while(c[i]!='#') { switch(c[i]) { case '{': case '[': case '(':push(&s,c[i]); break; case '}':if(!is_empty_sequence_stack(s)&&get_top(s)=='{') { pop(&s); break; } else return 0; case ']':if(!is_empty_sequence_stack(s)&&get_top(s)=='[') { pop(&s); break; } else return 0; case ')':if(!is_empty_sequence_stack(s)&&get_top(s)=='(') { pop(&s); break; } else return 0; } i++; } return (is_empty_sequence_stack(s));/*棧空則匹配,否則不匹配*/ }
隊列
隊列的插入和刪除操作分別在表的兩端進行。插入的那一端稱爲隊尾,刪除的那一端稱爲隊首。隊列的插入操作和刪除操作也分別簡稱進隊和出隊。
隊列類型的描述如下:
ADT sequence_queue { 數據集合K:K={k1, k2,…, kn},n≥0,K中的元素是datatype類型 數據關係R:R={r} r={ <ki, ki+1>| i=1,2,…,n-1} 操作集合: (1) void init_sequence_queue(sequence_queue *sq) 隊列(順序存儲)初始化 (2) int is_empty_sequence_queue(sequence_queue sq) 判斷隊列(順序存儲)是否爲空 (3) void print_sequence_queue(sequence_queue sq) 打印隊列(順序存儲)的結點值 (4) datatype get_first(sequence_queue sq) 取得隊列(順序存儲)的隊首結點值 (5) void insert_sequence_queue (sequence_queue *sq,datatype x) 隊列(順序存儲)插入操作 (6) void delete_sequence_queue(sequence_queue *sq) 隊列(順序存儲)的刪除操作 }ADT sequence_queue;
// seqqueue.h #define MAXSIZE 100 typedef int datatype; typedef struct{ datatype a[MAXSIZE]; int front; int rear; }sequence_queue;
// seqqinit.c void init_sequence_queue(sequence_queue *sq) { sq->front = sq->rear = 0; }
// seqqempt.c int is_empty_sequence_queue(sequence_queue sq) { return(sq.front==sq.rear?1:0); }
// seqqprin.c void print_sequence_quence(sequence_queue sq) { int i; if(is_empty_sequence_queue(sq)) { printf("the sequence_queue is empty!"); } else { for(i=sq.front; i<sq.rear; i++) printf("%5d", sq.a[i]); } }
// seqqinse.c void insert_sequence_queue(sequence_queue *sq, datatype x) { int i; if(sq->rear==MAXSIZE) { printf("the sequence_queue is full!"); exit(1); } sq->a[sq->rear] = x; sq->rear = sq->rear + 1; }
// seqqdele.c void delete_sequence_queue(sequence_queue *sq) { if(sq->front==rq->rear) { printf("the sequence_queue is empty!can't delete!\n"); exit(1); } sq->front++; }
循環隊列:
循環隊列滿的條件是:(rear+1)%MAXSIZE=front
循環隊列空的條件是:rear=front
// secqinst.c void insert_sequence_cqueue(sequence_queue *sq,datatype x) { int i; if((sq->rear+1)%MAXSIZE==sq->front) { printf("the sequence_queue is full! can’t insert!"); exit(1); } sq->a[sq->rear]=x; sq->rear=(sq->rear+1)%MAXSIZE; }
// secqdele.c void delete_sequence_cqueue(sequence_queue *sq) { if(sq->front==sq->rear) { printf("the sequence_queue is empty!can't delete!\n"); exit(1); } sq->front=(sq->front+1)%MAXSIZE; }
第3章 線性表的鏈式存儲
3.1鏈式存儲
數據結構的存儲方式必須體現它的邏輯關係 。在鏈式存儲方式下,實現中除存放一個結點的信息外,還需附設指針,用指針體現結點之間的邏輯關係。如果一個結點有多個後繼或多個前驅,那麼可以附設相應個數的指針,一個結點附設的指針指向的是這個結點的某個前驅或後繼。
3.2單鏈表
結點一般含有兩個域,一個是存放數據信息的info域,另一個是指向該結點的後繼結點的存放地址的指針域next。一個單鏈表必須有一個首指針指向單鏈表中的第一個結點。
ADT link_list{ 數據集合K:K={k1, k2,…, kn},n≥0,K中的元素是datatype 類型數據關係R:R={r} r={ <ki, ki+1>| i=1,2,…,n-1} 操作集合: (1) node *init_link_list() 建立一個空的單鏈表 (2) void print_link_list(node *head) 輸出單鏈表中各個結點的值 (3) node *insert_in_front_link_list(node *head,datatype x) 插入一個值爲x的結點作爲單鏈表的第一個結點 (4) node *find_num_link_list(node *head,datatype x) 在單鏈表中查找一個值爲x的結點 (5) node *find_pos_link_list(node *head,int i) 在單鏈表中查找第i個結點 (6) node *insert_x_after_y(node *head,datatype x,datatype y) 在單鏈表中值爲y的結點後插入一個值爲x的新結點 (7) node *insert_x_after_i(node *head,datatype x,int i) 在單鏈表中第i個結點後插入一個值爲x的新結點 (8) node *delete_num_link_list(node *head,datatype x) 在單鏈表中刪除一個值爲x的結點 (9) node *delete_pos_link_list(node *head,int i) 在單鏈表中刪除第i個結點 }ADT link_list;
// slnklist.h typedef int datatype; typedef struct link_node{ datatype info; struct link_node *next; }node;
// 建立一個空的單鏈表 node *init_link_list() { return NULL; }
// slnkprin.c void print_link_list(node *head) { node *p; p=head; if(!p) printf("\nslinklist is empty!"); else { printf("\neach value of node is:\n"); while(p) { printf("%5d",p->info); p=p->next; } } }
// 查找一個值爲x的結點 node *find_num_link_list(node *head,datatype x) { node *p; p=head; while(p&&p->info!=x) p=p->next; return p; }
// 查找第i個結點 node *find_pos_link_list(node *head,int i) { int j=1; node *p=head; if(i<1) { printf("\nError!\n"); exit(1); } while(p&&i!=j) { p=p->next; j++; } return p; }
// 插入一個值爲x的結點作爲單鏈表的第一個結點 node *insert_in_front_link_list(node *head,datatype x) { node *p; p=(node*)malloc(sizeof(node)); /*分配空間*/ p->info=x; /*設置新結點的值*/ p->next=head; /*插入(1)*/ head=p; /*插入(2)*/ return head; }
// 在單鏈表中第i個結點後插入一個值爲x的新結點 node *insert_x_after_i(node *head,datatype x,int i) { node *p,*q; q=find_pos_link_list(head,i);/*查找第i個結點*/ if(!q) { printf("\ncan't find node %d, can't insert!\n",i); exit(1); } p=(node*)malloc(sizeof(node));/*分配空間*/ p->info=x; /*設置新結點*/ p->next=q->next; /*插入(1)*/ q->next=p; /*插入(2)*/ return head; }
// 刪除一個值爲x的新結點 node *delete_num_link_list(node *head,datatype x) { node *pre=NULL,*p; if(!head) { printf("\nthe slinklist is empty!\n"); return head; } p=head; while(p&&p->info!=x)/*沒有找到並且沒有找完*/ { pre=p;p=p->next;}/*pre指向p的前驅結點*/ if(!pre&&p->info==x)/*要刪除的是第一個結點*/ head=head->next;/*刪除(1)*/ else pre->next=p->next; free(p); return head; } 3.3帶頭結點單鏈表一般的單鏈表中,第一個結點由head指示,而在帶頭結點單鏈表中,head指示的是所謂的頭結點,它不是存儲數據結構中的實際結點,第一個實際的結點是head->next指示的。在帶頭結點單鏈表的操作實現時要注意這一點。
ADT hlink_list { 數據集合K:K={k1, k2,…, kn},n≥0,K中的元素是datatype類型 數據關係R:R={r} r={ <ki, ki+1>| i=1,2,…,n-1} 操作集合: (1) node *init_hlink_list() 建立一個空的帶頭結點的單鏈表 (2) void print_hlink_list(node *head) 輸出帶頭結點單鏈表中各個結點的值 (3) node *find_num_hlink_list(node *head,datatype x) 在帶頭結點單鏈表中查找一個值爲x的結點 (4) node *find_pos_hlink_list(node *head,int i) 在帶頭結點單鏈表中查找第i個結點 (5) node *insert_in_front_hlink_list(node *head,datatype x) 插入一個值爲x的結點作爲帶頭結點單鏈表的第一個結點 (6) node *insert_x_after_y(node *head,datatype x,datatype y) 在帶頭結點單鏈表中值爲y的結點後插入一個值爲x的新結點 (7) node *insert_x_after_i(node *head,datatype x,int i) 在帶頭結點單鏈表中第i個結點後插入一個值爲x的新結點 (8) node *delete_num_hlink_list(node *head,datatype x) 在帶頭結點單鏈表中刪除一個值爲x的結點 (9) node *delete_pos_hlink_list(node *head,int i) 在帶頭結點單鏈表中刪除第i個結點 }ADT hlink_list;
// 建立一個空的帶頭結點單鏈表 node *init_hlink_list() { node *head; head=(node*)malloc(sizeof(node)); head->next=NULL; return head; }
// 輸出帶頭結點單鏈表中各個結點的值 void print_hlink_list(node *head) { node *p; p=head->next;/*從第一個(實際)結點開始*/ if(!p) printf("\n帶頭結點單鏈表是空的!"); else { printf("\n帶頭結點的單鏈表各個結點的值爲:\n"); while(p) { printf("%5d",p->info); p=p->next; } } }
// 在帶頭結點單鏈表中查找一個值爲x的結點 node *find_num_hlink_list(node *head,datatype x) { node *p; p=head->next;/*從第一個(實際)結點開始*/ while(p&&p->info!=x) p=p->next; return p; }
// 在帶頭結點單鏈表中查找第i個結點 node *find_pos_hlink_list(node *head,int i) { int j=0; node *p=head; if(i<0) { printf("\n帶頭結點的單鏈表中不存在第%d個結點!",i); return NULL; } while(p&&i!=j)/*沒有查找完並且還沒有找到*/ { p=p->next; j++;/*繼續向後(左)查找,計數器加1*/ } return p;/*返回結果,i=0時,p指示的是頭結點*/ }
// 在帶頭結點單鏈表中值爲y的結點後插入一個值爲x的新結點 node *insert_x_after_y(node *head,datatype x,datatype y) { node *p,*q; q=find_num_hlink_list(head,y);/*查找值爲y的結點*/ if(!q)/*沒有找到*/ { printf("\n沒有找到值爲%d的結點,不能插入%d!",y,x); return head; } p=(node*)malloc(sizeof(node));/*爲準備插入的新結點分配空間*/ p->info=x;/*爲新結點設置值x*/ p->next=q->next;/*插入(1)*/ q->next=p;/*插入(2)*/ return head; }
// 在帶頭結點單鏈表中刪除一個值爲x的結點 node *delete_num_hlink_list(node *head,datatype x) { node *pre=head,*q;/*首先pre指向頭結點*/ q=head->next;/*q從帶頭結點的第一個實際結點開始找值爲x的結點*/ while(q&&q->info!=x)/*沒有查找完並且還沒有找到*/ { pre=q; q=q->next; }/*繼續查找,pre指向q的前驅*/ pre->next=q->next;/*刪除*/ free(q);/*釋放空間*/ return head; } 3.4循環單鏈表對於一個循環單鏈表,若首指針爲head,表中的某個結點p是最後一個結點的特徵應該是p->next==head。循環單鏈表的頭文件和單鏈表的相同。
// 建立一個空的循環單鏈表 node *init_clink_list() { return NULL; }
// 輸出循環單鏈表中各個結點的值 void print_clink_list(node *head) { node *p; if(!head) printf("\n循環單鏈表是空的!\n"); else { printf("\n循環單鏈表各個結點的值分別爲:\n"); printf("%5d",head->info);/*輸出非空表中第一個結點的值*/ p=head->next;/*p指向第一個結點的下一個結點*/ while(p!=head)/*沒有回到第一個結點*/ { printf("%5d",p->info); p=p->next; } } }
// 在循環單鏈表中第i個結點後插入一個值爲x的新結點 node *insert_x_after_i(node *head,datatype x,int i) { node *p,*q; q=find_pos_clink_list(head,i);/*查找第i個結點,q指向第i個結點*/ if(!q)/*沒有找到,則不進行插入*/ printf("\n表中不存在第%d個結點,無法進行插入!\n",i); else { /*找到了第i個結點,準備插入x*/ p=(node*)malloc(sizeof(node));/*分配空間*/ p->info=x;/*設置新結點的值*/ p->next=q->next;/*插入,修改指針(1)*/ q->next=p;/*插入,修改指針(2)*/ } return head; }
// 在循環單鏈表中刪除一個值爲x的結點 node *delete_num_clink_list(node *head,datatype x) { node *pre=NULL,*q;/*q用於查找值爲x的結點,pre指向q的前驅結點*/ if(!head)/*表爲空,則無法做刪除操作*/ { printf("\n循環單鏈表爲空,無法做刪除操作!"); return NULL; } q=head;/*從第1個結點開始準備查找*/ while(q->next!=head&&q->info!=x)/*沒有找遍整個表並且沒有找到*/ { pre=q; q=q->next;/*pre爲q的前驅,繼續查找*/ }/*循環結束後,pre爲q的前驅*/ if(q->info!=x)/*沒找到*/ { printf("沒有找到值爲%d的結點!",x); } else /*找到了,下面要刪除q*/ { pre->next=q->next;/*刪除q指向的結點*/ free(q);/*釋放空間*/ } return head; } 3.5雙鏈表
// 雙鏈表的頭文件 typedef int datatype; typedef struct dlink_node{ datatype info; struct dlink_node *llink,*rlink; }dnode;
// 輸出雙鏈表中各個結點的值 void print_dlink_list(dnode *head) { dnode *p; p=head; if(!p) printf("\n雙鏈表是空的!\n"); else { printf("\n雙鏈表中各個結點的值爲:\n"); while(p) { printf("%5d",p->info); p=p->rlink; } } }
// 查找雙鏈表中第i個結點 dnode *find_pos_dlink_list(dnode *head,int i) { int j=1; dnode *p=head; if(i<1) { printf("\n第%d個結點不存在!\n",i); return NULL; } while(p&&i!=j)/*沒有找完整個表並且沒有找到*/ { p=p->rlink;j++;/*繼續沿着右指針向後查找,計數器加1*/ } if(!p) { printf("\n第%d個結點不存在!\n",i); return NULL; } return p; }
// 在雙鏈表中第i個結點後插入一個值爲x的新結點 dnode *insert_x_after_i(dnode *head,datatype x,int i) { dnode *p,*q; p=(dnode*)malloc(sizeof(dnode));/*分配空間*/ p->info=x;/*設置新結點的值*/ if(i==0)/*在最前面插入一個值爲x的新結點*/ { p->llink=NULL;/*新插入的結點沒有前驅*/ p->rlink=head;/*新插入的結點的後繼是原來雙鏈表中的第一個結點*/ head=p;/*新結點成爲雙鏈表的第一個結點*/ return head; } q=find_pos_dlink_list(head,i);/*查找第i個結點*/ if(!q)/*第i個結點不存在*/ { printf("第%d個結點不存在,無法進行插入",i); return head; } if(q->rlink==NULL)/*在最後一個結點後插入*/ { p->rlink=q->rlink;/*即爲NULL,新插入的結點沒有後繼。插入操作(1)*/ p->llink=q;/*插入操作(2)*/ q->rlink=p;/*插入操作(4)*/ }/*注意不能和下面的一般情況一樣處理,這裏如執行下面的(3)將出錯!*/ else/*一般情況下的插入*/ { p->rlink=q->rlink;/*插入操作(1)*/ p->llink=q;/*插入操作(2)*/ q->rlink->llink=p;/*插入操作(3)*/ q->rlink=p;/*插入操作(4)*/ } return head; }
// 在雙鏈表中刪除一個值爲x的結點 dnode *delete_num_dlink_list(dnode *head,datatype x) { dnode *q; if(!head)/*雙鏈表爲空,無法進行刪除操作*/ { printf("雙鏈表爲空,無法進行刪除操作"); return head; } q=head; while(q&&q->info!=x) q=q->rlink;/*循環結束後q指向的是值爲x的結點*/ if(!q) { printf("\n沒有找到值爲%d的結點!不做刪除操作!",x); } if(q==head&&head->rlink)/*被刪除的結點是第一個結點並且表中不只一個結點*/ { head=head->rlink; head->llink=NULL; free(q);return head; } if(q==head&&!head->rlink)/*被刪除的結點是第一個結點並且表中只有這一個結點*/ { free(q); return NULL;/*雙鏈表置空*/ } else { if(!q->rlink)/*被刪除的結點是雙鏈表中的最後一個結點*/ { q->llink->rlink=NULL; free(q); return head; } else/*q是在一個有2個以上結點的雙鏈表中的一個非開始、也非終端結點*/ { q->llink->rlink=q->rlink; q->rlink->llink=q->llink; free(q); return head; } } } 3.6鏈式棧棧的鏈式存儲稱爲鏈式棧。鏈式棧就是一個特殊的單鏈表,對於這特殊的單鏈表,它的插入和刪除規定在單鏈表的同一端進行。鏈式棧的棧頂指針一般用top表示。鏈式棧類型的描述如下:
ADT link_stack{ 數據集合K:K={k1, k2,…, kn},n≥0,K中的元素是datatype類型 數據關係R:R={r} r={ <ki, ki+1>| i=1,2,…,n-1} 操作集合: (1) node *init_link_stack() 建立一個空鏈式棧 (2) int empty_link_stack(node *top) 判斷鏈式棧是否爲空 (3) datatype get_top(node *top) 取得鏈式棧的棧頂結點值 (4) void print_link_stack(node *top) 輸出鏈式棧中各個結點的值 (5) node *push_link_stack(node *top,datatype x) 向鏈式棧中插入一個值爲x的結點 (6) node *pop_link_stack(node *top) 刪除鏈式棧的棧頂結點 }ADT link_stack;
// 取得鏈式棧的棧頂結點值 datatype get_top(node *top) { if(!top) { printf("\n鏈式棧是空的!"); exit(1); } return(top->info); }
// 判斷鏈式棧是否爲空 int empty_link_stack(node *top) { return (top? 0:1); }
// 輸出鏈式棧中各個結點的值 void print_link_stack(node *top) { node *p; p=top; if(!p) printf("\n鏈式棧是空的!"); while(p) { printf("%5d",p->info); p=p->next; } }
// 向鏈式棧中插入一個值爲x的結點 node *push_link_stack(node *top,datatype x) { node *p; p=(node*)malloc(sizeof(node)); /*分配空間*/ p->info=x; /*設置新結點的值*/ p->next=top; /*插入(1)*/ top=p; /*插入(2)*/ return top; }
// 刪除鏈式棧的棧頂結點 node *pop_link_stack(node *top) { node *q; if(!top) { printf("\n鏈式棧是空的!"); return NULL; } q=top;/*指向被刪除的結點(1)*/ top=top->next;/*刪除棧頂結點(2)*/ free(q); return top; } 3.7鏈式隊列隊列的鏈式存儲稱爲鏈式隊列。鏈式隊列就是一個特殊的單鏈表,對於這種特殊的單鏈表,它的插入和刪除規定在單鏈表的不同端進行。鏈式隊列的隊首和隊尾指針分別用front和rear表示。鏈式隊列類型的描述如下:
ADT link_queue{ 數據集合K:K={k1, k2,…, kn},n≥0,K中的元素是datatype類型 數據關係R:R={r} r={ <ki, ki+1>| i=1,2,…,n-1} 操作集合: (1) queue *init_link_queue() 建立一個空的鏈式隊列 (2) int empty_link_queue(queue qu) 判斷鏈式隊列是否爲空 (3) void print_link_queue(queue *qu) 輸出鏈式隊列中各個結點的值 (4) datatype get_first(queue qu) 取得鏈式隊列的隊首結點值 (5) queue *insert_link_queue(queue *qu,datatype x) 向鏈式隊列中插入一個值爲x的結點 (6) queue *delete_link_queue(queue *qu) 刪除鏈式隊列中隊首結點 }ADT link_queue; 鏈式隊列的結點定義必須有隊首和隊尾指針,因此增加定義一個結構類型,其中的兩個域分別爲隊首和隊尾指針。其定義如下:
typedef struct{ node *front,*rear; /*定義隊首與隊尾指針*/ }queue;
// 建立一個空的鏈式隊列 queue *init_link_queue() { queue *qu; qu=(queue*)malloc(sizeof(queue)); /*分配空間*/ qu->front=NULL; /*隊首指針設置爲空*/ qu->rear=NULL; /*隊尾指針設置爲空*/ return qu; }
// 取得鏈式隊列的隊首結點值 datatype get_first(queue qu) { if(!qu.front) { printf("\n鏈式隊列是空的!"); exit(1); } return(qu.front->info); }
// 向鏈式隊列中插入一個值爲x的結點 queue *insert_link_queue(queue *qu,datatype x) { node *p; p=(node*)malloc(sizeof(node)); /*分配空間*/ p->info=x; /*設置新結點的值*/ p->next=NULL; if(qu->front==NULL) qu->front=qu->rear=p; else { qu->rear->next=p; /*隊尾插入*/ qu->rear=p; } return qu; }
// 刪除隊首結點 queue *delete_link_queue(queue *qu) { node *q; if(!qu->front) { printf("隊列爲空,無法刪除!"); return qu; } q=qu->front; /*q指向隊首結點(1)*/ qu->front=q->next; /*隊首指針指向下一個結點(2)*/ free(q); /*釋放原隊首結點空間*/ if(qu->front==NULL) qu->rear=NULL; /*隊列中的唯一結點被刪除後,隊列變空(3)*/ return qu; }