《算法导论》第十章----基本数据结构
基本的数据结构是很基础的东西,而且运行时间也很容易看出来,所以本文也是简单地提及一些性质,主要还是通过一些练习来熟悉它们的性质。
(PS:无聊翻开TAOCP的第一卷,发现第二章也是讲一些数据结构,而且讲得很详细。如果明年年初计划可以完成,就应该开始看TAOCP,继续努力吧!)
栈
栈是先进后出(后进先出),就好像洗盘子的时候,你最先放的盘子在最底,下次拿出来洗,就是最后才拿出来。(例子举得有点搓。。。)
具体操作为进栈、出栈(也叫压入、弹出)。
因为是基于数组来实现栈,所以不仅仅要注意下溢(空栈出栈),还要注意上溢(满栈进栈)。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define MAX 6 5 6 typedef struct { 7 int array[MAX]; 8 int top; 9 }Stack; //栈结构体,包含数组和栈顶下标的记录。 10 11 int stack_empty(Stack *S); //判断是否为空栈 12 13 void push(Stack *S, int x); //进栈操作 14 15 int pop(Stack *S); //出栈操作 16 17 int main(){ 18 Stack *S; 19 S->top = -1; 20 //stack_empty(S); 21 pop(S); 22 int i, num; 23 for(i = 0; i <= 5; i++){ 24 scanf("%d", &num); 25 push(S, num); 26 } 27 28 int p = pop(S); 29 printf("%d\n", p); 30 31 return 0; 32 } 33 34 /* 35 * 栈顶初始为-1,判断是否为-1 36 */ 37 int stack_empty(Stack *S){ 38 if(S->top == -1) 39 return 1; 40 else 41 return 0; 42 } 43 44 /* 45 * 进栈操作,如果栈顶的下标刚好为数组的结尾,就提示上溢,不能再进栈 46 */ 47 void push(Stack *S, int x){ 48 if(S->top+1 == MAX){ 49 printf("overflow\n"); 50 return ; 51 } 52 else{ 53 S->top++; 54 S->array[S->top] = x; 55 } 56 } 57 58 /* 59 * 出栈操作,如果栈为空,提示下溢,不能出栈 60 */ 61 int pop(Stack *S){ 62 if(stack_empty(S)){ 63 printf("underflow\n"); 64 return ; 65 } 66 else{ 67 //S->top--; 68 return S->array[S->top--]; 69 } 70 }
练习10.1-2
说明如何用一个数组A[1..n]来实现两个栈,使得两个栈中的元素总数不到n时,两者都不会发生上溢。注意PUSH和POP操作的时间应为O(1)
分别将数组的头和尾作为单独的两个栈。见代码。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define MAX 12 5 6 typedef struct { 7 int array[MAX]; 8 int top_1; 9 int top_2; 10 //int which; 11 }T_Stack; //栈结构,一个数组,两个栈顶下标(一头一尾) 12 13 int empty(T_Stack *T_S, int which); //判断栈是否为空,which为1对应数组头的那个栈,which为2对应数组尾的那个栈 14 15 void push(T_Stack *T_S, int which, int x); //进栈操作 16 17 int pop(T_Stack *T_S, int which); //出栈操作 18 19 int main(){ 20 int i, num; 21 T_Stack *T_S; 22 T_S->top_1 = -1; 23 T_S->top_2 = MAX; 24 25 for(i = 1; i <= 6; i++){ 26 scanf("%d", &num); 27 push(T_S, 1, num); 28 } 29 30 for(i = 1; i <= 6; i++){ 31 scanf("%d", &num); 32 push(T_S, 2, num); 33 } 34 35 int po = pop(T_S, 1); 36 printf("%d\n", po); 37 38 po = pop(T_S, 2); 39 printf("%d\n", po); 40 41 42 return 0; 43 } 44 45 int empty(T_Stack *T_S, int which){ 46 if(which == 1){ 47 if(T_S->top_1 == -1) 48 return 1; 49 else 50 return 0; 51 } 52 53 else if(which == 2){ 54 if(T_S->top_2 == MAX) 55 return 1; 56 else 57 return 0; 58 } 59 } 60 61 void push(T_Stack *T_S, int which, int x){ 62 if(T_S->top_1 + 1 == T_S->top_2){ 63 printf("overflow\n"); 64 return ; 65 } 66 67 if(which == 1){ 68 T_S->top_1++; 69 T_S->array[T_S->top_1] = x; 70 } 71 else if(which == 2){ 72 T_S->top_2--; 73 T_S->array[T_S->top_2] = x; 74 } 75 } 76 77 int pop(T_Stack *T_S, int which){ 78 if(which == 1){ 79 if(empty(T_S, which)){ 80 printf("underflow\n"); 81 return ; 82 } 83 else{ 84 //T_S->top1--; 85 return T_S->array[T_S->top_1--]; 86 } 87 } 88 else if(which == 2){ 89 if(empty(T_S, which)){ 90 printf("underflow\n"); 91 return ; 92 } 93 else{ 94 //T_S->top2++; 95 return T_S->array[T_S->top_2++]; 96 } 97 } 98 }
队列
队列是先进先出,就和排队一样,排头的比排尾的先。
具体操作为入队、出队。
同样是因为基于数组来实现(循环数组),所以要注意下溢(队列为空,再出队)和上溢(队列为满,再入队)。(PS:代码已添加处理上下溢问题,练习10.1-4)
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define MAX 7 5 6 typedef struct { 7 int array[MAX]; 8 int head; 9 int tail; 10 }Queue; //队列结构体,一个数组,一个队首下标,一个队尾下标 11 12 void enqueue(Queue *Q, int x); //进队列 13 14 int dequeue(Queue *Q); //出队列 15 16 int main(){ 17 int i, num; 18 Queue *Q; 19 Q->head = Q->tail = 0; 20 dequeue(Q); 21 for(i = 1; i <= 6; i++){ 22 scanf("%d", &num); 23 enqueue(Q, num); 24 } 25 26 for(i = 1; i <= 6; i++){ 27 int de = dequeue(Q); 28 printf("%d\n", de); 29 } 30 if(Q->head == Q->tail) 31 printf("Yes\n"); 32 return 0; 33 } 34 35 /* 36 * 如果队首下标等于队尾下标+1,表示上溢,不能再进队列。 37 * 如果队尾下标在添加元素后等于数组的末端,那么就要回到数组的前端(前提未上溢) 38 */ 39 void enqueue(Queue *Q, int x){ 40 if(Q->head == ((Q->tail + 1) % MAX)){ 41 printf("overflow\n"); 42 return ; 43 } 44 45 else{ 46 Q->array[Q->tail] = x; 47 Q->tail = (Q->tail + 1) % MAX; 48 } 49 } 50 51 /* 52 * 如果队首下标等于队尾下标,表示队列为空,不能出队列 53 * 如果队首小表在出队列后等于数组末端,那么就要回到数组的前端 54 */ 55 int dequeue(Queue *Q){ 56 if(Q->head == Q->tail){ 57 printf("underflow\n"); 58 return ; 59 } 60 int x = Q->array[Q->head]; 61 if(Q->head == MAX-1) 62 Q->head = 0; 63 else 64 Q->head++; 65 66 return x; 67 }
练习10.1-5
栈的插入和删除都在同一端进行,而队列的插入和删除却在两头进行。另有一种双端队列,其两端都可以左插入和删除操作。对于一个用数组构造的双端队列,请写出四个在两端进行插入和删除操作的过程,要求运行时间都为O(1)
PS:已添加处理上下溢问题
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define MAX 15 5 6 typedef struct { 7 int array[MAX]; 8 int head; 9 int tail; 10 }Deque; //双端队列结构体,一个数组、一个头下标、一个尾下标 11 12 void H_enqueue(Deque *D, int x); //从队列头进队列 13 14 int H_dequeue(Deque *D); //从队列头出队列 15 16 void T_enqueue(Deque *D, int x); //从队列尾进队列 17 18 int T_dequeue(Deque *D); //从队列尾出队列 19 20 int main(){ 21 int i, num; 22 Deque *de; 23 de->head = de->tail = 0; 24 H_dequeue(de); 25 T_dequeue(de); 26 for(i = 1; i <= 7; i++){ 27 scanf("%d", &num); 28 H_enqueue(de, num); 29 } 30 for(i = 1; i <= 7; i++){ 31 scanf("%d", &num); 32 T_enqueue(de, num); 33 } 34 int H_de = H_dequeue(de); 35 int T_de = T_dequeue(de); 36 printf("%d\n", H_de); 37 printf("%d\n", T_de); 38 39 return 0; 40 } 41 42 void H_enqueue(Deque *D, int x){ 43 if(D->head == ((D->tail + 1) % MAX)){ 44 printf("overflow\n"); 45 return ; 46 } 47 48 else{ 49 if(D->head == 0) 50 D->head = MAX-1; 51 else 52 D->head--; 53 D->array[D->head] = x; 54 } 55 } 56 57 int H_dequeue(Deque *D){ 58 if(D->head == D->tail){ 59 printf("underflow\n"); 60 return ; 61 } 62 int x = D->array[D->head]; 63 if(D->head == MAX-1) 64 D->head = 0; 65 else 66 D->head++; 67 68 return x; 69 } 70 71 void T_enqueue(Deque *D, int x){ 72 if(D->head == ((D->tail + 1) % MAX)){ 73 printf("overflow\n"); 74 return ; 75 } 76 77 else{ 78 D->array[D->tail] = x; 79 D->tail = (D->tail + 1) % MAX; 80 } 81 } 82 83 int T_dequeue(Deque *D){ 84 if(D->head == D->tail){ 85 printf("underflow\n"); 86 return ; 87 } 88 89 int x = D->array[D->tail-1]; 90 if(D->tail == 0) 91 D->tail = MAX-1; 92 else 93 D->tail--; 94 95 return x; 96 }
练习10.1-6
说明如何用两个栈实现一个队列,并分析有关队列操作的运行时间
一个栈为A、一个栈为B。入队列的时候相当于进B栈,如果B栈已经为满栈的时候,将B栈的所有元素出栈,再进A栈,然后把要入队列的元素进B栈。如果A、B栈同为满栈的时候,为上溢。出队列时就从A栈弹出(如果A栈为空,先从B栈依次弹出并一次进A栈)。PS:代码可能有瑕疵。。。。
#include <stdio.h> #define MAX 4 typedef struct { int array[MAX]; int top; }Stack; typedef struct { Stack pr_stack; Stack ne_stack; }Queue; int stack_empty(Stack *S); int push(Stack *S, int x); int pop(Stack *S); void enqueue(Queue *Q, int x); int dequeue(Queue *Q); int main(){ int i, num; Queue *Q; Stack Sp; Sp.top = -1; Stack Sn; Sn.top = -1; Q->pr_stack = Sp; Q->ne_stack = Sn; dequeue(Q); for(i = 1; i <= 8; i++){ scanf("%d", &num); enqueue(Q, num); } enqueue(Q, num+1); int de; for(i = 1; i <= 8; i++){ de = dequeue(Q); printf("%d ", de); } printf("\n"); return 0; } int stack_empty(Stack *S){ if(S->top == -1) return 1; else return 0; } int push(Stack *S, int x){ if(S->top+1 == MAX){ return 0; } else{ S->top++; S->array[S->top] = x; return 1; } } int pop(Stack *S){ if(stack_empty(S)){ printf("underflow\n"); return ; } else{ return S->array[S->top--]; } } void enqueue(Queue *Q, int x){ if(push(&Q->ne_stack, x) == 0){ if(Q->pr_stack.top+1 == MAX){ printf("overflow\n"); return ; } else{ while(Q->pr_stack.top+1 != MAX && !(stack_empty(&Q->ne_stack))){ push(&Q->pr_stack, pop(&Q->ne_stack)); } push(&Q->ne_stack, x); } } } int dequeue(Queue *Q){ if(stack_empty(&Q->pr_stack)){ if(stack_empty(&Q->ne_stack)){ printf("underflow\n"); return ; } while(Q->pr_stack.top+1 != MAX && !(stack_empty(&Q->ne_stack))){ push(&Q->pr_stack, pop(&Q->ne_stack)); } } return pop(&Q->pr_stack); }
链表
链表中的对象按照线性顺序排序,不像数组通过下标来决定对象的线性序,链表的顺序是由对象的指针来决定的。
PS:下列为单链表实现代码(头插入),注意要释放删除结点的内存。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct ListNode{ 5 int value; 6 struct ListNode * next; 7 }ListNode; //链表结点结构体,对象的值,后指针 8 9 void list_insert(ListNode *l , int x); //链表的插入,头插入 10 11 void print_list(ListNode *l); //打印链表 12 13 int list_search(ListNode *l, int k); //链表的查找,k为要查找的值 14 15 void list_delete(ListNode *l, int k); //链表的删除,k为要删除的结点的值 16 17 int main(){ 18 ListNode *head; //头节点 19 int i, num; 20 head = (ListNode *)malloc(sizeof(ListNode)); 21 head->next = NULL; 22 23 for(i = 1; i <= 5; i++){ 24 scanf("%d", &num); 25 list_insert(head, num); 26 } 27 28 print_list(head); 29 30 printf("%d\n", list_search(head, 3)); 31 32 list_delete(head, 5); 33 print_list(head); 34 return 0; 35 } 36 37 void list_insert(ListNode *l, int x){ 38 ListNode * p; 39 p = (ListNode *)malloc(sizeof(ListNode)); 40 p->value = x; 41 if(l->next == NULL){ 42 l->next = p; 43 p->next = NULL; 44 } 45 else{ 46 p->next = l->next; 47 l->next = p; 48 } 49 50 } 51 52 void print_list(ListNode *l){ 53 ListNode * p = l->next; 54 while(p != NULL){ 55 printf("%d ", p->value); 56 p = p->next; 57 } 58 printf("\n"); 59 } 60 61 int list_search(ListNode *l, int key){ 62 ListNode *p = l->next; 63 while(p != NULL){ 64 if(p->value == key) 65 return 1; 66 p = p->next; 67 } 68 69 return 0; 70 } 71 72 void list_delete(ListNode *l, int key){ 73 ListNode *p = l->next; 74 75 if(p->value == key){ 76 l->next = p->next; 77 free(p); 78 } 79 else{ 80 while(p->next->value != key){ 81 p = p->next; 82 } 83 ListNode *de = p->next; 84 p->next = p->next->next; 85 free(de); //要释放删除节点的内存 86 } 87 }
PS:下列为双链表代码实现(头插入)。(注释参考单链表。。。)(添加日期:2013.10.27)
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct ListNode { 5 int value; 6 struct ListNode * next; 7 struct ListNode * prev; 8 }ListNode; 9 10 void list_insert(ListNode *l, int x); 11 12 void print_list(ListNode *l); 13 14 int list_search(ListNode *l, int k); 15 16 void list_delete(ListNode *l, int k); 17 18 int main(){ 19 int i, num; 20 ListNode *head; 21 head = (ListNode *)malloc(sizeof(ListNode)); 22 head->next = NULL; 23 24 for(i = 1; i <= 5; i++){ 25 scanf("%d", &num); 26 list_insert(head, num); 27 } 28 //print_list(head); 29 list_delete(head, 3); 30 print_list(head); 31 return 0; 32 } 33 34 void list_insert(ListNode *l, int x){ 35 ListNode * n; 36 n = (ListNode *)malloc(sizeof(ListNode)); 37 n->value = x; 38 39 if(l->next == NULL){ 40 l->next = n; 41 n->prev = l; 42 n->next = NULL; 43 } 44 45 else{ 46 n->next = l->next; 47 l->next->prev = n; 48 l->next = n; 49 n->prev = l; 50 } 51 } 52 53 void print_list(ListNode *l){ 54 ListNode *p = l->next; 55 56 while(p != NULL){ 57 printf("%d ", p->value); 58 p = p->next; 59 } 60 61 printf("\n"); 62 } 63 64 int list_search(ListNode *l, int k){ 65 ListNode *p = l->next; 66 67 while(p != NULL){ 68 if(p->value == k){ 69 return 1; 70 } 71 p = p->next; 72 } 73 74 return 0; 75 } 76 77 void list_delete(ListNode *l, int k){ 78 ListNode *p = l->next; 79 while(p != NULL){ 80 if(p->value == k){ 81 p->prev->next = p->next; 82 p->next->prev = p->prev; 83 84 free(p); 85 } 86 p = p->next; 87 } 88 }
练习10.2-2
用一单链表L实现一个栈,要求PUSH和POP操作的时间仍为O(1)
该单链表的插入为头插入,对应PUSH操作,POP操作为删除头结点指向的下一结点。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct ListNode{ 5 int value; 6 struct ListNode * next; 7 }ListNode; 8 9 typedef struct { 10 ListNode * top; 11 }Stack; 12 13 void list_insert(ListNode *l , int x); 14 15 void print_list(ListNode *l); 16 17 int stack_empty(Stack *S); 18 19 void push(Stack *S, int x); 20 21 int pop(Stack *S); 22 23 int main(){ 24 int i, num; 25 Stack * S; 26 S = (Stack *)malloc(sizeof(Stack)); 27 S->top = (ListNode *)malloc(sizeof(ListNode)); 28 29 for(i = 1; i <= 5; i++){ 30 scanf("%d", &num); 31 push(S, num); 32 } 33 34 //printf("%d\n", stack_empty(S)); 35 //print_list(S->top); 36 37 for(i = 1; i<= 5; i++) 38 printf("%d ", pop(S)); 39 printf("\n"); 40 41 return 0; 42 } 43 44 void list_insert(ListNode *l, int x){ 45 ListNode * p; 46 p = (ListNode *)malloc(sizeof(ListNode)); 47 p->value = x; 48 if(l->next == NULL){ 49 l->next = p; 50 p->next = NULL; 51 } 52 else{ 53 p->next = l->next; 54 } 55 l->next = p; 56 } 57 58 void print_list(ListNode *l){ 59 ListNode * p = l->next; 60 while(p != NULL){ 61 printf("%d ", p->value); 62 p = p->next; 63 } 64 printf("\n"); 65 } 66 67 68 int stack_empty(Stack *S){ 69 if(S->top->next == NULL) 70 return 1; 71 else 72 return 0; 73 } 74 75 void push(Stack *S, int x){ 76 list_insert(S->top, x); 77 } 78 79 int pop(Stack *S){ 80 if(stack_empty(S)){ 81 printf("underflow\n"); 82 return ; 83 } 84 else{ 85 ListNode *p = S->top->next; 86 int x = p->value; 87 S->top->next = p->next; 88 free(p); 89 return x; 90 } 91 }
PS:还有一些练习没完成。。完成会不定期更新。。。。
还要继续努力,朝着目标进发!!!