7-1 数组循环左移
本题要求实现一个对数组进行循环左移的简单函数:一个数组a中存有n(>)个整数,在不允许使用另外数组的前提下,将每个整数循环向左移m(≥)个位置,即将a中的数据由(a0a1⋯an−1)变换为(am⋯an−1a0a1⋯am−1)(最前面的m个数循环移至最后面的m个位置)。如果还需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
输入格式:
输入第1行给出正整数n(≤)和整数m(≥);第2行给出n个整数,其间以空格分隔。
输出格式:
在一行中输出循环左移m位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。
输入样例:
8 3
1 2 3 4 5 6 7 8
输出样例:
4 5 6 7 8 1 2 3
1 #include <stdio.h> 2 #define N 100 3 int main() 4 { 5 int i, n, k, arr[N] = {0}; 6 scanf("%d%d",&n,&k); 7 /* 输入数组 */ 8 for(int i=0; i<n; ++i){ 9 scanf("%d,",&arr[i]); 10 } 11 /* 输出 */ 12 for(i=0; i<n-1; ++i){ 13 printf("%d ",arr[(i+k)%n]); 14 } 15 printf("%d",arr[(i+k)%n]); 16 return 0; 17 }
7-2 装箱问题
假设有N项物品,大小分别为s1、s2、…、si、…、sN,其中si为满足1的整数。要把这些物品装入到容量为100的一批箱子(序号1-N)中。装箱方法是:对每项物品, 顺序扫描箱子,把该物品放入足以能够容下它的第一个箱子中。请写一个程序模拟这种装箱过程,并输出每个物品所在的箱子序号,以及放置全部物品所需的箱子数目。
输入格式:
输入第一行给出物品个数N(≤);第二行给出N个正整数si(1,表示第i项物品的大小)。
输出格式:
按照输入顺序输出每个物品的大小及其所在的箱子序号,每个物品占1行,最后一行输出所需的箱子数目。
输入样例:
8
60 70 80 90 30 40 10 20
输出样例:
60 1
70 2
80 3
90 4
30 1
40 5
10 1
20 2
5
1 #include <stdio.h> 2 #define N 1000 3 struct node{ 4 int c; 5 int cnt; 6 }; 7 int main() 8 { 9 struct node a[N]; 10 int n, capacity = 100, count = 1; 11 scanf("%d", &n); 12 for(int i=0; i<n; ++i){ 13 scanf("%d",&a[i].c); 14 a[i].cnt = 0; 15 } 16 for(int i= 0; i<n; ++i) 17 { 18 if(a[i].cnt) continue; 19 int sum = a[i].c; 20 a[i].cnt = count; 21 for(int j=i+1; j<n; ++j){ 22 if(!a[j].cnt && sum+a[j].c <= capacity){ 23 sum += a[j].c; 24 a[j].cnt = count; 25 } 26 } 27 count++; 28 } 29 for(int i=0; i<n; ++i){ 30 printf("%d %d ", a[i].c, a[i].cnt); 31 } 32 printf("%d ", count-1); 33 return 0; 34 }
7-3 两个有序序列的中位数
已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列,的中位数指A(N−1)/2的值,即第⌊个数(A0为第1个数)。
输入格式:
输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。
输出格式:
在一行中输出两个输入序列的并集序列的中位数。
输入样例1:
5
1 3 5 7 9
2 3 4 5 6
输出样例1:
4
输入样例2:
6
-100 -10 1 1 1 1
-50 0 2 3 4 5
输出样例2:
1
1 /* 算法1 2 3个静态数组, 2个序列全部并入第3个序列 */ 3 #include <stdio.h> 4 #define N 100000 5 6 int main() 7 { 8 int a1[N], a2[N], c[2*N], cnt1=0, cnt2=0, cnt=0; 9 int n; 10 scanf("%d",&n); 11 for(int i=0; i<n; ++i) 12 scanf("%d",&a1[i]); 13 for(int i=0; i<n; ++i) 14 scanf("%d",&a2[i]); 15 for(int i=0; cnt1<n && cnt2<n; ++i) 16 { 17 if(a1[cnt1]<=a2[cnt2]){ 18 c[cnt++] = a1[cnt1++]; 19 } 20 else{ 21 c[cnt++] = a2[cnt2++]; 22 } 23 } 24 while(cnt1<n) c[cnt++] = a1[cnt1++]; 25 while(cnt2<n) c[cnt++] = a2[cnt2++]; 26 printf("%d ", c[(2*n-1)/2]); 27 return 0; 28 }
1 /*算法2 2 三个动态数组, 2个序列并入第3个序列n个元素 3 */ 4 #include <stdio.h> 5 #include <malloc.h> 6 int main() 7 { 8 int* a1, *a2, *c; 9 int cnt1=0, cnt2=0, cnt=0; 10 int n; 11 scanf("%d",&n); 12 a1 = (int*)malloc(sizeof(int)*n); 13 a2 = (int*)malloc(sizeof(int)*n); 14 c = (int*)malloc(sizeof(int)*n); 15 16 for(int i=0; i<n; ++i) 17 scanf("%d",&a1[i]); 18 for(int i=0; i<n; ++i) 19 scanf("%d",&a2[i]); 20 21 for(int i=0; i<n; ++i) 22 { 23 if(a1[cnt1]<=a2[cnt2]){ 24 c[cnt++] = a1[cnt1++]; 25 } 26 else{ 27 c[cnt++] = a2[cnt2++]; 28 } 29 } 30 31 printf("%d ", c[n-1]); 32 return 0; 33 }
1 /*算法3 2 二个动态数组, 2个序列并入第3个序列n个元素 3 */ 4 #include <stdio.h> 5 #include <malloc.h> 6 int main() 7 { 8 int* a, *c; 9 int cnt1=0, cnt2=0; 10 int n; 11 scanf("%d",&n); 12 a = (int*)malloc(sizeof(int)*n*2); 13 c = (int*)malloc(sizeof(int)*n); 14 15 for(int i=0; i<n*2; ++i) 16 scanf("%d",&a[i]); 17 18 for(int i=0; i<n; ++i) 19 { 20 if(a[cnt1]<=a[n+cnt2]){ 21 c[i] = a[cnt1++]; 22 } 23 else{ 24 c[i] = a[n+cnt2++]; 25 } 26 } 27 28 printf("%d ", c[n-1]); 29 return 0; 30 }
1 /* 算法4 2 2个动态数组, 不并, 通过下标计算中位数 3 */ 4 #include <stdio.h> 5 #include <malloc.h> 6 int main() 7 { 8 int* a1, *a2; 9 int cnt1=0, cnt2=0; 10 int n; 11 scanf("%d",&n); 12 a1 = (int*)malloc(sizeof(int)*n); 13 a2 = (int*)malloc(sizeof(int)*n); 14 15 for(int i=0; i<n; ++i) 16 scanf("%d",&a1[i]); 17 for(int i=0; i<n; ++i) 18 scanf("%d",&a2[i]); 19 20 while(cnt1+cnt2<(2*n-1)/2) 21 { 22 if(a1[cnt1]<=a2[cnt2]){ 23 cnt1++; 24 } 25 else{ 26 cnt2++; 27 } 28 } 29 printf("%d ", a1[cnt1]>a2[cnt2]?a2[cnt2]:a1[cnt1]); 30 return 0; 31 }
给定一系列正整数,请设计一个尽可能高效的算法,查找倒数第K个位置上的数字。
输入格式:
输入首先给出一个正整数K,随后是若干正整数,最后以一个负整数表示结尾(该负数不算在序列内,不要处理)。
输出格式:
输出倒数第K个位置上的数据。如果这个位置不存在,输出错误信息NULL
。
输入样例:
4 1 2 3 4 5 6 7 8 9 0 -1
输出样例:
7
1 #include <stdio.h> 2 #include <malloc.h> 3 4 struct Node { 5 int number; 6 struct Node* next; 7 }; 8 9 int main() 10 { 11 int k; 12 scanf("%d",&k); 13 14 struct Node *p = NULL, *t = NULL; //p新结点, t当前结点 15 int x; 16 scanf("%d", &x); 17 while(x >= 0) 18 { 19 p = (struct Node*)malloc(sizeof(struct Node)); 20 p->number = x; 21 p->next = NULL; 22 23 p->next = t, t = p;//头部插入更新当前t 24 scanf("%d",&x); 25 } 26 27 while(--k && p) 28 { 29 p = p->next; 30 } 31 32 if(p == NULL) 33 printf("NULL "); 34 else 35 printf("%d ", p->number); 36 37 return 0; 38 }
已知两个非降序链表序列S1与S2,设计函数构造出S1与S2的交集新链表S3。
输入格式:
输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−表示序列的结尾(−不属于这个序列)。数字用空格间隔。
输出格式:
在一行中输出两个输入序列的交集序列,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL
。
输入样例:
1 2 5 -1
2 4 5 8 10 -1
输出样例:
2 5
1 #include <stdio.h> 2 #include <malloc.h> 3 4 struct Node { 5 int number; 6 struct Node* next; 7 }; 8 9 struct Node* CreatLinkList() 10 { 11 struct Node *h = NULL, *r = NULL, *p = NULL; 12 int x, flag = 1; 13 while(scanf("%d", &x) && x >=0) 14 { 15 p = (struct Node*)malloc(sizeof(struct Node)); 16 p->number = x; 17 p->next = NULL; 18 if(flag) h = r = p; 19 else r->next = p, r = p;//尾插 20 flag = 0; 21 } 22 return h; 23 } 24 25 int main() 26 { 27 int flag = 1; 28 struct Node *s1, *s2; 29 s1 = CreatLinkList(), s2 = CreatLinkList(); 30 31 while(s1 && s2) 32 { 33 if(s1->number > s2->number) 34 s2 = s2->next; 35 else if(s1->number < s2->number) 36 s1 = s1->next; 37 else{ 38 if(!flag) 39 printf(" "); 40 printf("%d", s1->number); 41 s1 = s1->next; 42 s2 = s2->next; 43 flag = 0; 44 } 45 } 46 47 if(flag) printf("NULL "); 48 49 return 0; 50 }
7-6 符号配对
请编写程序检查C语言源程序中下列符号是否配对:/*与*/、(与)、[与]、{与}。
输入格式:
输入为一个C语言源程序。当读到某一行中只有一个句点.和一个回车的时候,标志着输入结束。程序中需要检查配对的符号不超过100个。
输出格式:
首先,如果所有符号配对正确,则在第一行中输出YES,否则输出NO。然后在第二行中指出第一个不配对的符号:如果缺少左符号,则输出?-右符号;如果缺少右符号,则输出左符号-?。
输入样例1:
void test() { int i, A[10]; for (i=0; i<10; i++) /*/ A[i] = i; } .
输出样例1:
NO
/*-?
输入样例2:
void test() { int i, A[10]; for (i=0; i<10; i++) /**/ A[i] = i; }] .
输出样例2:
NO
?-]
输入样例3:
void test() { int i double A[10]; for (i=0; i<10; i++) /**/ A[i] = 0.1*i; } .
输出样例3:
YES
1 #include <stdio.h> 2 #define N 100 3 4 void printLeft(char ch) {//左字符打印 5 if(ch == '<') printf("NO /*-? "); 6 else printf("NO %c-? ", ch); 7 } 8 9 void printRight(char ch) {//右字符打印 10 if(ch == '<') printf("NO ?-*/ "); 11 else printf("NO ?-%c ",ch); 12 } 13 14 int charMatch(char ch1,char ch2)//字符匹配 15 { 16 return ch1=='(' && ch2==')' 17 || ch1=='[' && ch2==']' 18 ||ch1=='{' && ch2=='}' 19 ||ch1 == ch2; 20 } 21 22 int MatchOrPrint(char ch1, int* top, int flag, char ch2) 23 { 24 if(*top != -1 && charMatch(ch1,ch2)){ 25 (*top)--; //栈有字符且匹配出栈 26 } 27 else //栈无字符或不匹配 28 { 29 flag = 0; //栈无字符或不匹配 30 if(*top == -1) {//1.栈无字符(缺左打右) 31 printRight(ch2); 32 } 33 else { //2.栈有字符(缺右打左) 34 printLeft(ch1); 35 } 36 } 37 return flag; 38 } 39 40 int main() 41 { 42 char str[10*N];//字符串 43 char stack[N]; //栈 44 int i, top = -1, flag = 1;//flag匹配标志 45 46 while(gets(str) && str[0] != '.')//按行读入 47 { 48 for( i = 0; str[i]; i++) //逐行处理 49 { 50 if(str[i] == '(' || str[i] == '[' || str[i] == '{') 51 { 52 stack[++top] = str[i]; //左符号入栈 53 } 54 else if(str[i] == '/' && str[i + 1] == '*') 55 { 56 stack[++top] = '<'; //左符号入栈 57 i++; 58 } 59 else if(str[i] == '*' && str[i + 1] == '/') 60 { 61 flag = MatchOrPrint(stack[top], &top, flag, '<'); 62 i++; // 处理右*/避免'/'再次处理, for循环再次++ 63 } 64 else if(str[i] == ')'||str[i] == ']'||str[i] == '}') 65 { //处理右 ')', ']', '}' 66 flag = MatchOrPrint(stack[top], &top, flag, str[i]); 67 } 68 if(flag == 0) break;//栈无字符或不匹配 69 } 70 if(flag == 0) break; //栈无字符或不匹配 71 } 72 73 if(flag && top == -1) printf("YES ");//栈空且左右匹配 74 else if(flag) { //即使左右匹配栈不空(缺右打左) 75 printLeft(stack[top]); 76 } 77 return 0; 78 }
7-7 整型关键字的散列映射
给定一系列整型关键字和素数P,用除留余数法定义的散列函数将关键字映射到长度为P的散列表中。用线性探测法解决冲突。
输入格式:
输入第一行首先给出两个正整数N(≤)和P(≥的最小素数),分别为待插入的关键字总数、以及散列表的长度。第二行给出N个整型关键字。数字间以空格分隔。
输出格式:
在一行内输出每个整型关键字在散列表中的位置。数字间以空格分隔,但行末尾不得有多余空格。
输入样例:
4 5
24 15 61 88
输出样例:
4 0 1 3
1 #include <stdio.h> 2 #include <malloc.h> 3 int main() 4 { 5 int n, p; 6 scanf( "%d%d", &n, &p ); 7 int* table = (int*)calloc(p, sizeof(int)); //散列表 8 int x; 9 for( int i = 0; i < n; i++ ) 10 { 11 scanf( "%d", &x ); 12 int index = x % p; 13 while( table[index] ){ 14 if(table[index] == x)//重复数字 15 break; 16 index = (index+1)%p; 17 } 18 table[index] = x;//在散列表的index位置记录x 19 if( i == 0 ) printf( "%d", index ); 20 else printf( " %d", index ); 21 } 22 return 0; 23 }
7-8 树的遍历
给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。
输入格式:
输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。
输出格式:
在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
4 1 6 3 5 7 2
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdbool.h> 4 #include <malloc.h> 5 #define ERROR -1 6 #define N 31 7 8 /* 二叉树结点 */ 9 typedef struct TreeNode *BinTree; 10 struct TreeNode{ 11 int Data; 12 BinTree Left; 13 BinTree Right; 14 }; 15 16 /* 创建树结点 */ 17 BinTree NewNode( int V ) 18 { 19 BinTree T = (BinTree)malloc(sizeof(struct TreeNode)); 20 T->Data = V; 21 T->Left = T->Right = NULL; 22 return T; 23 } 24 25 /* 建树 */ 26 BinTree BuildTree(int a[],int b[],int i,int j,int s,int e) 27 { /* i,j树的后序序列的起止, s,e树的中序序列的起止 */ 28 int k; 29 BinTree p; 30 if( i > j ) return NULL;//递归终止 31 32 p = NewNode(a[j]); //后序的根a[j]创建结点 33 34 k = s; /* 寻找树根位置 */ 35 while( ( k <= e ) && ( b[k] != a[j] ) ) k++; 36 37 if( k > e ) exit(ERROR); 38 39 p->Left = BuildTree(a, b, i, i+(k-s)-1, s, k-1); /* 左子树 */ 40 p->Right = BuildTree(a, b, i+(k-s), j-1, k+1, e); /* 右子树 */ 41 return p; 42 } 43 44 /* 顺序循环队列*/ 45 typedef struct QNode *Queue; /* 队列指针 */ 46 struct QNode { 47 BinTree* Data; /* 存储元素的数组 */ 48 int Front, Rear; /* 队列的头、尾指针 */ 49 int MaxSize; /* 队列最大容量 */ 50 }; 51 52 /* 建队列 */ 53 Queue CreateQueue( int MaxSize ) 54 { 55 Queue Q = (Queue)malloc(sizeof(struct QNode)); 56 Q->Data = (BinTree*)malloc(MaxSize * sizeof(BinTree)); 57 Q->Front = Q->Rear = 0; 58 Q->MaxSize = MaxSize; 59 return Q; 60 } 61 62 /* 队满 */ 63 bool IsFull( Queue Q ) 64 { 65 return ((Q->Rear+1)%Q->MaxSize == Q->Front); 66 } 67 68 /* 队空 */ 69 bool IsEmpty( Queue Q ) 70 { 71 return (Q->Front == Q->Rear); 72 } 73 74 /* 入队 */ 75 bool AddQ( Queue Q, BinTree X ) 76 { 77 if ( IsFull(Q) ) { 78 return false; 79 } 80 else { 81 Q->Rear = (Q->Rear+1)%Q->MaxSize; 82 Q->Data[Q->Rear] = X; 83 return true; 84 } 85 } 86 87 /* 出队 */ 88 BinTree DeleteQ( Queue Q ) 89 { 90 if ( IsEmpty(Q) ) 91 return NULL; 92 Q->Front =(Q->Front+1)%Q->MaxSize; 93 return Q->Data[Q->Front]; 94 } 95 96 /* 层序遍历 */ 97 void LevelorderTraversal ( BinTree BT ) 98 { 99 Queue Q; 100 BinTree T; 101 102 if ( !BT ) return; /* 若是空树则直接返回 */ 103 104 Q = CreateQueue(N); /* 创建空队列Q */ 105 AddQ( Q, BT ); /* 树根入队 */ 106 107 int flag = 0; /* 控制输出空格 */ 108 109 while ( !IsEmpty(Q) ) 110 { 111 T = DeleteQ( Q ); 112 if(flag) printf(" "); 113 flag = 1; 114 printf("%d", T->Data); /* 访问取出队列的结点 */ 115 if ( T->Left ) AddQ( Q, T->Left ); /* 左子树入队 */ 116 if ( T->Right ) AddQ( Q, T->Right ); /* 右子树入队 */ 117 } 118 } 119 int main() 120 { 121 int a[N] = {0}, b[N] = {0}; 122 int n; 123 scanf("%d", &n); 124 /* 输入树的后序遍历中序遍历序列 */ 125 for(int i=0; i<n; ++i) 126 { 127 scanf("%d", &a[i]); 128 } 129 for(int i=0; i<n; ++i) 130 { 131 scanf("%d", &b[i]); 132 } 133 /* 恢复树 */ 134 BinTree root = BuildTree(a, b , 0, n-1 , 0, n-1); 135 /* 层序遍历输出 */ 136 LevelorderTraversal(root); 137 138 return 0; 139 }
7-9 根据后序和中序遍历输出先序遍历
本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果。
输入格式:
第一行给出正整数N(≤30),是树中结点的个数。随后两行,每行给出N个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。
输出格式:
在一行中输出Preorder:以及该树的先序遍历结果。数字间有1个空格,行末不得有多余空格。
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
Preorder: 4 1 3 2 6 5 7
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdbool.h> 4 #include <malloc.h> 5 #define ERROR -1 6 #define N 31 7 8 /* 二叉树结点 */ 9 typedef struct TreeNode *BinTree; 10 struct TreeNode{ 11 int Data; 12 BinTree Left; 13 BinTree Right; 14 }; 15 16 typedef BinTree Stack; /* 栈类型 */ 17 18 /* 创建树结点 */ 19 BinTree NewNode( int V ) 20 { 21 BinTree T = (BinTree)malloc(sizeof(struct TreeNode)); 22 T->Data = V; 23 T->Left = T->Right = NULL; 24 return T; 25 } 26 27 /* 建树 */ 28 BinTree BuildTree(int a[],int b[],int i,int j,int s,int e) 29 { /* i,j树的后序序列的起止, s,e树的中序序列的起止 */ 30 int k; 31 BinTree p; 32 if( i > j ) return NULL;//递归终止 33 34 p = NewNode(a[j]); //后序的根a[j]创建结点 35 36 k = s; /* 寻找树根位置 */ 37 while( ( k <= e ) && ( b[k] != a[j] ) ) k++; 38 39 if( k > e ) exit(ERROR); 40 41 p->Left = BuildTree(a, b, i, i+(k-s)-1, s, k-1); /* 左子树 */ 42 p->Right = BuildTree(a, b, i+(k-s), j-1, k+1, e); /* 右子树 */ 43 return p; 44 } 45 46 /* 非递归先序遍历 */ 47 void PreorderTraversal( BinTree BT ) 48 { 49 BinTree T = BT; 50 Stack S[N]; /* 建栈 */ 51 int top = -1; 52 while( T || top != -1 )/* 树不空或栈不空 */ 53 { 54 while(T)/*一直向左并将沿途结点压入堆栈*/ 55 { 56 S[++top] = T; /* 结点压栈(第一次遇到结点) */ 57 printf(" %d", T->Data); /*(访问)打印结点*/ 58 T = T->Left;/* 一直向左 */ 59 } 60 if(top != -1) /* 栈不空 */ 61 { 62 T = S[top--]; /*结点弹出堆栈(第二次遇到结点)*/ 63 T = T->Right;/*转向右子树*/ 64 } 65 } 66 } 67 68 int main() 69 { 70 int a[N] = {0}, b[N] = {0}; 71 int n; 72 scanf("%d", &n); 73 /* 输入树的后序遍历中序遍历序列 */ 74 for(int i=0; i<n; ++i) 75 { 76 scanf("%d", &a[i]); 77 } 78 for(int i=0; i<n; ++i) 79 { 80 scanf("%d", &b[i]); 81 } 82 /* 恢复树 */ 83 BinTree root = BuildTree(a, b , 0, n-1 , 0, n-1); 84 /* 先序遍历输出 */ 85 printf("Preorder:"); 86 PreorderTraversal(root); 87 88 return 0; 89 }
7-10 是否完全二叉搜索树
将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果。
输入格式:
输入第一行给出一个不超过20的正整数N;第二行给出N个互不相同的正整数,其间以空格分隔。
输出格式:
将输入的N个正整数顺序插入一个初始为空的二叉搜索树。在第一行中输出结果树的层序遍历结果,数字间以1个空格分隔,行的首尾不得有多余空格。第二行输出YES,如果该树是完全二叉树;否则输出NO。
输入样例1:
9
38 45 42 24 58 30 67 12 51
输出样例1:
38 45 24 58 42 30 12 67 51
YES
输入样例2:
8
38 24 12 45 58 67 42 51
输出样例2:
38 45 24 58 42 12 67 51
NO
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdbool.h> 4 5 /* 树结点 */ 6 typedef struct TreeNode *BinTree; 7 typedef struct TreeNode TreeNode; 8 struct TreeNode{ 9 int Data, ID; 10 BinTree Left; 11 BinTree Right; 12 }; 13 14 /* 顺序循环队列*/ 15 #define MAXSIZE 21 //最大队列长度 N 16 typedef struct QNode *Queue; 17 struct QNode { 18 BinTree* Data; /* 存储元素的数组 */ 19 int Front, Rear; /* 队列的头、尾指针 */ 20 int MaxSize; /* 队列最大容量 */ 21 }; 22 23 BinTree Insert( BinTree BST, int X ); /* 插入 */ 24 void LevelorderTraversal ( BinTree BT ); /* 层次遍历 */ 25 Queue CreateQueue( ); /* 建队 */ 26 bool IsFull( Queue Q ); /* 队满 */ 27 bool IsEmpty( Queue Q ); /* 队空 */ 28 bool AddQ( Queue Q, BinTree X ); /* 入队 */ 29 BinTree DeleteQ( Queue Q ); /* 出队 */ 30 31 int main() 32 { 33 int N, i, X; 34 scanf("%d", &N); 35 36 BinTree BST = NULL; 37 for ( i=0; i<N; i++ ) { 38 scanf("%d", &X); 39 BST = Insert(BST, X); 40 } 41 LevelorderTraversal(BST); 42 return 0; 43 } 44 45 BinTree Insert( BinTree BST, int X ) 46 { 47 if(!BST) 48 { 49 BST = (BinTree)malloc(sizeof(TreeNode)); 50 BST->Data = X; 51 BST->Left = NULL; 52 BST->Right = NULL; 53 } 54 else 55 { 56 if(X > BST->Data) 57 BST->Left = Insert(BST->Left,X); 58 else if(BST->Data > X) 59 BST->Right = Insert(BST->Right,X); 60 } 61 return BST; 62 } 63 64 void LevelorderTraversal ( BinTree BT ) 65 { 66 Queue Q; 67 BinTree T; 68 69 bool flag = true, ans = true; 70 int order = 0; /* 顺序 */ 71 72 if ( !BT ) return; /* 若是空树则直接返回 */ 73 74 Q = CreateQueue(); /* 创建空队列Q */ 75 76 77 AddQ( Q, BT ); /* 树根入队 */ 78 BT->ID = 1; 79 80 while ( !IsEmpty(Q) ) 81 { 82 T = DeleteQ( Q ); /* 出队 */ 83 order++; 84 85 if(T->ID != order) 86 ans = false; /* 顺序不同 */ 87 if(flag) /* 输出格式 */ 88 { 89 printf("%d", T->Data); 90 flag = false; 91 } 92 else printf(" %d", T->Data); 93 94 if ( T->Left ){ 95 T->Left->ID = 2 * T->ID; /* 左儿子顺序 */ 96 AddQ( Q, T->Left ); /* 左儿子入队 */ 97 } 98 if ( T->Right ){ 99 T->Right->ID = 2 * T->ID + 1;/* 右儿子顺序 */ 100 AddQ( Q, T->Right ); /* 右儿子入队 */ 101 } 102 } 103 104 if(ans) /* 顺序相同 */ 105 printf(" YES"); 106 else 107 printf(" NO"); 108 } 109 110 /* 建队列 */ 111 Queue CreateQueue( )//返回队列指针 112 { 113 Queue Q = (Queue)malloc(sizeof(struct QNode)); 114 Q->Data = (BinTree*)malloc(MAXSIZE * sizeof(BinTree)); 115 Q->Front = Q->Rear = 0; 116 Q->MaxSize = MAXSIZE; 117 return Q; 118 } 119 120 /* 队满 */ 121 bool IsFull( Queue Q ) 122 { 123 return ((Q->Rear+1)%Q->MaxSize == Q->Front); 124 } 125 126 /* 队空 */ 127 bool IsEmpty( Queue Q ) 128 { 129 return (Q->Front == Q->Rear); 130 } 131 132 /* 入队 */ 133 bool AddQ( Queue Q, BinTree X ) 134 { 135 if ( IsFull(Q) ) { 136 return false; 137 } 138 else { 139 Q->Rear = (Q->Rear+1)%Q->MaxSize; 140 Q->Data[Q->Rear] = X; 141 return true; 142 } 143 } 144 145 /* 出队 */ 146 BinTree DeleteQ( Queue Q ) 147 { 148 if ( IsEmpty(Q) ) 149 return NULL; 150 Q->Front =(Q->Front+1)%Q->MaxSize; 151 return Q->Data[Q->Front]; 152 }
7-11 修理牧场
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li的总和。但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。
请编写程序帮助农夫计算将木头锯成N块的最少花费。
输入格式:
输入首先给出正整数N(≤104),表示要将木头锯成N块。第二行给出N个正整数(≤50),表示每段木块的长度。
输出格式:
输出一个整数,即将木头锯成N块的最少花费。
输入样例:
8
4 5 1 2 1 3 1 1
输出样例:
49
1 /* 2 建一个最小堆, 堆插入树结点, 形成树结点森林; 3 每次从堆中取2个最小树结点, 建中间树结点并插入堆; 4 (最后堆中森林形成一棵Huffman树) 5 打印Huffman树的WPL; 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 11 /* 二叉树结点 */ 12 typedef struct TreeNode* Tree; 13 struct TreeNode { 14 int Weight; 15 Tree Left, Right; 16 }; 17 18 /* 堆(优先队列) */ 19 #define MAXSIZE 10001 20 typedef struct HeapNode* Heap; 21 struct HeapNode { 22 struct TreeNode Data[MAXSIZE];/* 完全二叉树顺序存储 */ 23 int Size; 24 }; 25 26 /* 建树结点 */ 27 Tree NewTreeNode() { 28 Tree T; 29 T = (Tree)malloc(sizeof(struct TreeNode)); 30 T->Weight = 0; 31 T->Left = T->Right = NULL; 32 return T; 33 } 34 35 /* 建空堆 */ 36 Heap CreatHeap() { 37 Heap H; 38 H = (Heap)malloc(sizeof(struct HeapNode)); 39 H->Size = 0; /* 空堆 */ 40 H->Data[0].Weight = -1; /* 哨兵 */ 41 return H; 42 } 43 44 /* 堆插入 */ 45 void Insert(Heap H, struct TreeNode T) { 46 int i = ++H->Size; /* ++堆, 从堆的最后开始寻找插入结点位置 */ 47 for( ; T.Weight < H->Data[i/2].Weight; i /= 2) 48 H->Data[i] = H->Data[i/2]; /* 父结点下滤 */ 49 H->Data[i] = T; /* 插入结点 */ 50 } 51 52 /* 堆删除 */ 53 Tree Delete(Heap H) 54 { /* 提取堆最后一个树结点, 从堆的第一个结点开始比较,上滤, 堆-- */ 55 int parent, child; 56 struct TreeNode Temp = H->Data[H->Size--]; /* 提取堆最后一个树结点 */ 57 Tree T = NewTreeNode(); /* 创建树结点 */ 58 *T = H->Data[1]; /* 从堆中提取第一个树结点 */ 59 for(parent = 1; 2*parent <= H->Size; parent = child) 60 { 61 child = 2 * parent; /* 从父结点找左孩子 */ 62 if(child != H->Size && H->Data[child].Weight > H->Data[child+1].Weight) 63 child++; /* 有右孩子且比左孩子小, 选右孩子 */ 64 if(Temp.Weight < H->Data[child].Weight) 65 break; /* 原堆最后一个树结点找到位置 */ 66 H->Data[parent] = H->Data[child]; /* 孩子结点上滤, 孩子结点覆盖父结点 */ 67 } 68 H->Data[parent] = Temp; /* 原堆最后一个树结点归位 */ 69 return T; /* 返回原堆第一个树结点 */ 70 } 71 72 /* 建Huffman树 */ 73 Tree Huffman(Heap H) 74 { 75 Tree T = NewTreeNode(); /* 临时树结点 */ 76 while(H->Size != 1) 77 { /* 每次取堆中2个最小值建中间树结点 */ 78 T->Left = Delete(H); 79 T->Right = Delete(H); 80 T->Weight = T->Left->Weight + T->Right->Weight; 81 82 Insert(H, *T); /* 复制创建的中间树结点(传参)插入堆 */ 83 } 84 free(T); /* 释放临时结点 */ 85 T = Delete(H); /* 取出堆中的树 */ 86 return T; 87 } 88 89 int WPL(Tree T, int Depth) { /* 整棵树的WPL */ 90 if(!T->Left && !T->Right) { return Depth*T->Weight; } 91 else return WPL(T->Left, Depth+1) + WPL(T->Right, Depth+1); 92 } 93 94 int main() 95 { 96 int N, Weight; 97 98 Heap H = CreatHeap(); /* 建空堆 */ 99 Tree T = NewTreeNode(); /* 建临时树结点 */ 100 101 scanf("%d", &N); /* 树结点个数 */ 102 for(int i = 0; i < N; i++) 103 { 104 scanf("%d", &Weight); 105 T->Weight = Weight; 106 H->Data[H->Size].Left = H->Data[H->Size].Right = NULL; 107 Insert(H, *T); /* 复制创建的树结点(传参), 插入堆, 形成树森林 */ 108 } 109 110 free(T); /* 释放临时结点 */ 111 112 T = Huffman(H); /* 建Huffman树 */ 113 114 printf("%d ", WPL(T, 0)); /* 输出Huffman树的WPL */ 115 116 return 0; 117 }
7-12 地下迷宫探索
地道战是在抗日战争时期,在华北平原上抗日军民利用地道打击日本侵略者的作战方式。地道网是房连房、街连街、村连村的地下工事,如下图所示。
我们在回顾前辈们艰苦卓绝的战争生活的同时,真心钦佩他们的聪明才智。在现在和平发展的年代,对多数人来说,探索地下通道或许只是一种娱乐或者益智的游戏。本实验案例以探索地下通道迷宫作为内容。
假设有一个地下通道迷宫,它的通道都是直的,而通道所有交叉点(包括通道的端点)上都有一盏灯和一个开关。请问你如何从某个起点开始在迷宫中点亮所有的灯并回到起点?
输入格式:
输入第一行给出三个正整数,分别表示地下迷宫的节点数N(1<N≤1000,表示通道所有交叉点和端点)、边数M(≤3000,表示通道数)和探索起始节点编号S(节点从1到N编号)。随后的M行对应M条边(通道),每行给出一对正整数,分别是该条边直接连通的两个节点的编号。
输出格式:
若可以点亮所有节点的灯,则输出从S开始并以S结束的包含所有节点的序列,序列中相邻的节点一定有边(通道);否则虽然不能点亮所有节点的灯,但还是输出点亮部分灯的节点序列,最后输出0,此时表示迷宫不是连通图。
由于深度优先遍历的节点序列是不唯一的,为了使得输出具有唯一的结果,我们约定以节点小编号优先的次序访问(点灯)。在点亮所有可以点亮的灯后,以原路返回的方式回到起点。
输入样例1:
6 8 1 1 2 2 3 3 4 4 5 5 6 6 4 3 6 1 5
输出样例1:
1 2 3 4 5 6 5 4 3 2 1
输入样例2:
6 6 6 1 2 1 3 2 3 5 4 6 5 6 4
输出样例2:
6 4 5 4 6 0
1 #include <stdio.h> 2 3 #define MAXN 1010 /* 最大结点数 */ 4 int AdjMatrix[MAXN][MAXN] = {0}; /* 邻接矩阵 */ 5 int visited[MAXN] = {0}; /* 访问标记 */ 6 int flag = 0; 7 int count = 1; /* 入口1 */ 8 int n; /* 结点数 */ 9 10 void DFS(int s) 11 { 12 if(flag) 13 printf(" "); 14 flag++; 15 printf("%d", s); /* 访问 */ 16 visited[s] = 1; 17 for(int i = 1;i<=n;i++) /* 结点序号从1开始到n */ 18 if(!visited[i]&&AdjMatrix[s][i]) /* 结点没访问且有边 */ 19 { 20 count++; /* 访问结点计数 */ 21 DFS(i); /* 递归调用 */ 22 printf(" %d",s); /* 返回 */ 23 } 24 } 25 26 int main() 27 { 28 int m,s; 29 scanf("%d%d%d", &n, &m, &s); /* 结点数 边数 入口 */ 30 for(int i = 0;i<m;i++) 31 { 32 int a,b; /* 边的两个结点编号 */ 33 scanf("%d%d", &a, &b); 34 AdjMatrix[a][b] = AdjMatrix[b][a] = 1; 35 } 36 DFS(s); /* 深度搜索 */ 37 if(count<n) /* 迷宫不连通 */ 38 printf(" 0"); 39 return 0; 40 }
7-13 畅通工程之局部最小花费问题
某地区经过对城镇交通状况的调查,得到现有城镇间快速道路的统计数据,并提出“畅通工程”的目标:使整个地区任何两个城镇间都可以实现快速交通(但不一定有直接的快速道路相连,只要互相间接通过快速路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建快速路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全地区畅通需要的最低成本。
输入格式:
输入的第一行给出村庄数目N (1≤N≤100);随后的N(N−1)/2行对应村庄间道路的成本及修建状态:每行给出4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态 — 1表示已建,0表示未建。
输出格式:
输出全省畅通需要的最低成本。
输入样例:
4 1 2 1 1 1 3 4 0 1 4 1 1 2 3 3 0 2 4 2 1 3 4 5 0
输出样例:
3
1 #include <stdio.h> 2 3 #define inf 65535 4 #define MAXN 200 5 int map[MAXN][MAXN],w[MAXN],visit[MAXN]; 6 int N; 7 8 void init() 9 { 10 for(int i = 1; i<=N; i++) { 11 for(int j = 1; j<=N; j++) { 12 map[i][j] = inf; 13 } 14 } 15 int start,end,cost,state,k = N*(N-1)/2; 16 for(int i = 0; i<k; i++) 17 { /* 两个村庄start,end,村庄之间路的花费cost,是否已修建state */ 18 scanf("%d%d%d%d",&start,&end,&cost,&state); 19 if(state == 0) { 20 map[start][end] = cost; 21 map[end][start] = cost; 22 } else { 23 map[start][end] = 0; 24 map[end][start] = 0; 25 } 26 } 27 } 28 29 void prim() 30 { /* 从1顶点出开始生成最小生成树 */ 31 for(int i = 1; i<=N; i++){ 32 visit[i] = 0; 33 if(i != 1) 34 w[i] = map[1][i]; 35 } 36 visit[1] = 1; 37 int pos = 0,MIN = inf; 38 for(int i = 0; i<N; i++) 39 { 40 MIN = inf,pos = 0; 41 for(int j = 1; j<=N; j++) 42 {/* 从没有访问过的顶点中找到下一个距离最短的边 */ 43 if(!visit[j] && MIN>w[j]) { 44 MIN = w[j]; 45 pos = j; 46 } 47 } 48 visit[pos] = 1; 49 for(int j = 1; j<=N; j++) 50 { /* 用这个最小的顶点的邻接边的权值更新w[]的值 */ 51 if(!visit[j] && map[pos][j]<w[j]) { 52 w[j] = map[pos][j]; 53 } 54 } 55 } 56 } 57 58 int main() 59 { 60 scanf("%d",&N); 61 init(); 62 prim(); 63 64 int sum = 0; 65 /* 最低成本 */ 66 for(int i = 1; i<=N; i++) { 67 sum += w[i]; 68 } 69 printf("%d ",sum); 70 return 0; 71 }
7-14 奥运排行榜
每年奥运会各大媒体都会公布一个排行榜,但是细心的读者发现,不同国家的排行榜略有不同。比如中国金牌总数列第一的时候,中国媒体就公布“金牌榜”;而美国的奖牌总数第一,于是美国媒体就公布“奖牌榜”。如果人口少的国家公布一个“国民人均奖牌榜”,说不定非洲的国家会成为榜魁…… 现在就请你写一个程序,对每个前来咨询的国家按照对其最有利的方式计算它的排名。
输入格式:
输入的第一行给出两个正整数N和M(≤224,因为世界上共有224个国家和地区),分别是参与排名的国家和地区的总个数、以及前来咨询的国家的个数。为简单起见,我们把国家从0 ~ N−1编号。之后有N行输入,第i行给出编号为i−1的国家的金牌数、奖牌数、国民人口数(单位为百万),数字均为[0,1000]区间内的整数,用空格分隔。最后面一行给出M个前来咨询的国家的编号,用空格分隔。
输出格式:
在一行里顺序输出前来咨询的国家的排名:计算方式编号。其排名按照对该国家最有利的方式计算;计算方式编号为:金牌榜=1,奖牌榜=2,国民人均金牌榜=3,国民人均奖牌榜=4。输出间以空格分隔,输出结尾不能有多余空格。
若某国在不同排名方式下有相同名次,则输出编号最小的计算方式。
输入样例:
4 4 51 100 1000 36 110 300 6 14 32 5 18 40 0 1 2 3
输出样例:
1:1 1:2 1:3 1:4
#include <stdio.h> #define MAXN 225 /* 224个国家 */ struct country { int gold; /* 金牌 */ int medal; /* 奖牌 */ int pop; /* 人口数population */ } cts[MAXN]; /* 城市数 */ int main() { int m,n; scanf("%d %d",&n,&m); /* 参与总数n,咨询总数m */ for(int i = 0; i < n; i++) /* 参与 */ { scanf("%d %d %d",&cts[i].gold,&cts[i].medal,&cts[i].pop); } for(int i = 0; i < m; i++) /* m个国家咨询 */ { int num; scanf("%d",&num); /* 咨询国家编号num */ int rank[4] = {0}; /* 4种计算名次方法 */ for(int i = 0; i < n; i++) /* 计算名次 */ { if(cts[i].gold > cts[num].gold) rank[0]++; if(cts[i].medal > cts[num].medal) rank[1]++; if(cts[i].gold*cts[num].pop > cts[num].gold*cts[i].pop) rank[2]++; if(cts[i].medal*cts[num].pop > cts[num].medal*cts[i].pop) rank[3]++; } int sum = rank[0]; int index = 0; for(int i = 1;i < 4;i++) { if(rank[i] < sum) { sum = rank[i]; index = i; } } if(i) printf(" "); printf("%d:%d",sum+1,index+1); /* 某国的排名 */ } printf(" "); }
7-15 寻找大富翁
胡润研究院的调查显示,截至2017年底,中国个人资产超过1亿元的高净值人群达15万人。假设给出N个人的个人资产值,请快速找出资产排前M位的大富翁。
输入格式:
输入首先给出两个正整数N(≤106)和M(≤10),其中N为总人数,M为需要找出的大富翁数;接下来一行给出N个人的个人资产值,以百万元为单位,为不超过长整型范围的整数。数字间以空格分隔。
输出格式:
在一行内按非递增顺序输出资产排前M位的大富翁的个人资产值。数字间以空格分隔,但结尾不得有多余空格。
输入样例:
8 3 8 12 7 3 20 9 5 18
输出样例:
20 18 12
1 #include <stdio.h> 2 3 #define N 1000000 4 int key[N] = {0}; 5 6 int main() 7 { 8 int n,m,x,count=0,flag=0; 9 scanf("%d%d",&n,&m); 10 for(int i=0; i<n; ++i){ 11 scanf("%d",&x); 12 key[x]++; 13 } 14 for(int j=N-1; j>=0 && count<m; --j){ 15 if(key[j]){ 16 if(flag) printf(" "); 17 printf("%d",j); 18 flag = 1; 19 count++; 20 if(key[j]>1){ 21 key[j]--; 22 j++; 23 } 24 } 25 } 26 }