本篇主要为自学《啊哈!算法》所记录的知识点或学习过程及补充。
一、排序算法
1.桶排序
例如,0-10的考试分数,每一个分数均有一个桶与之对应。将符合该桶分数的人扔进桶里,再把按题目要求按顺序把人倒出来,便完成了排序。
以本书例题讲解
【例】小哼班上的五个同学分别考了5,3,5,2,8分。请将分数进行从大到小排列。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a[11]; //0-10的分数段共11个数,故数组内元素个数为11,也就是桶的个数为11;数组元素值代
表处于这个分数段的人数
int i,j,n,t;
for(i=0;i<=10;i++)
{
a[i]=0; //人数全部初始化为0
}
for(i=0;i<=4;i++) //输入分数。一共5人,循环进行5次。
{
scanf("%d",&n);
a[n]++;
}
for(i=10;i>=0;i--) //开始进行排序
{
for(j=1;j<=a[i];j++) //数组元素值为几就打印几次
{
printf("%d ",i);
}
}
system("PAUSE");
return 0;
}
2.冒泡排序
泡泡浮起时,一层一层到水面上;冒泡排序同样,基本思想就是从第一个数开始与相邻的数层层比较,将其移动至正确的位置(通过与被比较数交换)
【例】输入n个整数,并对它们进行从小到大排序。
#include<stdio.h> //从小到大排序
#include<stdlib.h>
int main()
{
int i,j,n,t; //i,j是控制循环的条件;n是输入数据总数 ;t作为中间变量交换数据
int a[100]; //存放输入数据
for(i=0;i<=100;i++)
{
a[i]=0; //数组数据初始化
}
printf("请输入需要进行排序的数据个数:\n");
scanf("%d",&n);
printf("请输入数据:\n");
for(j=0;j<=n-1;j++)
{
scanf("%d",&a[j]);
}
for(i=0;i<=n-1;i++) //排序主体
{
for(j=0;j<=n-i-2;j++) //注意循环条件的控制,很容易出错
{
if(a[j]>a[j+1])
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
for(i=0;i<=n-1;i++)
{
printf("%d ",a[i]);
}
system("PAUSE");
return 0;
}
因为每排一次都要进行比较,所以该排序方法时间复杂度较高
冒泡排序的核心是双重循环嵌套。
第一层是选定被移动的数据,第二层是将被移动数据进行层层比较。
3.快速排序
与冒泡排序相似,快速排序的重要步骤之一也是数据交换,只不过是以选定的基准数为标准。
例如,要求一组数据从小到大排列。先随机选定一个数据作为基准数,再从该组数据两端开始层层筛查。每一轮外层循环过后,基准数左边的数应小于它,右边的数应大于它。
再以基准数所在位置为分界点,对左右两边的数据分别重复上述步骤。
【例】输入n个数,并对其从小到大进行排列。
#include<stdio.h>
#include<stdlib.h>
int n; //定义全局变量(子函数中需要使用)
int a[100];
void q(int l,int r) //由于需要对基准数两侧的数据再次重复操作,即调用函数自身,故要用到递归
{
int i,j,t,temp; //i,j控制循环次数;t交换数据;temp存放基准数;
if(l>r)
{
return; //return语句后的任何代码都不会再执行;
}
temp=a[l]; //选定最左边的数为基准数
i=l;
j=r;
while(i!=j) //i,j不相等时循环进行下去
{
while(a[j]>=temp&&i<j)
{
j--;
}
while(a[i]<=temp&&i<j)
{
i++;
}
if(i<j)
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
a[l]=a[i]; //将基准数的值赋值和循环停下的位置的值交换
a[i]=temp;
q(l,i-1); //递归对基准数两侧的数进行排序
q(i+1,r);
return;
}
int main()
{
int i,j;
for(i=0;i<100;i++) //数组值初始化为0
{
a[i]=0;
}
printf("请输入数据个数:\n");
scanf("%d",&n);
printf("请输入数据:\n");
for(j=0;j<=n-1;j++)
{
scanf("%d",&a[j]);
}
q(0,n-1);
for(i=0;i<=n-1;i++)
{
printf("%d ",a[i]);
}
system("PAUSE");
return 0;
}
快速排序之所以时间复杂度要低于冒泡排序,是因为它的交换是跳跃式的,不限于相邻两个数之间。交换距离大,交换次数少,所用时间少。
4.*插入排序(*后为补充内容)
1.从第一个元素开始,该元素可以认为已经被排序。
2.取下一个元素tem,从已排序的元素序列从后往前扫描。
3.如果该元素大于tem,则将该元素移到下一位。
4.重复步骤3,直到找到已排序元素中小于等于tem的元素。
5.tem插入到该元素的后面,如果已排序所有元素都大于tem,则将tem插入到下标为0的位置。
6.重复步骤2~5。
#include<stdio.h>
#include<stdlib.h>
int a[100];
void sort(int a[],int n)
{
int tmp;
int i,j;
for(i=1;i<n;i++)
{
tmp=a[i]; //tmp用于存放a[i]的值,便于后面交换用
for(j=i-1;j>=0&&a[j]>tmp;j--) //从i前一项开始,从后往前找比a[i]大的数
{
a[j+1]=a[j]; //将该元素后移
}
a[j+1]=tmp; //将a[i]放到正确的位置,该轮排序完成
}
return;
}
int main()
{
int i,n;
printf("Please input the lenth:\n");
scanf("%d",&n);
printf("Please input the progression:\n");
for(i=0;i<=n-1;i++)
{
scanf("%d",&a[i]);
}
sort(a,n);
for(i=0;i<n;i++)
{
printf("%d ",a[i]);
}
system("PAUSE");
return 0;
}
5.*选择排序
从数组中选出一个最大值、一个最小值,分别置于该组数据首尾;
从剩下的数据中继续找最值,放在首尾;
重复步骤直至全部排列完成
#include<stdio.h>
#include<stdlib.h>
int a[100];
int n;
void sort(int a[],int n)
{
int start=0;
int end=n-1;
int i,j,t;
while(start<end)
{
int min=start,max=end;
for(i=start;i<=end;i++)
{
if(a[i]<a[min])
{
min=i;
}
}
for(i=start;i<=end;i++)
{
if(a[i]>a[max])
{
max=i;
}
}
t=a[max];
a[max]=a[end];
a[end]=t;
if(min==end)
{
min=max;
}
t=a[min];
a[min]=a[start];
a[start]=t;
start++;
end--;
}
return;
}
int main()
{
int i,j;
printf("请输入数据个数:\n");
scanf("%d",&n);
printf("请输入数据:\n");
for(i=0;i<=n-1;i++)
{
scanf("%d",&a[i]);
}
sort(a,n);
for(j=0;j<n;j++)
{
printf("%d ",a[j]);
}
system("PAUSE");
return 0;
}
二、队列
一行人排队买东西,排在前面的人先买先走,后面的人只能从队尾开始排。
这就是队列的先进先出原则。
【例】有一串加密过的数字631758924,解密规则如下:将第一个数删除,再把第二个数移至这串数字末尾;然后再将新数列的第一个数字删除,将第二个数字移至末尾......直到最后一个数字被删除。被删除的数字按删除顺序排列就是正确的QQ号。
下面是我自己看完题目后写的代码,较为复杂。
#include<stdio.h>
#include<stdlib.h>
int n;
int a[100]; //数组用于存放QQ号
void queue(int start,int end)
{
int i,j,t;
for(i=0;i<=n-1;start+=2,end+=1)
{
a[end+1]=a[start+1];
i++;
}
return;
}
int main()
{
int j;
printf("请输入QQ号长度:\n");
scanf("%d",&n);
printf("请输入QQ号:\n");
for(j=0;j<=n-1;j++)
{
scanf("%d",&a[j]);
}
queue(0,n-1);
for(j=0;j<=n+3;j+=2)
{
printf("%d",a[j]);
}
printf("%d%d",a[n+2],a[n]);
system("PAUSE");
return 0;
}
作者思路不同之处在于边删除边输出,相比之下简化了许多,也不必像我自己写的需要推几遍循环条件
#include<stdio.h>
#include<stdlib.h>
struct queue
{
int data[100];
int head;
int tail;
};
int main()
{
struct queue q;
int i;
q.head=0;
q.tail=0;
for(i=0;i<=99;i++)
{
q.data[i]=0;
}
for(i=0;i<=8;i++)
{
scanf("%d",&q.data[i]);
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++;
}
system("PAUSE");
return 0;
}
三、栈
一桶罐装薯片,想要吃到底部的薯片就要先把上层的薯片拿出来
这是栈的后进先出原则
【例】编写程序判断字符串是否为回文字符串
思路是将该数组中间元素左边的元素入栈,再将右边元素与其一一匹配。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char str[100],a[50];
int mid,lens,next,result=1;
int i;
memset(str,0,100); //数组初始化
memset(a,0,50);
printf("请输入字符串:\n");
gets(str);
lens=strlen(str);
mid=(lens/2)-1; //mid前的字符全部入栈
for(i=0;i<=mid;i++)
{
a[i]=str[i];
}
if(lens%2==0) //当字符串长度为偶数
{
next=mid+1;
}
else
{
next=mid+2;
}
for(i=0;i<(lens/2);i++)
{
if(a[mid-i]!=str[next+i])
{
result=0;
break;
}
}
if(result==0)
{
printf("NO");
}
else if(result==1)
{
printf("YES");
}
system("PAUSE");
return 0;
}
四、链表与模拟链表
在一串从小到大排列的数中再插入一个数,使用链表这种数据结构会比数列方便许多。
链表的实现可以借助指针。
链表的每个结点存储两个数据——数字和下一个结点的地址,这点可以用结构体实现。
#include<stdio.h>
#include<stdlib.h>
struct node //建立一个结构体存放数据结点
{
int data; //data用于存放数据
struct node *next; //指针next用于存放下一个数据的地址(因为下一个数据类型是struct node,故指针类型也为struct node
};
int main()
{
struct node *head,*p,*q,*t;
int i,n,a;
printf("请输入数据个数:\n");
scanf("%d",&n);
head=NULL; //链表还未建立时头指针为空
printf("请输入数据:\n");
for(i=0;i<=n-1;i++)
{
scanf("%d",&a);
p=(struct node *)malloc(sizeof(struct node)); //为数据结点分配内存空间
p->data=a;
p->next=NULL;
if(head==NULL) //如果这是第一个创建的结点,则p指向该结点
{
head=p;
}
else //如果这不是第一个创建的结点,则上一个结点的后继指针指向该结点
{
q->next=p;
}
q=p; //在p指向下一个结点前,q指向当前结点
}
printf("请输入待插入的数:\n");
scanf("%d",&a);
t=head; //从头开始遍历链表
while(t!=NULL)
{
if(t->next==NULL||t->next->data>a)
{
p=(struct node *)malloc(sizeof(struct node));
p->data=a;
p->next=t->next;
t->next=p;
break;
}
t=t->next;
}
t=head;
while(t!=NULL)
{
printf("%d ",t->data);
t=t->next;
}
free(p); //释放malloc函数分配的内存空间,防止内存泄漏
p=NULL; //使用后该指针变量要重新指向NULL,防止悬空指针出现
system("PAUSE");
return 0;
}
1355

被折叠的 条评论
为什么被折叠?



