第 1 章 排序
桶排序
示例一:
#include<stdio.h>
int main()
![请添加图片描述](https://img-blog.csdnimg.cn/direct/15572b1935fd4bacb16fa3ee2c219237.gif)
{
int a[11], i, j, t;
for (i = 0; i <= 10; i++)
{
a[i] = 0;//初始化为0
}
for (i = 1; i <= 5; i++)//循环读入5个数
{
scanf_s("%d", &t);//把每一个数读到变量t中
a[t]++;//进行计数
}
for (i = 0; i <= 10; i++)//依次判断a[0]~a[10]
{
for (j = 1; j <= a[i]; j++)//出现几次就打印几次
{
printf("%d ", i);
}
}
getchar(); getchar();
//这里的getchar();用来暂停程序,以便查看程序输出的内容
//也可以用system("pause");等来代替
return 0;
}
输入内容:
5 3 5 2 8
输出内容:
2 3 5 5 8
示例二:
#include<stdio.h>
int main()
{
int book[1001], i, j, t, n;
for (i = 0; i <= 1000; i++)
{
book[i] = 0;
}
scanf_s("%d", &n);//输入一个数n,表示接下来有n个数
for (i = 1; i <= n; i++)//循环读入n个数,并进行桶排序
{
scanf_s("%d", &t);//把每一个数读到变量t中
book[t]++;//进行计数,对编号为t的桶放一个小旗子
}
for (i = 1000; i >= 0; i--)//依次判断编号1000~0的桶
{
for (j = 1; j <= book[i]; j++)//出现了几次就将桶的编号打印几次
{
printf("%d ", i);
}
}
return 0;
}
输入内容:
10
8 100 50 22 15 6 1 1000 999 0
输出内容:
1000 999 100 50 22 15 8 6 1 0
冒泡排序
示例一:
#include<stdio.h>
int main()
{
int a[100],i,j,t,n;
scanf_s("%d", &n);//输入一个数n,表示接下来有n个数
for (i = 1; i <= n; i++)//循环读入n个数到数组a中
{
scanf_s("%d", &a[i]);
}
//冒泡排序的核心部分
for (i = 1; i <= n - 1; i++)//n个数排序,只用进行n-1趟
{
for (j = 1; j <= n - i; j++)
//从第1位开始比较直到最后一个尚未归位的数,想一想为什么到n-i就可以了
{
if (a[j] < a[j + 1])//比较大小并交换
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
for (i = 1; i <= n; i++)//输出结果
{
printf("%d ", a[i]);
}
return 0;
}
输入内容;
10
8 100 50 22 15 6 1 1000 999 0
输出内容:
1000 999 100 50 22 15 8 6 1 0
示例二:
#include<stdio.h>
struct student
{
char name[20];
int score;
};//这里创建了一个结构体用来存储姓名和分数
int main()
{
struct student a[100], t;
int i, j, n;
scanf_s("%d", &n);//输入一个数n
for (i = 1; i <= n; i++)//循环读入n个人名和分数
{
scanf_s("%s %d", a[i].name,&a[i].score);
}
//按分数从高到低排序
for (i = 1; i <= n - 1; i++)
{
for (j = 1; j <= n - i; j++)
{
if (a[j].score < a[j + 1].score)//对分数进行比较
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
for (i = 1; i <= n; i++)//输出人名
{
printf("%s\n", a[i].name);
}
return 0;
}
输入内容:
5
huhu 5
haha 3
xixi 5
hengheng 2
gaoshou 8
输出内容:
gaoshou
huhu
xixi
haha
hengheng
快速排序
示例一:
#include<stdio.h>
int a[101], n;//定义全局变量,这两个变量需要在子函数中使用
void quicksort(int left,int right)
{
int i, j, t, temp;
if (left > right)
{
return;
}
temp = a[left];//temp中存放的就是基准数
i = left;
j = right;
while (i != j)
{
//顺序很重要,要先从右往左找
while (a[j] >= temp && i < j)
{
j--;
}
//再从左往右找
while (a[i] <= temp && i < j)
{
i++;
}
//交换两个数在数组中的位置
if (i < j)//当哨兵i和哨兵j没有相遇时
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
//最终将基准数归位
a[left] = a[i];
a[i] = temp;
quicksort(left, i - 1);//继续处理左边的,这里是一个递归的过程
quicksort(i + 1, right);//继续处理右边的,这里是一个递归的过程
return;
}
int main()
{
int i, j;
//读入数据
scanf_s("%d", &n);
for (i = 1; i <= n; i++)
{
scanf_s("%d", &a[i]);
}
quicksort(1, n);//快速排序调用
//输出排序后的结果
for (i = 1; i <= n; i++)
{
printf("%d", a[i]);
}
return 0;
}
输入内容:
10
5 6 9 8 7 4 1 2 3 10
输出内容:
1 2 3 4 5 6 7 8 9 10
练习
题目:
每本书都有对应的 ISBN 码,子安要去买书,每一种书只买一本,在路上,他脑子里记的要去买的书有重复的,但他不知道,等到了书店之后,他发现:
①他要汇总当前一共要买几本书?
②他要“去重”(去除重复的)
③他要“排序”(将 ISBN 码从小到大排序)。
现在请你帮帮这个娃。
例如输入:
10
20 40 32 67 40 20 89 300 400 15
则输出:
8
15 20 32 40 67 89 300 400
方法一:桶排序
#include<stdio.h>
int main()
{
int a[1001], n, i, t;
for (i = 1; i <= 1000; i++)
{
a[i] = 0;//初始化
}
scanf_s("%d", &n);//读入n
for (i = 1; i <= n; i++)//循环读入n个图书的ISBN号
{
scanf_s("%d", &t);//把每一个ISBN号读到变量t中
a[t] = 1;//标记出现过的ISBN号
}
for (i = 1; i <= 1000; i++)//依次判断1~1000这1000个桶
{
if (a[i] == 1)//如果这个ISBN号出现过则打印出来
{
printf("%d ", i);
}
}
return 0;
}
方法二:冒泡排序
#include<stdio.h>
int main()
{
int a[101], n, i, j, t;
scanf_s("%d", &n);//读入n
for (i = 1; i <= n; i++)//循环读入n个图书的ISBN号
{
scanf_s("%d", &a[i]);
}
//开始冒泡排序
for (i = 1; i <= n - 1; i++)
{
for (j = 1; j <= n - i; j++)
{
if (a[j] > a[j + 1])
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
printf("%d", a[1]);//输出第1个数
for (i = 2; i <= n; i++)//从2循环到n
{
if (a[i] != a[i - 1])//如果当前这个数是第一次出现则输出
{
printf("%d ", a[i]);
}
}
return 0;
}
第 2 章 栈、队列、链表
队列
题目:
一串数“631758924”,首先将第 1 个数删除,紧接着将第 2 个数放到这串数的末尾,再将第 3 个数删除并将第 4 个数放到这串数的末尾,再将第 5 个数删除…直到剩下最后一个数,将最后一个数也删除。按照刚才删除的顺序,把这些删除的数连在一起。求后来的数字串是多少?
示例一:
#include<stdio.h>
int main()
{
int q[102] = { 0,6,3,1,7,5,8,9,2,4 }, head, tail;
//初始化队列
head = 1;
tail = 10;//队列中已经有 9 个元素了,tail指向队尾的后一个位置
while (head < tail)//当队列不为空的时候执行循环
{
//打印队首并将队首出队
printf("%d ", q[head]);
head++;
//先将新队首的数添加到队尾
q[tail] = q[head];
tail++;
//再将队首出队
head++;
}
return 0;
}
输出内容:
6 1 5 9 4 7 2 8 3
示例二:
#include<stdio.h>
struct queue
{
int data[100];//队列的主体,用来存储内容
int head;//队首
int tail;//队尾
};
int main()
{
struct queue q;
int i;
//初始化队列
q.head = 1;
q.tail = 1;
for (i = 1; i <= 9; i++)
{
//依次向队列插入 9 个数
scanf_s("%d", &q.data[q.tail]);
q.tail++;
}
while (q.head < q.tail)//当队列不为空的时候执行循环
{
//打印队首并将队首出队
printf("%d ", q.data[q.head]);
q.head++;
//先将新队首的数添加到队尾
q.data[q.tail] = q.data[q.head];
q.tail++;
//再将队首出队
q.head++;
}
return 0;
}
输入内容:
6 3 1 7 5 8 9 2 4
输出内容:
6 1 5 9 4 7 2 8 3
栈
题目:
判断是否为回文字符串
代码:
#include<stdio.h>
#include<string.h>
int main()
{
char a[101], s[101];
int i, len, mid, next, top;
gets(a);//读入一行字符串
len = strlen(a);//求字符串的长度
mid = len / 2 - 1;//求字符串的中点
top = 0;//栈的初始化
//将mid前的字符依次入栈
for (i = 0; i <= mid; i++)
{
s[++top] = a[i];
}
//判断字符串的长度是奇数还是偶数,并找出需要进行字符匹配的起始下标
if (len % 2 == 0)
{
next = mid + 1;
}
else
{
next = mid + 2;
}
//开始匹配
for (i = next; i <= len - 1; i++)
{
if (a[i] != s[top])
{
break;
}
top--;
}
//如果top的值为 0 ,则说明栈内所有字符都被一一匹配了
if (top == 0)
{
printf("YES");
}
else
{
printf("NO");
}
return 0;
}
示例一
例如输入:(有空格)
a b c c b a
则输出:
YES
示例二
例如输入:(后面的无空格)
a b c cba
则输出:
NO
示例三
例如输入:
abcdef
则输出:
NO
练习
题目:
纸牌游戏——小猫钓鱼
规则:
将一副扑克牌平均分成两份,每人拿一份。子安先拿出手中的第一张扑克牌放在桌上,然后小贤也拿出手上的第一张扑克牌,并放在子安刚刚打出的扑克牌上面,就像这样两人交替出牌。出牌时,如果某人打出的牌与桌上某张牌的牌面相同,即可将两张相同的牌及其中间所夹的牌全部收走,并以此放到自己手中牌的末尾。当任意一人手中的牌全部出完时,游戏结束,对手获胜。
条件:假如游戏开始时,子安手中有 6 张牌,顺序为 2 4 1 2 5 6 ,小贤手中也有 6 张牌,顺序为 3 1 3 5 6 4,最终谁会获胜呢?
代码:
#include<stdio.h>
struct queue
{
int data[1000];
int head;
int tail;
};
struct stack
{
int data[10];
int top;
};
int main()
{
struct queue q1, q2;
struct stack s;
int book[10];
int i, t;
//初始化队列
q1.head = 1; q1.tail = 1;
q2.head = 1; q2.tail = 1;
//初始化栈
s.top = 0;
//初始化用来标记的数组,用来标记哪些牌已经在桌上
for (i = 1; i <= 9; i++)
{
book[i]=0;
}
//依次向队列插入 6 个数
//子安手上的 6 张牌
for (i = 1; i <= 6; i++)
{
scanf_s("%d", &q1.data[q1.tail]);
q1.tail++;
}
//小贤手上的 6 张牌
for (i = 1; i <= 6; i++)
{
scanf_s("%d", &q2.data[q2.tail]);
q2.tail++;
}
while (q1.head < q1.tail && q2.head < q2.tail)//当队列不为空的时候执行循环
{
t = q1.data[q1.head];//子安出一张牌
//判断子安当前打出的牌是否能赢牌
if (book[t] == 0)//表明桌上没有牌面为 t 的牌
{
//子安此轮没有赢牌
q1.head++;//子安已经打出一张牌,所以要把打出的牌出队
s.top++;
s.data[s.top] = t;//再把打出的牌放到桌上,即入栈
book[t] = 1;//标记桌上现在已经有牌面为 t 的牌
}
else
{
//子安此轮可以赢牌
q1.head++;//子安已经打出一张牌,所以要把打出的牌出队
q1.data[q1.tail] = t;//紧接着把打出的牌放到手中牌的末尾
q1.tail++;
while (s.data[s.top] != t)//把桌上可以赢得的牌依次放到手中牌的末尾
{
book[s.data[s.top]] = 0;//取消标记
q1.data[q1.tail] = s.data[s.top];//依次放入队尾
q1.tail++;
s.top--;//栈中少了一张牌,所以栈顶要减 1
}
//收回桌上牌面为 t 的牌
book[s.data[s.top]] = 0;
q1.data[q1.tail] = s.data[s.top];
q1.tail++;
s.top--;
}
if (q1.head == q1.tail)//子安手中的牌如果已经打完,游戏结束
{
break;
}
t = q2.data[q2.head];//小贤出一张牌
//判断小贤当前打出的牌是否能赢牌
if (book[t] == 0)//表明桌上没有牌面为 t 的牌
{
//小贤此轮没有赢牌
q2.head++;//小贤已经打出一张牌,所以要把打出的牌出队
s.top++;
s.data[s.top] = t;//再把打出的牌放到桌上,即入栈
book[t] = 1;//标记桌上现在已经有牌面为 t 的牌
}
else
{
//小贤此轮可以赢牌
q2.head++;//小贤已经打出一张牌,所以要把打出的牌出队
q2.data[q2.tail] = t;//紧接着把打出的牌放到手中牌的末尾
q2.tail++;
while (s.data[s.top] != t)//把桌上可以赢得的牌依次放到手中牌的末尾
{
book[s.data[s.top]] = 0;//取消标记
q2.data[q2.tail] = s.data[s.top];//依次放入队尾
q2.tail++;
s.top--;
}
//收回桌上牌面为 t 的牌
book[s.data[s.top]] = 0;
q2.data[q2.tail] = s.data[s.top];
q2.tail++;
s.top--;
}
}
if (q2.head == q2.tail)
{
printf("子安赢了\n");
printf("子安当前手中的牌是:");
for (i = q1.head; i <= q1.tail - 1; i++)
{
printf(" %d ", q1.data[i]);
}
if (s.top > 0)//如果桌上有牌则依次输出桌上的牌
{
printf("\n桌上的牌是:");
for (i = 1; i <= s.top; i++)
{
printf(" %d ", s.data[i]);
}
}
else
{
printf("\n桌上已经没有牌了");
}
}
else
{
printf("小贤赢了\n");
printf("小贤当前手中的牌是:");
for (i = q2.head; i <= q2.tail - 1; i++)
{
printf(" %d ", q2.data[i]);
}
if (s.top > 0)//如果桌上有牌则依次输出桌上的牌
{
printf("\n桌上的牌是:");
for (i = 1; i <= s.top; i++)
{
printf(" %d ", s.data[i]);
}
}
else
{
printf("\n桌上已经没有牌了");
}
}
return 0;
}
例如输入:
2 4 1 2 5 6
3 1 3 5 6 4
输出结果:
小贤赢了
小贤当前手中的牌是 1 6 5 2 3 4 1
桌上的牌是 3 4 5 6 2
链表
代码:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *p;//定义一个指针 p
p=(int *)malloc(sizeof(int));//指针 p 获取动态分配的内存空间地址
*p=10;//向指针 p 所指向的内存空间中存入 10
printf("%d",*p);//输出指针 p 所指向的内存中的值
return 0;
}
输出结果:
10
需要注意,malloc函数的返回类型是void * 类型。void * 表示未确定类型的指针。在C和C++中,void * 类型可以强制转换为任何其他类型的指针。上面的代码中我们可以将其强制转化为整型指针,以便告诉计算机这里的4个字节作为一个整体用来存放整数。
模拟链表
定义:将链表通过数组的方式来实现(上一节是使用指针)
代码:
#include<stdio.h>
int main()
{
int data[101], right[101];
int i, n, t, len;
//读入已有的数
scanf_s("%d", &n);
for (i = 1; i <= n; i++)
{
scanf_s("%d", &data[i]);
}
len = n;
//初始化数组right
for (i = 1; i <= n; i++)
{
if (i != n)
{
right[i] = i + 1;
}
else
{
right[i] = 0;
}
}
//直接在数组data的末尾增加一个家
len++;
scanf_s("%d", &data[len]);
//从链表的头部开始遍历
t = 1;
while (t != 0)
{
if (data[right[t]] > data[len])
//如果当前结点下一个结点的值大于待插入数,将数插入到中间
{
right[len] = right[t];//新插入数的下一个结点标号等于当前结点的下一个结点编号
right[t] = len;//当前结点的下一个结点编号就是新插入数的编号
break;//插入完成跳出循环
}
t = right[t];
}
//输出链表中所有的数
while (t != 0)
{
printf("%d ", data[t]);
t = right[t];
}
getchar();
getchar();
return 0;
}
例如输入:
9
2 3 5 8 9 10 18 26 32
6
输出结果:
2 3 5 6 8 9 10 18 26 32
第 3 章 枚举
奥数
题目:
在 i 的上填入相同的数字,使等式成立。
代码:
for (i = 1; i <= 9; i++)
{
if ((i * 10 + 3) * 6528 == (30 + i) * 8256)
{
printf("%d", i);
}
}
题目:
a b c + d e f = x y z,将数字 1~9 分别填入 9 个字母的位置上,每个数字只能使用一次使得等式成立。
例如:173+286=459就是一个合理的组合,请问一共有多少种合理的组合呢?注意:173+286=459与286+173=459同一种组合。
示例一:(枚举法)
#include<stdio.h>
int main()
{
int a, b, c, d, e, f, g, h, i, total = 0;
for (a = 1; a <= 9; a++)//第 1 个数的百位
for (b = 1; b <= 9; b++)//第 1 个数的十位
for (c = 1; c <= 9; c++)//第 1 个数的个位
for (d = 1; d <= 9; d++)//第 2 个数的百位
for (e = 1; e <= 9; e++)//第 2 个数的十位
for (f = 1; f <= 9; f++)//第 2 个数的个位
for (g = 1; g <= 9; g++)//第 3 个数的百位
for (h = 1; h <= 9; h++)//第 3 个数的十位
for (i = 1; i <= 9; i++)//第 3 个数的个位
{
//接下来要判断每一位上的数互不相等
if (a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i
&& b != c && b != d && b != e && b != f && b != g && b != h && b != i
&& c != d && c != e && c != f && c != g && c != h && c != i
&& d != e && d != f && d != g && d != h && d != i
&& e != f && e != g && e != h && e != i
&& f != g && f != h && f != i
&& g != h && g != i
&& h != i
&& a * 100 + b * 10 + c + d * 100 + e * 10 + f == g * 100 = h * 10 + i)
{
total++;
printf("%d%d%d+%d%d%d=%d%d%d\n", a, b, c, d, e, f, g, h, i);
}
}
printf("total=%d", total / 2);//请想一想为什么要除以 2
getchar();
getchar();
return 0;
}
原因:因为173+286=459与286+173=459是同一种组合,因此我们在输出的时候需要将total除以2
示例二:(标记法)
#include<stdio.h>
int main()
{
int a[10], i, total = 0, book[10], sum;
//这里用a[1]至a[9]来代替刚才的a,b,c,d,e,f,g,h,i
for (a[1] = 1; a[1] <= 9; a[1]++)
for (a[2] = 1; a[2] <= 9; a[2]++)
for (a[3] = 1; a[3] <= 9; a[3]++)
for (a[4] = 1; a[4] <= 9; a[4]++)
for (a[5] = 1; a[5] <= 9; a[5]++)
for (a[6] = 1; a[6] <= 9; a[6]++)
for (a[7] = 1; a[7] <= 9; a[7]++)
for (a[8] = 1; a[8] <= 9; a[8]++)
for (a[9] = 1; a[9] <= 9; a[9]++)
{
for (i = 1; i <= 9; i++)//初始化book数组
{
book[i] = 0;
}
for (i = 1; i <= 9; i++)//如果某个数出现过就标记一下
{
book[a[i]] = 1;
}
//统计共出现了多少个不同的数
sum = 0;
for (i = 1; i <= 9; i++)
sum += book[i];
//如果正好出现了 9 个不同的数,并且满足等式条件,则输出
if (sum == 9 && a[1] * 100 + a[2] * 10 + a[3] + a[4] * 100 + a[5] * 10 + a[6] == a[7] * 100 + a[8] * 10 + a[9])
{
total++;
printf("%d%d%d+%d%d%d=%d%d%d\n", a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
}
}
getchar(); getchar();
return 0;
}
炸弹人
题目:
小贤最近爱玩炸弹人。游戏规则:用放置炸弹的方法来消灭敌人。须将画面上的敌人全部消灭后,并找到隐藏在墙里的暗门才能过关。
现在有一个特殊的关卡如下。你只有一枚炸弹,但是这枚炸弹威力超强(杀伤距离超长,可以消灭杀伤范围内所有的敌人)。请问在哪里放置炸弹才可以消灭最多的敌人呢?
我们先将这个地图模型化。墙用 # 表示。这里有两种墙,一种是可以被炸掉的,另外一种是不能被炸掉的。但是由于现在只有一枚炸弹,所以都用 # 表示,炸弹是不能穿墙的。敌人用 G表示,空地用 . 表示,当然炸弹只能放在空地上。
#############
#GG.GGG#GGG.#
###.#G#G#G#G#
#.......#..G#
#G#.###.#G#G#
#GG.GGG.#.GG#
#G#.#G#.#.###
##G...G.....#
#G#.#G###.#G#
#...G#GGG.GG#
#G#.#G#G#.#G#
#GG.GGG#G.GG#
#############
代码:
#include<stdio.h>
int main()
{
char a[20][21];//假设这里的地图大小不超过 20*20
int i, j, sum, map = 0, p, q, x, y, n, m;
//读入 n 和 m ,n 表示有多少行字符,m 表示每行有多少列
scanf_s("%d %d", &n, &m);
//读入 n 行字符
for (i = 0; i <= n; i++)
{
scanf_s("%s", a[i]);
}
//用两重循环枚举地图中的每一点
for (i = 0; i <= n - 1 ; i++)
{
for (j = 0; j <= m - 1; j++)
{
//首先判断这个点是不是平地,是平地才可以被放置炸弹
if (a[i][j] == '.')
{
sum = 0;//sum 用来计数(可以消灭的敌人数),所以需要初始化为 0
//将当前坐标 i , j 复制到两个新变量 x , y中,以便向上下左右四个方向分别统计可以消灭的敌人数
//向上统计可以消灭的敌人数
x = i; y = j;
while (a[x][y] != '#')//判断是不是墙,如果不是墙就继续
{
//如果当前点是敌人,则进行计数
if (a[x][y] == 'G')
{
sum++;
}
//x-- 的作用是继续向上统计
x--;
}
//向下统计可以消灭的敌人数
x = i; y = j;
while (a[x][y] != '#')
{
if (a[x][y] == 'G')
{
sum++;
}
//x++ 的作用是继续向下统计
x++;
}
//向左统计可以消灭的敌人数
x = i; y = j;
while (a[x][y] != '#')
{
if (a[x][y] == 'G')
{
sum++;
}
//y-- 的作用是继续向左统计
y--;
}
//向右统计可以消灭的敌人数
x = i; y = j;
while (a[x][y] != '#')
{
if (a[x][y] == 'G')
{
sum++;
}
//y++ 的作用是继续向右统计
y++;
}
//更新 map 的值
if (sum > map)
{
//如果当前点所能消灭的敌人总数大于 map ,则更新 map
map = sum;
//并用 p 和 q 记录当前点的坐标
p = i;
q = j;
}
}
}
}
printf("将炸弹放置在(%d,%d)处,最多可以消灭 %d 个敌人\n", p, q, map);
getchar(); getchar();
return 0;
}
例如输入:
13 13
#############
#GG.GGG#GGG.#
###.#G#G#G#G#
#.......#..G#
#G#.###.#G#G#
#GG.GGG.#.GG#
#G#.#G#.#.###
##G...G.....#
#G#.#G###.#G#
#...G#GGG.GG#
#G#.#G#G#.#G#
#GG.GGG#G.GG#
#############
输出结果:
将炸弹放置在(9,9)处,最多可以消灭 8 个敌人