应用题
习题一
已知模式串 t = “abcaabbabcab”, 写出用KMP法求得的每个字符对应的 next 和 nextval函数值。
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 255
typedef struct
{
char ch[MAXLEN + 1];
int length;
}SString;
int next[MAXLEN];
int nextval[MAXLEN];
void get_next(SString str, int next[]);
void get_nextval(SString T, int nextval[]);
int main()
{
SString T = { "abcaabbabcab" ,12 };
get_next(T, next);
get_nextval(T, nextval);
return 0;
}
void get_next(SString T, int next[])
{
next[1] = 0;
//若不设置next[0],则系统默认next[0] = 0
int i = 1; //i即next数组的下标,也是函数next的自变量
int j = 0; //j也是
while (i <= T.length)
{
if (j == 0 || T.ch[i - 1] == T.ch[j - 1])
{
++i;
++j;
next[i] = j;
//i即next数组从1开始的下标,也是函数next的最终自变量
//printf("next[i] = j : next[%d] = %d\n",i,j);
}
else
{
//printf("j = next[j] : j = next[%d] = %d\n",j,next[j]);
j = next[j];
//j也是函数next的自变量,但是它要作为自变量不断地被next函数作用,最后求得next[i]的值
}
}
printf("\nnext数组中的元素为:");
for (int n = 1; n <= T.length; n++)
{
printf("%d ", next[n]);
}
}
void get_nextval(SString T, int nextval[])
{
nextval[1] = 0;
int i = 1;
int j = 0;
int k = 0;
while (i <= T.length)
{
if (j == 0 || T.ch[i - 1] == T.ch[j - 1])
{
++i;
++j;
if (T.ch[i - 1] != T.ch[j - 1]) //注意此时i,j分别增加了一个位置
{
nextval[i] = j;
}
else // T.ch[i - 1] == T.ch[j - 1]
{
k = nextval[j]; //因为模式串中滑动前后的与主串对齐的元素相等,所以一定会将j进行至少一次的next函数作用,使之回退到k
while (T.ch[i - 1] == T.ch[k - 1])
//此时模式串中滑动之后与主串那某个位置对齐的元素位置为第k个,滑动之前为第i个,如果这两个元素还相等,要继续对k进行next函数作用,直到不相等才跳出while循环
{
k = nextval[k];
}
nextval[i] = k; //跳出while循环的k值就是nextval[i]的最终值
}
}
else
{
j = nextval[j];
}
}
printf("\nnextval数组中的元素为:");
for (int n = 1; n <= T.length; n++)
{
printf("%d ", nextval[n]);
}
}
习题二
设目标为 t = “abcaabbabcabaacbacba”, 模式为p = “abcabaa”。 计算模式p 的nextval 函数值;
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 255
typedef struct
{
char ch[MAXLEN + 1];
int length;
}SString;
int next[MAXLEN];
int nextval[MAXLEN];
void get_next(SString str, int next[]);
void get_nextval(SString T, int nextval[]);
int main()
{
SString T = { "abcabaa" ,7 };
get_next(T, next);
get_nextval(T, nextval);
return 0;
}
void get_next(SString T, int next[])
{
next[1] = 0;
//若不设置next[0],则系统默认next[0] = 0
int i = 1; //i即next数组的下标,也是函数next的自变量
int j = 0; //j也是
while (i <= T.length)
{
if (j == 0 || T.ch[i - 1] == T.ch[j - 1])
{
++i;
++j;
next[i] = j;
//i即next数组从1开始的下标,也是函数next的最终自变量
//printf("next[i] = j : next[%d] = %d\n",i,j);
}
else
{
//printf("j = next[j] : j = next[%d] = %d\n",j,next[j]);
j = next[j];
//j也是函数next的自变量,但是它要作为自变量不断地被next函数作用,最后求得next[i]的值
}
}
printf("\nnext数组中的元素为:");
for (int n = 1; n <= T.length; n++)
{
printf("%d ", next[n]);
}
}
void get_nextval(SString T, int nextval[])
{
nextval[1] = 0;
int i = 1;
int j = 0;
int k = 0;
while (i <= T.length)
{
if (j == 0 || T.ch[i - 1] == T.ch[j - 1])
{
++i;
++j;
if (T.ch[i - 1] != T.ch[j - 1]) //注意此时i,j分别增加了一个位置
{
nextval[i] = j;
}
else // T.ch[i - 1] == T.ch[j - 1]
{
k = nextval[j]; //因为模式串中滑动前后的与主串对齐的元素相等,所以一定会将j进行至少一次的next函数作用,使之回退到k
while (T.ch[i - 1] == T.ch[k - 1])
//此时模式串中滑动之后与主串那某个位置对齐的元素位置为第k个,滑动之前为第i个,如果这两个元素还相等,要继续对k进行next函数作用,直到不相等才跳出while循环
{
k = nextval[k];
}
nextval[i] = k; //跳出while循环的k值就是nextval[i]的最终值
}
}
else
{
j = nextval[j];
}
}
printf("\nnextval数组中的元素为:");
for (int n = 1; n <= T.length; n++)
{
printf("%d ", nextval[n]);
}
}
习题三
数组A 中,每个元素A[i,j]的长度均为 32个二进制位,行下标从-1~9, 列下标从 1 ~11,从首地址S 开始连续存放在主存储器中,主存储器字长为 16 位。求:
①存放该数组所需多少单元?
11✖11✖2 = 242
②存放数组第4列所有元素至少需多少单元?
11✖2 = 22
③数组按行存放时,元素A[7,4]的起始地址是多少?
L[7,4] = S + (8✖11 + 3) ✖2 = S + 182
④数组按列存放时,元素A[4, 7]的起始地址是多少?
L[4,7] = S + (6✖11 + 5) ✖2 = S + 142
习题四
请将香蕉 banana 用工具H( )—Head( ), J()—Tail( )从L中取出。
L = (apple, (orange, (strawberry, (banana)), peach), pear)
解:设 A = (banana)
B =(strawberry,A)
C =(orange,B,peach)
则 L =(apple,C,pear)
求出 D = T(H(T(H(T(L)))))= T(B)=(A) =((banana))
注意:T(B)的求法(表尾一定是广义表,有自己的括号)且(A) 和 A 是有区别的。
则H(D)= A 【此时T(D)= () 】
则H(H(D)) = H(A) = banana
答案为:H(H(T(H(T(H(T(L)))))))
算法设计习题
习题五
写一个算法统计在输入字符串中各个不同字符出现的频度并将结果存入文件(字符串中的合法字符为 A~Z 这 26 个字母和 0~9这10个数字)。
#include<stdio.h>
#include<string.h>
#define MAXSIZE 100
int getline(char s[], int limit);
void count(char s[], int length);
int numb[MAXSIZE];
int alp1[MAXSIZE];
int alp2[MAXSIZE];
int main()
{
int len = 0;
char S[MAXSIZE];
while ((len = getline(S, MAXSIZE)) > 0)
{
count(S, len);
}
return 0;
}
int getline(char s[], int limit)
{
int i = 0;
int c = 0;;
for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; i++)
{
s[i] = c;
}
return i;
}
void count(char s[], int length)
{
int i = 0;
int k = 0;
int j = 0;
for (i = 0; i < length; i++)
{
if (s[i] >= '0' && s[i] <= '9')
{
k = s[i] - '0';
numb[k]++;
}
else if (s[i] >= 'a' && s[i] <= 'z')
{
k = s[i] - 'a';
alp1[k]++;
}
else if (s[i] >= 'A' && s[i] <= 'Z')
{
k = s[i] - 'A';
alp2[k]++;
}
}
printf("出现的数字及其频率是:");
for (j = 0; j < 10; j++)
{
if (numb[j] != 0)
{
printf("%c : %d ", j + '0', numb[j]);
}
}
printf("\n出现的小写字母及其频率是:");
for (j = 0; j < 26; j++)
{
if (alp1[j] != 0)
{
printf("%c : %d ", 'a'+j, alp1[j]);
}
}
printf("\n出现的大写字母及其频率是:");
for (j = 0; j < 26; j++)
{
if (alp2[j] != 0)
{
printf("%c : %d ", 'A' + j, alp2[j]);
}
}
//清空每个数组
for (j = 0; j < 26; j++)
{
numb[j] = 0;
alp1[j] = 0;
alp2[j] = 0;
}
printf("\n");
}
习题六
写一个递归算法来实现字符串逆序存储,要求不另设串存储空间。
写一个递归算法来实现字符串逆序存储,要求不另设串存储空间。——qixin622
#include<stdio.h>
#include<string.h>
#define MAXSIZE 100
int InvertStore(char A[]);
int main()
{
char a[MAXSIZE];
int len = 0;
while ((len = InvertStore(a)) > 0)
{
puts(a);
//puts()函数把一个字符串写入到标准输出stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。
memset(a, '\0', sizeof(a)); //清空数组 A 以备下次使用,或者写成a[0] = '\0';
}
return 0;
}
int InvertStore(char A[])
{
char ch;
static int i = 0;//需要使用静态变量,多次调用InvertStore时,只执行一次初始化
ch = getchar();
//printf("ch = %c\n",ch);
while (ch != EOF)
{
if (ch != '\n') //规定'\n'是字符串输入结束标志
{
//printf("进入if\n");
InvertStore(A);
//printf("ch = %c\n", ch);
A[i++] = ch;//字符串逆序存储
//printf("A[i] : A[%d]= %c\n",i-1,A[i-1]);
}
else
{
//printf("进入else\n");
// 当读取到换行符时,重置 i
i = 0;
}
A[i] = '\0'; //字符串结尾标记(没有这句字符之后会打印出乱码)
printf("i = %d\n", i);
return i;
}
}
习题七
编写算法,实现下面函数的功能。函数void insert(chars, chart, int pos)将字符串 t 插入到字符串 s 中,插入位置为 pos。假设分配给字符串 s 的空间足够让字符串 t 插入。(说明:不得使用任何库函数)
#include<stdio.h>
#include<string.h>
#define MAXSIZE 100
void insert(char* s, char* t, int pos);
int main()
{
char s[MAXSIZE];
char t[MAXSIZE];
insert(s, t, 3);
insert(s, t, 5);
return 0;
}
int getline(char s[], int limit)
{
int i = 0;
int c = 0;
for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; i++)
{
s[i] = c;
}
//因为要插入字符串或被插入,而换行符会被算作一个字符,所以不要加换行符
s[i] = '\0'; //结束符不会被算作一个字符,加结束符前后字符串的长度不会有变化
return i;
}
void insert(char* s, char* t, int pos)
{
int i = pos;
int j = 1; //i,j都是位置数
int lens = 0;
int lent = 0;
lens = getline(s, MAXSIZE);
lent = getline(t, MAXSIZE);
printf("lens = %d\n", lens);
printf("lent = %d\n", lent);
printf("\n字符串s中的元素为:");
for (int k = 0; k < lens; k++)
{
printf("%c ", s[k]);
}
printf("\n字符串t中的元素为:");
for (int k = 0; k < lent; k++)
{
printf("%c ", t[k]);
}
for (i = pos; i <= lens; i++)
{
s[i + lent] = s[i];
}
for (j = 1; j <= lent; j++)
{
s[pos + j-1] = t[j - 1];
}
lens = lens + lent;
s[lens] = '\0';
printf("\n新字符串s中的元素为:");
for(int k= 0; k<lens; k++)
{
printf("%c ", s[k]);
}
printf("\n");
}
[C语言][总结] getchar()清除缓冲区 ——FayeValentine77
#include<stdio.h>
#include<string.h>
#define MAXSIZE 100
void insert(char* s, char* t,int pos);
int main()
{
char s[MAXSIZE];
char t[MAXSIZE];
int pos = 0;
int c = 0;
char choice = '\0';
while (1)
{
insert(s, t, pos);
printf("是否继续?(y/n): ");
scanf_s("%c", &choice);
getchar(); // 清除输入缓冲区中的换行符
if (choice != 'y' && choice != 'Y')
break;
printf("\n\n");
}
return 0;
}
int getline(char s[], int limit)
{
int i = 0;
int c = 0;
for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; i++)
{
s[i] = c;
}
//因为要插入字符串或被插入,而换行符会被算作一个字符,所以不要加换行符
s[i] = '\0'; //结束符不会被算作一个字符,加结束符前后字符串的长度不会有变化
//getchar(); // 清除输入缓冲区
return i;
}
void insert(char* s, char* t, int pos)
{
int lens = 0;
int lent = 0;
printf("请输入字符串s:");
lens = getline(s, MAXSIZE);
printf("lens = %d\n", lens);
printf("字符串s中的元素为:");
for (int k = 0; k < lens; k++)
{
printf("%c ", s[k]);
}
printf("\n请输入字符串t:");
lent = getline(t, MAXSIZE);
printf("lent = %d", lent);
printf("\n字符串t中的元素为:");
for (int k = 0; k < lent; k++)
{
printf("%c ", t[k]);
}
printf("\n请输入要插入的位置数pos:");
scanf_s("%d", &pos);
getchar();
int i = pos;
int j = 1; //i,j都是位置数
//将s中的字符从第pos个位置开始,依次向后移动lent个位置
for (i = pos; i <= lens; i++)
{
s[i + lent] = s[i];
}
for (j = 1; j <= lent; j++)
{
s[pos + j-1] = t[j - 1];
}
lens = lens + lent;
s[lens] = '\0';
printf("\n新字符串s中的元素为:");
for(int k= 0; k<lens; k++)
{
printf("%c ", s[k]);
}
printf("\n新lens = %d\n\n",lens);
}
习题八
已知字符串S1 中存放一段英文,写出算法 format(s1, s2, s3, n), 将其按给定的长度n格式化成两端对齐的字符串S2, 其多余的字符送 S3。
#include<stdio.h>
#include<string.h>
#define MAXSIZE 100000
void format(char* s1, char* s2, char* s3, int n);
int main()
{
char s1[MAXSIZE];
char s2[MAXSIZE];
char s3[MAXSIZE];
char t[MAXSIZE];
int n = 0;
int c = 0;
char choice = '\0';
while (1)
{
format(s1, s2, s3, n);
printf("\n是否继续?(y/n): ");
scanf_s(" %c", &choice); //这里的"%c"前面必须要有空格,否则输入y也会退出主函数
getchar(); // 清除输入缓冲区中的换行符
if (choice != 'y' && choice != 'Y')
break;
printf("\n\n");
}
return 0;
}
int getline(char s[], int limit)
{
int i = 0;
int c = 0;
for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; i++)
{
s[i] = c;
}
//因为要插入字符串或被插入,而换行符会被算作一个字符,所以不要加换行符
s[i] = '\0'; //结束符不会被算作一个字符,加结束符前后字符串的长度不会有变化
//getchar(); // 清除输入缓冲区
return i;
}
void format(char* s1, char* s2, char* s3, int n)
{
int k1 = 0;
int k = 0;
int i = 0;
int j = 0;
int num = 1;
int a = 0;
int b = 0;
int c = 0;
printf("请输入字符串S1:");
k1 = getline(s1, MAXSIZE);
printf("k1 = %d\n", k1);
printf("字符串S1中的元素为:");
for (int c = 0; c < k1; c++)
{
printf("%c", s1[c]);
}
while (1)
{
printf("\n请输入S2的长度n:");
scanf_s("%d", &n);
if (n > k1)
{
printf("输入的n过大,请输入小于等于%d的数值:", k1);
continue;
}
else
{
break;
}
}
while (1)
{
for (k = k; s1[k] != '\0' && s1[k] == ' '; k++)
{
; //跳过s1前面的空格
}
//从第一个非空格元素开始,将s1中的n-1个元素写进s2中
for (i = k, j = 0; i < k1 && j < n - 1; i++, j++)
{
s2[j] = s1[i];
}
//跳出该for循环时,可能是i>=k1,也可能是j>=n-1
if (i >= k1)
{
printf("\n在第%d次截取s1中的字符时,s1中的非空格字符小于%d,将把s1中的剩余字符都写进s3中,此轮s2字符串为空。\n",num,n);
for (a = k,b = 0; a < k1; a++, b++)
{
s3[b] = s1[a];
}
if (b > 0)
{
printf("处理之后,s3中的元素为:");
for (c = 0; c < b; c++)
{
printf("%c", s3[c]);
}
}
else
{
printf("处理之后,s3中不含任何元素。");
}
break;
}
if (j = n - 1)
{
while (s1[i] != '\0' && s1[i] == ' ')
{
i++; //跳过空格
}
s2[j] = s1[i];
k = i + 1;
}
printf("\n处理之后,第%d个s2中的元素为:", num);
for (c = 0; c <= j; c++)
{
printf("%c", s2[c]);
}
s2[0] = '\0'; //将s2清空
num++;
}
s1[0] = '\0';
s2[0] = '\0';
s3[0] = '\0';
}
习题九
设二维数组a[l…m, l…n]含有m x n个整数。
①写一个算法判断a中所有元素是否互不相同?输出相关信息(yes/no);
②试分析算法的时间复杂度。
#include<stdio.h>
#define MAXSIZE 10
int JudgEqual(int a[][10], int m, int n);
int main()
{
int a[MAXSIZE][MAXSIZE]; //实参维数AXSIZE必须等于形参10
int m = 0;
int n = 0;
int result = 0;
int i = 0;
int j = 0;
char choice = '\0';
while (1)
{
printf("请输入二维数组的行数:");
scanf_s(" %d", &m); //但是输入的m和n可以小于MAXSIZE即10
printf("请输入二维数组的列数:");
scanf_s(" %d", &n);
printf("请输入二维数组中的元素(每个元素之间用空格分开,输完一行之后按回车):\n");
for (i = 0; i < m; i++)
{
printf("第%d行中的元素为:", i + 1);
for (j = 0; j < n; j++)
{
scanf_s("%d", &a[i][j]);
}
}
result = JudgEqual(a, m, n);
if (result == 1)
{
printf("数组中的元素各不相同。YES");
}
else
{
printf("数组中有相同的元素。NO");
}
printf("\n是否继续?(y/n): ");
scanf_s(" %c", &choice); //这里的"%c"前面必须要有空格,否则输入y也会退出主函数
getchar(); // 清除输入缓冲区中的换行符
if (choice != 'y' && choice != 'Y')
break;
printf("\n\n");
}
return 0;
}
//判断二维数组中所有元素是否互不相同,如是,返回1;否则,返回0。
int JudgEqual(int a[][10], int m, int n)
{
int i = 0;
int j = 0;
int p = 0;
int k = 0;
for (i = 0; i < m; i++)
{
for (j = 0; j < n - 1; j++) //要与其它元素比较的第i行的那个元素,其所在的那一列不用比较,即它自己不用跟自己比较,所以j<n-1
{
for (p = j + 1; p < n; p++) //和同行其它元素比较,p为列数
{
if (a[i][j] == a[i][p])
{
return 0;
}
}
for (k = i + 1; k < m; k++) //和第i+1行及以后元素比较,k为行数,p为列数
{
for (p = 0; p < n; p++)
{
if (a[i][j] == a[k][p])
{
return 0;
}
}
}
}
}
return 1;
}
②时间复杂度:
每个位置都可能有相等的元素
平均比较次数是每个位置匹配成功的概率Pi与每个位置匹配成功所用的次数乘积的加和。
最好情况下:
针对每个位置,比较次数为1。即其与它后面那个元素是相等的。
这mn-1个位置上均可能存在相等元素,即在每个位置上结束比较的概率为1 / (mn-1). 所以平均比较次数为:
1 * [1 / (mn-1) ]+ 1 * [1 / (mn-1) ] + …… +1 * [1 / (m*n-1) ]= 1.
即最好情况下的平均时间复杂度是O(1).
最坏情况下:
针对第i个位置,比较次数为mn – i,即其与最后一个元素(第mn-1个)是相等的。
这mn-1个位置上均可能存在相等元素,即在每个位置上结束比较的概率为1 / (mn-1). 所以平均比较次数为:
(mn-1) * [1 / (mn-1) ] + (mn-2) * [1 / (mn-1) ] + …… + 1 * [1 / (mn-1) ]
= [1 / (mn-1) ] * [(mn-1) + (mn-2) + …… + 1 ]
= [1 / (mn-1) ] * [ (mn) * (mn-1) / 2 ]
= (m*n) / 2
即最坏情况下的平均时间复杂度是O(n x m).
习题十
设任意 n个整数存放于数组A[l…n]中, 试编写算法, 将所有正数排在所有负数前面(要求:算法时间复杂度为O(n))。
#include<stdio.h>
#define MAXSIZE 10
void Arrange(int A[], int n);
//-1 1 2 6 -3
//0 -2 6 -1 -3 5 9
int main()
{
int A[MAXSIZE];
int n = 0;
char choice = '\0';
while (1)
{
Arrange(A, n);
printf("\n是否继续?(y/n): ");
scanf_s(" %c", &choice); //这里的"%c"前面必须要有空格,否则输入y也会退出主函数
getchar(); // 清除输入缓冲区中的换行符
if (choice != 'y' && choice != 'Y')
break;
printf("\n\n");
}
return 0;
}
void Arrange(int A[], int n)
{
printf("请输入数组A中的元素个数:");
scanf_s(" %d", &n);
int k = 0;
int i = 0;
int j = n - 1; //这个初始化一定要写在输入n的后面!!!
int x = 0; //用于交换
printf("请输入数组A中的元素(每个元素之间用空格分开,输完一行之后按回车):");
for (k = 0; k < n; k++)
{
scanf_s("%d", &A[k]);
}
while (i < j) //i=j时停止
{
//i自小至大搜索到负数停止
while (i < j && A[i] >= 0)
{
i++;
}
//j自大至小搜索到正数停止
while (i < j && A[j] < 0)
{
j--;
}
//在遍历过后i仍然小于j时,交换A[i]与A[j]
if (i < j)
{
x = A[i];
A[i++] = A[j];
A[j--] = x;
}
}
printf("处理过后数组A中的元素为:");
for (k = 0; k < n; k++)
{
printf("%d ", A[k]);
}
}
最好情况下:
数组A中数已排好,正数在前,负数在后,不发生交换。
即最好情况下的平均时间复杂度是O(1).
最差情况下:
数组A中正数全部在后,负数全部在前,需要进行n/2次交换。
即最坏情况下的平均时间复杂度是O(n).