第十次实验
题目一
(1)完全用指针操作,像一个数组输入10个值,并输出其中的最大值及其下标。
思路
就是把s数组的索引改成指针。没啥好说的,代码见。
#include <stdio.h>
#include <stdlib.h>
#define N 10
int main()
{
int arr[N] = { 0 };
int* pMax = arr;
printf("请输入十个整数:\n");
for (int i = 0; i < N; ++i)
{
scanf("%d", arr+i);
if (i > 0 && *pMax < *(arr + i))
{
pMax = arr + i;
}
}
printf("最大值为:%d, 下标为%d\n", *pMax, pMax-arr);
return EXIT_SUCCESS;
}
题目二
(2)在冒泡排序中,需要比较相邻的两个元素的大小,并决定是否需要交换。改写冒泡排序的程序,其中至少包含两个函数:bubble_sort(int a[],int n)和swap,bubble_sort用于将数组a中的n个元素排序,其中调用函数swap来交换两个数。
思路
回头再扯扯冒泡的优化问题。
#include <stdio.h>
#include <stdlib.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void bubble_sort(int a[], int n)
{
int boundary = n;
for (int i = 0; i < n; ++i)
{
int pos = 0;
int isChanged = 0;
for (int j = 0; j < boundary-1; ++j)
{
if (a[j] > a[j+1])
{
swap(a + j, a + j + 1);
isChanged = 1;
pos = j+1;
}
}
boundary = pos;
if (!isChanged) break;
}
}
int main()
{
int arr[8] = { 5,8,6,3,9,2,1,7 };
bubble_sort(arr, 8);
for (int i = 0; i < 8; ++i)
{
printf("%d ", arr[i]);
}
return EXIT_SUCCESS;
}
题目三
(3)编写一个函数void deleteChar(char *s,char c),在字符串s中删除字符c。如果该字符不存在,则什么都不做。例如,在“Duang Huan”中删除了‘u’后,变成了字符串“Dang Han”。
思路1
最简单的想法就是遍历字符串s中的每个元素,出现c就将c后面的字符全部向前移动一位,这样c就被覆盖了;依次不断进行下去,直到遍历结束。
例如
思路二(采用)
在思路一中,通过第一轮比较后移动元素时发现,我们将后面的u也一起移动了,这样,在后续p指针的移动比较时,需要将曾经移动的元素也一一比较。如果在移动的过程中也进行比较,我们可以快速的定位下一个u的位置,提高了程序效率。
#include <stdio.h>
#include <stdlib.h>
void deleteChar(char* s, char c)
{
char* p = s;
int step = 1;// 移动步长
// 找第一个c的位置
while (*p != c && *p != '\0') ++p;
// s中存在c,执行删除操作
while (*p != '\0')
{
char* tmp = p+1;
if (*tmp == c) ++step;
else *(p-step+1) = *tmp;
++p;
}
}
int main()
{
//char s[] = "uduuang uhuan";
char s[] = "Duang Huan";
deleteChar(s, 'u');
printf("%s",s);
return EXIT_SUCCESS;
}
题目四
(4)编写一个函数str_cat,实现函数strcat功能。要求用指针实现。
思路
strcat()是C语言头文件string.h
中的字符串连接函数。
仿照设计str_cat(const char* dst, char* src),直接将字符串src复制到dst的末尾即可。
#include <stdio.h>
#include <stdlib.h>
// 假设dst空间足够大
void str_cat(char* dst, const char* src)
{
char* d = dst;
const char* s = src;
while (*d != '\0') ++d;
while (*s != '\0')
{
*d = *s;
++d; ++s;
}
*d = *s;
}
int main()
{
char dst[100] = "abcdefg";
char src[10] = "abcde";
str_cat(dst, src);
printf("%s", dst);
return EXIT_SUCCESS;
}
题目五
(5)输入一个长度大于3的字符串,从中随机选取3个字符构成一个新的字符串,输出这个新的字符串。分两种情况:
a)一个位置上的字符可以多次被选取。
b)一个位置上的字符只能被选取一次。
思路
第一种情况比较容易,用随机数生成三个随机位置,按位置取字符串中的字符,然后拼接在一起即可。
至于第二种情况,只需要保证生成的三个随机位置不重复即可。可以用数组存储取过的位置,这样每次生成随机数都检验是否曾经生成过一样的随机数即可。如果数组中存在重复的数,就再次生成;如果不存在,说明不重复,可以使用,并将该数放入数组。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_SIZE 100
int str_len(const char* s)
{
int len = 0;
while (*(s + len) != '\0') ++len;
return len;
}
int found( int *arr, int pos)
{
int flag = (*arr)&(1 << pos);
*arr = (*arr) | (1 << pos);
return flag;
}
void case1(const char* s, char* d, int n)
{
int len = str_len(s);
for (int i = 0; i < n; ++i)
{
int tmp = rand() % len;
*(d + i) = *(s + tmp);
}
}
void case2(const char* s, char* d, int n)
{
int len = str_len(s);
int arr_flag = 0;
for (int i = 0; i < n; ++i)
{
int tmp_pos = 0;
while (1)
{
tmp_pos = rand() % len;
if (!found(&arr_flag, tmp_pos)) break;
}
*(d + i) = *(s + tmp_pos);
}
}
int main()
{
srand((unsigned int)time(NULL));
printf("Please input string end with '#':\n");
char s[MAX_SIZE];
char ch;
int i = 0;
while ((ch = getchar()) != '#') s[i++] = ch;
s[i] = '\0';
getchar();
char d[5] = {0};
case2(s,d,3);
printf("%s",d);
return EXIT_SUCCESS;
}
题目六
(6)输入一个n位数(n>=3),找出其中3个连续的数字构成的数中最大的那一个。例如,输入26895,则3个连续的数字构成的数是268、689、895.
思路
在位数比较少的情况下,可以直接计算出所有所有的连续三位数,并比较大小,输出最大数即可。
但是一旦n过大,或者找出最大的连续的100位数这种方法就无法处理了。
因此,我们可以这样考虑:
因为在n位数中取连续的三位数比较容易,困难的是比较大小,因此,本题的关键在于如果在取数的过程中找出最大值,问题又可以减小为如何比较三位数大小。
比较三位数大小就比较简单了,百位与百位比,十位与十位比,个位与个位比即可。
因为是连续的数字,因此本题中只需比较n中0~n-3
号位置的元素,即2、6、8的大小,也就是说先比较三位数中的百位数,取百位最大的,那么这个三位数也是最大的。本题中不难看出8是最大的。
但如果出现百位数相同呢,又或者百位十位都相同呢?
如果出现这种情况,就依次比较下去,直至分出大小。
#include <stdio.h>
#include <stdlib.h>
void exam(const char* s, int n)
{
const char* p = s;
const char* pMax = s;
++p;
while (*(p+n-1) != '\0')
{
if (*pMax < *p) pMax = p;
else if (*pMax == *p)
{
int flag = 0;
for (int i = 0; i < n; ++i)
{
if (*(pMax + i) < *(p + i)) { flag = 1; break; }
}
if (flag) pMax = p;
}
++p;
}
for (int i = 0; i < n; ++i)
{
printf("%c", *(pMax+i));
}
}
int main()
{
char s[] = "2688879795";
exam(s, 3);
return EXIT_SUCCESS;
}
题目七
(7)输入一个字符串,其中含有带括号的子串,输出不含带括号子串的字符串。例如,输入“Microsoft(MS) Office”,输出“Microsoft Office”。
思路
在字符串中寻找 ‘(’ 和 ‘)’ ,同时存在且出现循序合法就将括号连同中间的字符串一起删除
#include <stdio.h>
#include <stdlib.h>
// 功能:去除字符串中所有格式正确的括号(不支持括号的嵌套),若要支持括号的嵌套,需要使用栈进行操作
void removeBrackets(char* s)
{
char* p = s, * left = NULL, * right = NULL;
while (*p != '\0')
{
if (*p == '(') left = p;
else if (*p == ')') right = p;
if (left && right && right > left)
{ // 移动操作
// 递归移动
removeBrackets(p + 1);
int step = right - left + 1; // 移动步长
// 全部移动(由于递归的原因,后面的括号都已经去除)
while (*right != '\0')
{
*left = *(right + 1);
++left; ++right;
}
// 移动完成,重置left,right
left = right = NULL;
}
++p;
}
}
int main()
{
char s[] = "Microsoft(MS) Office";
removeBrackets(s);
printf("%s", s);
return EXIT_SUCCESS;
}
题目八
(8)读入一个含有注释信息的C程序,将其中的所有注释信息清理掉后,保存为另一个文件。
思路
没想到特别好的方法,因此,直接将文件所有内容读入内存处理。程序效率低,最好使用两个文件指针,一个读,一个写。
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#define FILE_MAX_SIZE (1<<20)
// 返回'/'的指针
char* hasTheOtherSymbol(char* s)
{
char* p = s + 2;
while (*p != '\0')
{
if (*p == '*' && *(p + 1) == '/') break;
++p;
}
return p + 1;
}
int removeSingleLineComment(char* s)
{
char* p = s+2;
char* src = s;
while (*p != '\0' && *p != '\n') ++p;
while (*p != '\0')
{
*src = *(p + 1);
++src; ++p;
}
return p - src + 1;
}
// src指向第一个/,dst指向后一个/
int removeMultiLineComment(char* src, char* dst)
{
char* from = src;
char* p = dst + 1;
while (*p != '\0')
{
*src = *p;
++src; ++p;
}
return p - src + 1;
}
int removeComment(char* s, int len)
{
char* p = s;
while (*p != '\0')
{
if (*p == '/' && *(p + 1) == '/')
{
// 将//到\n之间的字符全部删除
len -= removeSingleLineComment(p);
}
else if (*p == '/' && *(p + 1) == '*')
{
char* dst = hasTheOtherSymbol(p);
if (*dst != '\0')
{
len -= removeMultiLineComment(p, dst);
}
}
++p;
}
return len;
}
int main()
{
char* fileCharArray = (char*)malloc(sizeof(char) * FILE_MAX_SIZE);
if (!fileCharArray)
{
printf("内存分配失败");
exit(-1);
}
memset(fileCharArray, '\0', sizeof(char) * FILE_MAX_SIZE);
int charArrayLength = 0,
finalLength = 0;
FILE* fp = NULL;
if (!(fp = fopen("test.c", "r")))
{
printf("读取文件失败");
exit(-1);
}
fseek(fp, 0L, SEEK_END);
size_t fileLength = ftell(fp);
fseek(fp, 0L, SEEK_SET);
charArrayLength = fread(fileCharArray, sizeof(char), fileLength, fp);
fp = freopen("test.c", "w", fp);
finalLength = removeComment(fileCharArray, charArrayLength);
fseek(fp, 0, SEEK_SET);
memset(fileCharArray + finalLength+1, '\x20', sizeof(char) * (charArrayLength - finalLength));
fwrite(fileCharArray, sizeof(char), charArrayLength, fp);
fclose(fp);
return EXIT_SUCCESS;
}
题目九
(9)“洗牌”是一种非常常见的操作。所谓洗牌,就是给定一个序列,随机产生一个新的排列。例如,数据序列是:5 18 36 21 11 25,每洗一次牌,就产生一个完全不同的新排列。写一个程序,实现洗牌操作。
思路
Knuth Shuffle算法
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void Knuth_Shuffle(int arr[], int n)
{
for (int i = n - 1; i >= 0; --i)
{
swap(&arr[rand() % (i+1)], &arr[i]);
}
}
int main()
{
srand((unsigned int)time(NULL));
int arr[] = { 5, 18, 36, 21, 11, 25 };
int n = 6;
Knuth_Shuffle(arr, n);
return EXIT_SUCCESS;
}
结语
- 代码仅对思路作出实现,效率比较低下,代码质量较差,还请谅解。
- 若以上思路或程序有误,还请不吝赐教。
- 如果您有改进意见,欢迎指出。
- 程序的相关问题都欢迎交流。