前言
《啊哈!算法》是一本适用于小白的C语言算法书,里面的内容较为基础,这篇内容是我自学算法的记录,有些代码是个人的理解,与书上内容有所出入,内容如有错误,欢迎大家指正。
一、桶排序
5个数排序(数字在0 ~10之间)
#include <stdio.h>
int main(void)
{
int a[11] = { 0 }; //初始化数组
int i, j, k;
for ( i = 0; i < 5; i++ )
{
scanf("%d", &k); //输入5个数
a[k]++; //k每出现一次,对应的a[k]也会加1
}
// for ( i = 0; i < 11; i++) //从小到大
for ( i = 10; i >= 0; i-- ) //从大到小
{
for ( j = 0; j < a[i]; j++)
{
printf("%d ", i); //a[i]是多少,就会打印多少次
}
}
return 0;
}
初始化的数组中,数组的每一个元素都为0
若输入的数据为5,3,2,8,5
则变为a[2]=1,a[3]=1,a[5]=2,a[8]=1,其他元素仍为0
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7]] | a[8] | a[9] | a[10] |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 | 2 | 0 | 0 | 1 | 0 | 0 |
输出则是按照数组下标进行输出
n个数排序(0~1000)(同理)
#include <stdio.h>
int main(void)
{
int book[1001] = { 0 }; //初始化数组
int i, j ,k, n;
printf("Enter the total number: \n");
scanf("%d", &n); //输入n的值
for ( i = 0; i < n; i++)
{
printf("%d: ", i + 1);
scanf("%d", &k);
book[k]++;
}
//从大到小输出数字,并且数字出现几次就输出几次
for ( i = 1000; i >= 0; i--)
{
for ( j = 0; j < book[i]; j++)
{
printf("%d ", i);
}
}
return 0;
}
二、冒泡排序
#include <stdio.h>
int main(void)
{
int a[100] = { 0 };
int i, j, t, n;
scanf("%d", &n); //确定输入的数字个数
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]); //依次输入数字
}
for (i = 1; i < n; i++) // i的大小是从1到n-1,循环n-1次
{
for (j = 0; j < n - i; j++) //j是从0到n-i-1,循环n-i次
{
if (a[j] > a[j + 1]) //如果数组元素a[j]>a[k+1],就交换两个元素的值
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
for (i = 0; i < n; i++) //输出数组中所有元素
{
printf("%d ", a[i]);
}
return 0;
}
示例:a[5] = {12, 35, 99, 18, 76},
i | j | a[j] | a[j+1] |
1 | 0 | 12 | 35 |
1 | 1 | 35 | 99 |
1 | 2 | 99 | 18 |
1 | 2 | 18 | 99 |
1 | 3 | 99 | 76 |
1 | 3 | 76 | 99 |
j 的值从0~3,因为 i 的值为 1 ,j < n - i , 即 j < 4, 此时已经可以将最大值排到最后
此时,a[5]={12,35,18,76,99};
i | j | a[j] | a[j+1] |
2 | 0 | 12 | 35 |
2 | 1 | 35 | 18 |
2 | 1 | 18 | 35 |
2 | 2 | 35 | 76 |
同理,可将第二大的值排到倒数第二个位置,依此类推,可得结果
此时,a[5] = {12, 18, 35, 76, 99},该示例的排序已经完成,但循环仍要进行,直到 j 只能为0
name score
#include <stdio.h>
struct student
{
char name[20];
int score;
};
int main(void)
{
struct student a[100], t; //定义a[100]和t为结构体
int i, j, n;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%s %d", &a[i].name, &a[i].score);
}
for (i = 1; i < n; i++)
{
for (j = 0; 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 = 0; i < n; i++)
{
printf("%s\n", a[i].name); //输出以score排序后的name
}
return 0;
}
三、快速排序
#include <stdio.h>
int a[100], n;
void quicksort(int left, int right)
{
int i, j, t, temp;
if (left > right)
{
return;
}
//temp中存放基准数,若基准数在右侧,左侧先动
temp = a[right];//右侧(b)
i = left;
j = right;
while (i != j)
{
//从左往右(b)
while (a[i] <= temp && i < j)
{
i++;
}
while (a[j] >= temp && i < j)
{
j--;
}
if (i < j) //在 i 和 j 未相遇时,交换a[i]和a[j]的值
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
//在 i 和 j 相遇时,交换基准数与相遇位置的数值
a[right] = a[i]; //右侧(b)
a[i] = temp;
quicksort(left, i - 1); //递归
quicksort(i + 1, right);
}
int main(void)
{
int i, j, t;
scanf("%d", &n);
for (i = 0; i< n; i++)
{
scanf("%d", &a[i]);
}
quicksort(0, n - 1);
for (i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
return 0;
}
a[10]={6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
设左侧移动的为i, 右侧的为j
基准数为8,i 先动,直到找到比8大的数,即a[4];
然后 j 再动,直到找到比8小的数,即a[7]
交换a[4]与a[7]的值
a[10] = {6, 1, 2, 7, 5, 3, 4, 9, 10, 8};
i 移动到a[7], 与 j 重合,
此时, 将基准数与a[7] 的值互换,
a[10] = {6, 1, 2, 7, 5, 3, 4, 8, 10, 9};
现在,基准数左侧的数都小于基准数,右侧的都大于,也就是基准数已经到了排序后自己该在的位置。
再用同样的方法分别计算基准数两侧的数组,就可以完成数组的排序。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] |
6 | 1 | 2 | 7 | 9 | 3 | 4 | 5 | 10 | 8 |
6 | 1 | 2 | 7 | 5 | 3 | 4 | 9 | 10 | 8 |
6 | 1 | 2 | 7 | 5 | 3 | 4 | 8 | 10 | 9 |
注意:基准数在右端时,先移动左端,是为了保证基准数可以交换到对应的位置,如上表所示,在 i 和 j 相遇时, 右侧除了,还有一个比8大的数10,将8与 i , j 相遇位置的值9交换,就可以保证8右侧有两个大于8的数字,即保证了8已经到了正确的位置
同理可得,基准数在左端时,先移动右端
#include <stdio.h>
int a[100], n;
void quicksort(int left, int right)
{
int i, j, t, temp;
if (left > right)
{
return;
}
//temp中存放基准数,若基准数在左侧,右侧先动;在右侧,左侧先动
temp = a[left];//左侧(a)
// temp = a[right];//右侧(b)
i = left;
j = right;
while (i != j)
{
// //从右往左(a)
while (a[j] >= temp && i < j) // a[j] >= temp
{
j--;
}
while (a[i] <= temp && i < j) // a[i] <= temp
{
i++;
}
//从左往右(b)
// while (a[i] <= temp && i < j)
// {
// i++;
// }
// while (a[j] >= temp && i < j)
// {
// j--;
// }
if (i < j) //在 i 和 j 未相遇时,交换a[i]和a[j]的值
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
//在 i 和 j 相遇时,交换基准数与相遇位置的数值
a[left] = a[i]; //左侧(a)
// a[right] = a[i]; //右侧(b)
a[i] = temp;
quicksort(left, i - 1); //递归
quicksort(i + 1, right);
}
int main(void)
{
int i, j, t;
scanf("%d", &n);
for (i = 0; i< n; i++)
{
scanf("%d", &a[i]);
}
quicksort(0, n - 1);
for (i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
return 0;
}
四、排序练习
输入一组数字,进行排序和重组
1.先排序,再去重(桶排序)
#include <stdio.h>
int main(void)
{
int a[1001] = { 0 }; //初始化为0
int n, i, t;
scanf("%d",&n);
for (i = 0; i < n; i++)
{
scanf("%d", &t);
a[t] = 1; //标记出现过的编号
}
for (i = 0; i < 1000; i++)
{
if (a[i] == 1) //判断是否被标记
{
printf("%d ", i);
}
}
return 0;
}
2.先去重,在排序(冒泡排序/快速排序)
#include <stdio.h>
int main(void)
{
int a[100] = { 0 };
int n, i, j, t;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
for (i = 1; i < n; i++) //冒泡排序
{
for (j = 0; 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[0]); //先打印a[0]
for (i = 1; i < n ; i++)
{
if (a[i] != a[i - 1]) //判断从a[1]开始的代码是否与前一项的值相同
{
printf("%d ", a[i]);
}
}
return 0;
}
//快速排序
#include <stdio.h>
int a[100], n;
void quicksort(int left, int right)
{
int i, j, t, temp;
if (left > right)
{
return;
}
temp = a[right];
i = left;
j = right;
while (i != j)
{
while (a[i] <= temp && i < j)
{
i++;
}
while (a[j] >= temp && i < j)
{
j--;
}
if (i < j)
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
a[right] = a[i];
a[i] = temp;
quicksort(left, i - 1);
quicksort(i + 1, right);
}
int main(void)
{
int i, j, t;
scanf("%d", &n);
for (i = 0; i< n; i++)
{
scanf("%d", &a[i]);
}
quicksort(0, n - 1);
printf("%d ", a[0]);
for (i = 1; i < n ; i++)
{
if (a[i] != a[i - 1])
{
printf("%d ", a[i]);
}
}
return 0;
}