第四章快排

 

第四章

 

 

 

 

开头主要介绍了分治,分治的精髓是不断的缩小规模,当最小的规模可以解决,并且

小的规模能帮助大的解决时候,算法就已经完成了,这个思路特别像高中学的

数学归纳法的证明过程,只要过要倒着想。

分治并非可用于解决问题的算法,而是一种解决问题的思路。

例子:

递归方法解决数组的和。

首先要想如果缩小规模,如果每次只算当前数量规模减一的和就好了。

那基线也呼之欲出,当只有一个就是他本身啊。

 

代码如下:

#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<stdio.h>
#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);
 
}*/
void sort(int a[],int low,int 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;
 } 
posted @ 2020-10-24 01:09  罪梦者  阅读(89)  评论(0)    收藏  举报