第四章快排
第四章
开头主要介绍了分治,分治的精髓是不断的缩小规模,当最小的规模可以解决,并且
小的规模能帮助大的解决时候,算法就已经完成了,这个思路特别像高中学的
数学归纳法的证明过程,只要过要倒着想。
分治并非可用于解决问题的算法,而是一种解决问题的思路。
例子:
递归方法解决数组的和。
首先要想如果缩小规模,如果每次只算当前数量规模减一的和就好了。
那基线也呼之欲出,当只有一个就是他本身啊。
代码如下:
#include<stdio.h>
int sum(int a[],int n)
{
if(n==1)
return a[0];
else
return a[0]+sum(a+1,n-1);
}
int main()
{
int a[]={0,1,2,3,4,5,6,7};
printf("%d",sum(a,sizeof(a)/sizeof(a[0])));
return 0;
}
编写涉及数组的递归函数是,基线条件通常是数组为空,或者只包含一个元素,
陷入困境时请检查基线条件是不是这样
练习:1
编写一个递归函数来计算列表包含的元素数。
由于这本书主要的语言是python写的,换成c语言的话,我认为只有字符串数组求这个还有点意义。
所以我写了递归字符串中字符的个数,相当于strlen函数功能
代码如下:
#include<stdio.h>
int str_len(char a[])
{
if(a[0]==0)
return 0;
else
return 1+str_len(a+1);
}
int main()
{
char s[100];
while(scanf("%s",s))
{
printf("%d\n",str_len(s));
}
return 0;
}
练习2:
找出列表中最大的数字
每次去缩小规模一直到最后只有一个的时候最大值就是本身,递归条件就是让a[0]和下面的比哪个大返回哪个
代码如下:
#include<stdio.h>
int max(int a[],int n)
{
int temp;
if(n==1)
return a[0];
else
temp=max(a+1,n-1);
return a[0]>temp?a[0]:temp;
}
int main()
{
int a[]={1,2,6,10,7,9,61,4};
printf("%d\n",max(a,sizeof(a)/sizeof(a[0])));
return 0;
}
练习3:
用递归的方式写出二分查找.
#include<stdio.h>
int erfen(int a[],int x,int low,int high)
{
if(low>high)
return -1;
int mid=(low+high)/2;
if(a[mid]>x)
{
return erfen(a,x,low,mid-1);
}
else
if(a[mid]==x)
return mid;
else
return erfen(a,x,mid+1,high);
}
int main()
{
int a[100]={1,2,3,4,5,6,7,8,9,10};
int n;
while(scanf("%d",&n))
{
printf("%d\n",erfen(a,n,0,9));
}
}
快排
快速排序也是一种常用的排序方法,比选择排序快的多,例如c语言标准库的qsort函数实现的方法就是快排.
快排也是使用了分治
快排一样也是不断的缩小问题的规模,那问题就来了 第一个问题缩小到什么时候,当然是只有一个或者空的时候就不用排了
第二个问题根据什么来缩小规模呢,就是划分函数,将一个数作为基准值来划分。基准值左边的都小于它右边的都大于它,
形成 {小于基准值的分数组}+基准值+{大于基准值的分数组},只要左边和右边是有序的这个排序就完成了,那怎么让左边和右边
继续有序呢,你看这不已经降低规模递归条件已经满足了,继续递归,递归到只有一个或者空就不排了。
c代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void f(int a[],int n) //这个是一个测试 函数测试排序是否出错的
{
int i;
int b=a[0];
for(i=1;i<n;i++)
{
if(a[i]<b)
{
printf("*************************error********************\n\n\n");
break;
}
b=a[i];
}
}
void sort(int a[],int low,int high) // 未改进型最原始快排
{
if(low>=high)
return ;
int temp=a[low];
int temp_low=low,temp_high=high;
while(low<high)
{
while(low<high&&a[high]>temp) high--;
if(low<high)
{
a[low]=a[high];
low++;
}
while(low<high&&a[low]<temp) low++;
if(low<high)
{
a[high]=a[low];
high--;
}
}
a[low]=temp;
sort(a,temp_low,low-1);
sort(a,low+1,temp_high);
}
void output(int a[],int n) //输出数组元素
{
int i;
for(i=0;i<n;i++)
printf("%4d",a[i]);
printf("\n");
}
int main()
{
const int n=10;
int a[10];
int i;
int num=10;
srand(time(NULL));
while(num--)
{
for(i=0;i<n;i++)
{
a[i]=rand()%100; //随机数组元素值
}
printf("start:");
output(a,n);
printf("end :");
sort(a,0,n-1);
f(a,n);
output(a,n);
}
return 0;
}
仔细观察代码还有想一下什么时候快排最差呢,当然是原来的序列就有序,因为原来排好序的你只能一次为一个排好。
举例,1,2,3,4,5,6,7 以a[0]做哨兵时只会得到【】和1和2,3,4,5,6,7序列这样的时候你会再次排n
次第一次是n次,第二次是n-1.。。。。直到1,等差数列前n项合,最后得到o(n2)的排序,最差的情况了。。
那怎么规避这种情形呢,我想到要从哨兵选取,不能是这个序列中最小的(假设是递增排序),还要考虑效率,三个数当中
取中间那个数,哪三个呢当然是直接能找到的,序列的 前,后,中,第二优化点,当数列规模很小的时候考虑,可以用
别的在小数据中排序快的算法,我选择了插入算法,当数据个数小于15的时候。
代码如下:
#include<stdlib.h>
#include<time.h>
void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
void f(int a[],int n)
{
int i;
int b=a[0];
for(i=1;i<n;i++)
{
if(a[i]<b)
{
printf("*************************error********************\n\n\n");
break;
}
b=a[i];
}
}
void insert_sort(int a[],int n)
{
int i,j;
int temp;
for(i=1;i<n;i++)
{
temp=a[i];
for(j=i-1;j>=0&&temp<a[j];j--)
{
a[j+1]=a[j];
}
a[j+1]=temp;
}
}
/*void sort(int a[],int low,int high) //原始版快排
{
if(low>=high)
return ;
int temp=a[low];
int temp_low=low,temp_high=high;
while(low<high)
{
while(low<high&&a[high]>temp) high--;
if(low<high)
{
a[low]=a[high];
low++;
}
while(low<high&&a[low]<temp) low++;
if(low<high)
{
a[high]=a[low];
high--;
}
}
a[low]=temp;
sort(a,temp_low,low-1);
sort(a,low+1,temp_high);
}*/
{
if(high-low<15)
{
insert_sort(a+low,high-low+1);
return ;
}
int temp_low=low,temp_high=high;
int mid=(low+high)/2;
if(a[mid]>a[low]) //前中后三数取中值保证a[low]数值在中间
{
swap(a+mid,a+low);
}
if(a[mid]>a[high])
{
swap(a+mid,a+high);
}
if(a[low]>a[high])
{
swap(a+low,a+high);
}
int temp=a[low];
while(low<high)
{
while(low<high&&a[high]>temp) high--;
if(low<high)
{
a[low]=a[high];
low++;
}
while(low<high&&a[low]<temp) low++;
if(low<high)
{
a[high]=a[low];
high--;
}
}
a[low]=temp;
sort(a,temp_low,low-1);
sort(a,low+1,temp_high);
}
void output(int a[],int n)
{
int i;
for(i=0;i<n;i++)
printf("%4d",a[i]);
printf("\n");
}
int main()
{
const int n=10;
int a[10];
int i;
int num=10;
srand(time(NULL));
while(num--)
{
for(i=0;i<n;i++)
{
a[i]=rand()%100;
}
printf("start:");
output(a,n);
printf("end :");
sort(a,0,n-1);
f(a,n);
output(a,n);
}
return 0;
}