文章目录
- 1.完美乘法:a*b=c,a、b、c中只出现0~9的数字,且每个数字在这个等式中只出现一遍。要求输出所有符合条件的式子和式子总数。
- 2.求数组中第k小的数
- 3.【约瑟夫环问题】n个人围成圈,从第一个人开始1~3报数,数到3的人退出,直到只有1个人,问剩下来的这个人的编号是多少。
- 4.输出一个数n的所有质因数;
- 5.输出范围为n以内的所有质数,并统计数量。
- 6.链表逆置
- 7.求2/1+3/2+5/4+8/5+...的前n项之和。
- 8.从1-100中找出符合一下条件的数,输入数字d,找到该数和该数的平方中都含有输入的数字d
- 9.输入两个字符串str1,str2,在str1中删除str2中出现过的字符,最后输出str1。
- 10.如果有两个数,每个数的所有约数(除了本身外)的和等于对方,那么成为这两个数为互满数。求出30000内所有的互满数并输出。
- 11.输入一个字符串,将连续相同的字符删减为一个,以"*"作为字符串结尾。
- 12.输入字符串s1和s2,输入m,在字符串s1的第m个位置插入s2,并输出插入后的s1字符串。
- 13.读程序、写出功能并且指出需要改正的地方的题目。
1.完美乘法:a*b=c,a、b、c中只出现0~9的数字,且每个数字在这个等式中只出现一遍。要求输出所有符合条件的式子和式子总数。
输出格式:
“a * b = c”
“x” //x为符合条件的式子数量
#include<bits/stdc++.h>
using namespace std;
int main(){
int count=0;
int f[10],s[3];
/*
这里讲为什么两个for循环这样设置
因为只能出现10个数字
c的取值必须是5位数
那么a和b的取值要么是1位*4位,要么是2位*3位
不会再出现其他情况了
即使是手写代码,也一定要注意不能出现时间复杂度高于5*10^8这个量级
不然会超出1s的执行时间,大多数OJ就不会通过这样的代码
*/
for(int i=1; i<9999; i++){
for(int j =1; j<99; j++){
int key = 0;
for(int x=0; x<10; x++) f[x]=0; //初始化出现的数字数组
int k = i*j;
s[0]=i; //存储暴力搜索的三个数
s[1]=j;
s[2]=k;
for(int x=0; x<3; x++){ //这三个数分别记录已出现的数字
int y=s[x];
while(y>0){
int t = y%10;
f[t]++;
y=y/10;
}
}
for(int x =0; x<10; x++)
if(f[x]!=1) key++; //若已存储数字超过1 或者为0 说明不符合题意
if(key ==0){ //若为key=0 则说明符合题意
count ++;
cout << k <<" = "<< i<< " * "<< j<<endl;
}
}
}
cout<<count;
return 0;
}
2.求数组中第k小的数
解题思想:利用快速排序做一个升序序列,直接输出第k个数即可。
//代码如下:
#include<iostream>
using namespace std;
void quick_sort(int a[], int l, int r){
if(l>=r) return;
int i=l,j=r-1;
int x = a[r];
while(i<j){
while(i<j && a[i]<x) i++;
while(i<j && a[j]>=x) j--;
int t= a[i];
a[i]=a[j];
a[j] =t;
}
//最后再移动枢纽
if(a[i]>= a[r]){
int t= a[i];
a[i]=a[r];
a[r] =t;
}
else i++;
if(i)
quick_sort(a,l,i-1);
quick_sort(a,i+1,r);
}
int main(){
int a[1000];
int n,k;
cin >> n>>k;
for(int i=0; i<n; i++) cin >> a[i];
quick_sort(a,0,n-1); //做升序即可
for(int i=0; i<n;i++) cout <<a[i]<<' ';
cout << endl;
cout << a[k];
return 0;
}
3.【约瑟夫环问题】n个人围成圈,从第一个人开始1~3报数,数到3的人退出,直到只有1个人,问剩下来的这个人的编号是多少。
输入样例:100
输出样例:91
//代码一:本代码只输出最后剩下来的这个人的编号
#include<bits/stdc++.h>
using namespace std;
int n, m;
int main()
{
cin >> n >> m;
int p = 0;
for (int i = 2; i <= n; i++) {
p = (p + m) % i;
}
cout << p + 1;
return 0;
}
若是要依次输出退出的人的编号,那么代码又是什么样的呢?
//代码一:
//利用STL容器中的queue很容易就实现了
#include<bits/stdc++.h>
using namespace std;
queue<int>q;
int cnt;
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i){
q.push(i);
}
while (!q.empty()){
cnt++;
int x = q.front();
q.pop();
if (cnt == k){
cnt = 0;
cout << x << " ";
}
else q.push(x);
}
return 0;
}
//代码二:
//构造单循环链表
#include<iostream>
using namespace std;
struct Node {
int id;
Node* next;
Node(int x) :id(x), next(NULL) {};
};
Node* Head = new Node(1);
Node* End = Head;
int n, m;
int cnt = 1;
void add(int k){ //单循环链表关键步骤
Node* p = new Node(k);
End->next = p;
End = End->next;
}
int main(){
cin >> n >> m;
for (int i = 2; i <= n; ++i){
add(i);
}
End->next = Head;
Node* t = Head;
while (t){
if (++cnt == m){
cout << t->next->id << " ";
t->next = t->next->next;//删除操作
if (t == t->next)break; //特判一下
cnt = 1;
}
t = t->next;
}
cout << t->id;
return 0;
}
4.输出一个数n的所有质因数;
//法一:递归法
#include<iostream>
using namespace std;
void prim(int n, int i){
if(i>n) return;
if(n%i==0){
cout << i<<' ';
prim(n/i,i);
}
if(n>0 && n%i!=0) prim(n, i+1);
}
int main(){
int x;
cin >> x;
prim(x,2);
return 0;
}
变体1:输出两个数的最大公因数gcd,最小公倍数lcm。
#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b){ //辗转相除法 要求a<b
if(a%b!=0) return gcd(b,a%b);
return b;
}
int main(){
int a,b;
cin >> a>> b;
if(a<b) swap(a,b);
cout << gcd(a,b)<<endl;
cout <<"lcm(a,b):"<<a/gcd(a,b)*b;
return 0;
}
变体2:输入n个数字,求出其最小公倍数。
#include<bits/stdc++.h>
using namespace std;
int x[10010];。
int gcd(int a, int b){ //辗转相除法 要求a<b
if(a%b!=0) return gcd(b,a%b);
return b;
}
int lcm(int a, int b){
if(a<b) swap(a,b);
return a/gcd(a,b)*b;
}
int main(){
int n; //n Numbers
cin >> n;
for(int i=0; i<n; i++) cin >> x[i];
int k=x[0];
for(int i=1; i<n; i++){
k = lcm(k,x[i]);
}
cout << k;
return 0;
}
5.输出范围为n以内的所有质数,并统计数量。
//主要代码如下
//CodeBegin
int sieve(int n){
int i,j,k;
k=0;
vis[0]=vis[1]=1;
for(i=2;i<=n;i++){
if(vis[i]==0) prime[k++]=i;
for(j=i+i;j<=n;j+=i) vis[j]=1;
}
return k;
}
//CodeEnd
#include<stdio.h>
#include<string.h>//memset函数的头文件
//设定一个宏,定义数组大小
#define maxn 20010
int vis[maxn];//vis用来判断数字是否访问过
int prime[maxn];//prime用来存储筛选出来的素数
int sieve(int n){
int i,j,k;
k=0;//i用来控制逐个访问,j用来第二轮标记,k用来标记prime数组位置
memset(vis,0,sizeof(int)*maxn);
vis[0]=vis[1]=1;
for(i=2;i<=n;i++){
if(vis[i]==0)//这个数是目前最小的,且未被访问过的
prime[k++]=i;//因此这个数是素数
for(j=2;i*j<=n;j++)//j表示倍数,从两倍开始倍增,直到上界为止
vis[i*j]=1;//将倍数标记为已访问
}
return k;//返回范围内素数的个数
}
void print(int k)//打印结果函数
{
int i;
for(i=0;i<k;i++){
printf("%5d ",prime[i]);//每个数字占5个宽度
if((i+1)%5==0)//每五个为一组换行
printf("\n");
}
printf("\n\n");
}
int main(){
int n;//求n以内的素数
int k;//保存返回的素数个数
while(scanf("%d",&n)!=EOF){
k=sieve(n);
if(k==0)
printf("(1-%d]范围内没有素数\n",n);
else
printf("(1-%d]范围内的素数有:\n",n);
print(k);
}
}
6.链表逆置
如果是填空题,注意多想些情况就好,如果是编程题就随意发挥,正确即可。
最好就是直接用头插法
如果填空题中不是头插法,那就举例子,例子中赋值好理解代码。
7.求2/1+3/2+5/4+8/5+…的前n项之和。
//找规律+模拟
#include<bits/stdc++.h>
using namespace std;
int main(){
double i=1,j=2,m,ans=0;
int n;
cin >> n;
for(int k=1; k<=n; k++){
ans += j/i;
m = i+j;
i=j;
j=m;
}
cout << ans;
return 0;
}
8.从1-100中找出符合一下条件的数,输入数字d,找到该数和该数的平方中都含有输入的数字d
//模拟即可
#include<bits/stdc++.h>
using namespace std;
int main(){
int d;
cin >> d;
for(int i=1; i<101; i++){
int x=i, y=i*i;
bool flag1=false, flag2=false;
while(x){
int m = x%10;
if(m==d){
flag1=true;
break;
}
x /= 10;
}
while(y){
int m = y%10;
if(m==d){
flag2=true;
break;
}
y /= 10;
}
if(flag1&&flag2){
cout << i<<' '<<i*i<<endl;
}
}
return 0;
}
9.输入两个字符串str1,str2,在str1中删除str2中出现过的字符,最后输出str1。
三种做法:
//法一:指针法:
#include <stdio.h>
void deleteChars(char *str1, const char *str2) {
if (str1 == NULL || str2 == NULL) {
return;
}
char *p1 = str1;
char *p2 = str2;
while (*p2 != '\0') {
char *temp = p1;
char *q = p1;
while (*q != '\0') {
if (*q != *p2) {
*temp = *q;
temp++;
}
q++;
}
*temp = '\0';
p2++;
}
}
int main() {
char str1[] = "abcdef";
const char str2[] = "bd";
deleteChars(str1, str2);
printf("%s\n", str1); // 输出: "acef"
return 0;
}
//法二:数组暴力求解
#include <stdio.h>
#include <string.h>
void removeChars(char str1[], char str2[]) {
int i, j, k;
int len1 = strlen(str1);
int len2 = strlen(str2);
// 标记str2中字符出现情况
int flag[256] = {0};
// 标记str2中字符
for (i = 0; i < len2; i++) {
flag[str2[i]] = 1;
}
// 移动str1中不在str2中出现的字符
j = 0;
for (i = 0; i < len1; i++) {
if (!flag[str1[i]]) {
str1[j++] = str1[i];
}
}
str1[j] = '\0';
}
int main() {
char str1[100], str2[100];
scanf("%s", str1);
scanf("%s", str2);
// 调用函数删除字符
removeChars(str1, str2);
// 输出结果
printf("Resulting string: %s\n", str1);
return 0;
}
//法三:桶数组记录/map记录求解 C/C++都可以做
#include <stdio.h>
#include <string.h>
#define MAX_CHAR 256
void deleteChars(char str1[], char str2[]) {
int bucket[MAX_CHAR] = {0}; // 初始化桶数组
// 在桶数组中标记str2中出现的字符
for (int i = 0; str2[i] != '\0'; i++) {
bucket[(int)str2[i]] = 1;
}
int index = 0;
// 遍历str1,将不在桶数组中出现的字符添加到结果字符串中
for (int i = 0; str1[i] != '\0'; i++) {
if (bucket[(int)str1[i]] == 0) {
str1[index++] = str1[i];
}
}
str1[index] = '\0'; // 在末尾添加字符串结束标志
}
int main() {
char str1[100], str2[100];
printf("Enter str1: ");
scanf("%s", str1);
printf("Enter str2: ");
scanf("%s", str2);
deleteChars(str1, str2);
printf("Result: %s\n", str1);
return 0;
}
10.如果有两个数,每个数的所有约数(除了本身外)的和等于对方,那么成为这两个数为互满数。求出30000内所有的互满数并输出。
#include <stdio.h>
#define MAX_NUM 30000
// 计算一个数的所有约数的和
int sumOfDivisors(int num) {
int sum = 1; // 包括1作为约数
for (int i = 2; i <= num / 2; i++) {
if (num % i == 0) {
sum += i;
}
}
return sum;
}
int main() {
for (int i = 1; i <= MAX_NUM; i++) {
int sum1 = sumOfDivisors(i); // 第一个数i的所有约数之和为sum1
int sum2 = sumOfDivisors(sum1); // 第一个数i的所有约数之和sum1的所有约数之和为sum2
// 检查是否成为互满数
if (sum2 == i && sum1 != i) { //sum1!=i条件是为了去重
printf("(%d, %d)\n", i, sum1);
}
}
return 0;
}
11.输入一个字符串,将连续相同的字符删减为一个,以"*"作为字符串结尾。
//典型的用do...while循环来实现
#include <stdio.h>
int main() {
char ch;
char old=' ',new_;
scanf("%c",&ch);
do{
new_ = ch;
if(new_==old) continue;
old = new_;
putchar(ch);
}while(scanf("%c",&ch) && ch!='*');
return 0;
}
12.输入字符串s1和s2,输入m,在字符串s1的第m个位置插入s2,并输出插入后的s1字符串。
#include <stdio.h>
#include <string.h>
void insertString(char s1[], char s2[], int m) {
int len1 = strlen(s1);
int len2 = strlen(s2);
// 检查输入合法性
if (m < 0 || m > len1) {
printf("Invalid position!\n");
return;
}
// 将s2插入s1的第m个位置
//先把中间为len2的空留出来
for (int i = len1; i >= m; i--) {
s1[i + len2] = s1[i]; // 后移len2个位置
}
//在中间长度为len2的空填充str2
for (int i = 0; i < len2; i++) {
s1[m + i] = s2[i]; // 插入s2
}
s1[len1 + len2] = '\0'; // 添加字符串结束标志
}
int main() {
char s1[100], s2[100];
int m;
printf("Enter s1: ");
scanf("%s", s1);
printf("Enter s2: ");
scanf("%s", s2);
printf("Enter the position to insert s2 into s1: ");
scanf("%d", &m);
insertString(s1, s2, m);
printf("Result: %s\n", s1);
return 0;
}
13.读程序、写出功能并且指出需要改正的地方的题目。
需要注意:
①**数组边界检查:**在处理数组时,要确保不会越界访问数组元素,这可能导致程序崩溃或产生未定义的行为。
如数组大小固定的话,写出“数组应该用动态内存的方法,在输入n以后在动态申请数组的大小”。
在动态内存分配的情况下,要确保释放分配的内存,以防止内存泄漏。
②一些边界验证:
在插入字符串时,应该确保目标数组足够大,以防止溢出。
冒泡排序的两个循环体的循环条件分别是什么?注意到了吗?
冒泡排序多种写法的总结如下:
/*
需要注意的是每个循环的循环终止条件的不同
可以完成相同的排序 但是过程可以不同
*/
//传统冒泡排序1
for (i = 0; i < n-1; i++) {
//bool flag = false;
for (j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
//flag = true;
}
}
//if(!flag) break; //本轮没交换 说明排序完成
}
// 改变内层循环终止条件的冒泡排序2
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
for (int j = i+1; j < n; j++) {
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
//改变内层循环终止条件的冒泡排序3
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = n - 1; j > i; j--) {
if (arr[j - 1] > arr[j]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
}
③输入缓冲区溢出: 使用了scanf
函数读取字符串输入,然而这种方式可能导致输入缓冲区溢出。
更安全的做法是使用fgets
函数来读取字符串输入,并且要注意处理换行符。
④待补充…