面试笔试算法系列之分治法
分治法:顾名思义乃是分而治之的意思,将一个大规模的问题分成若干个小规模的子问题。其中每个子问题是相对独立的,小规模情况下容易解。
问题举例:
1.快速排序
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
面试中经常会要求写出快速排序的代码,一段简洁代码如下:
void Qsort(int a[], int low, int high) { if(low >= high) { return; } int first = low; int last = high; int key = a[first];/*用字表的第一个记录作为枢轴*/ while(first < last) { while(first < last && a[last] >= key) { --last; } a[first] = a[last];/*将比第一个小的移到低端*/ while(first < last && a[first] <= key) { ++first; } a[last] = a[first]; /*将比第一个大的移到高端*/ } a[first] = key;/*枢轴记录到位*/ Qsort(a, low, first-1); Qsort(a, first+1, high); }
我们举例 57, 68, 59, 52, 72, 28, 96, 33, 24,上述代码的过程主要是设置一个first指针和last指针,分别指向比key小的值和比key大的值,上述代码的过程简洁,但是代码细节过强。
使用Partition函数进行分治快排
int Partition(int* data,int length,int start,int end){ if(data==NULL||length<0||start<0||end>length-1) exit(0); //数组中随机选取一个数字,将数组分为两组,左边的比该数字小,右边的比该数字大 int index = Random(start,end); //放到最后的位置,以便和所有的数进行比较 Swap(&data[end],&data[index]); //定义一个small,指向当前比选中数(现在的位置是data[end])小的数 int small = start-1; for(int index = start;index < end;index++){ if(data[index]<data[end]){ //找到一个比选中数小的数 small++; //判断当前位置index和small所指的位置是否相等 if(index!=small){ Swap(&data[index],&data[small]); } } } //定位到新的位置,把选中的数放回去 small++; Swap(&data[small],&data[end]); return small; }
这个使用Partition函数的快速排序过程如下图所示,
2.找出数组中出现次数大于一半的数字
该问题可以使用一个Partition分治函数去解决,寻找对应中间位置的数字,即为所需数字,源码如下
#include <iostream> #include <stdlib.h> #include <time.h> using namespace std; void Swap(int *a,int *b){ int temp=*a; *a = *b; *b=temp; } int Random(int start,int end){ srand((unsigned)time(NULL)); return start+(int)(rand()%(end-start+1)); } int Partition(int* data,int length,int start,int end){ if(data==NULL||length<0||start<0||end>length-1) exit(0); //数组中随机选取一个数字,将数组分为两组,左边的比该数字小,右边的比该数字大 int index = Random(start,end); //放到最后的位置,以便和所有的数进行比较 Swap(&data[end],&data[index]); //定义一个small,指向当前比选中数(现在的位置是data[end])小的数 int small = start-1; for(int index = start;index < end;index++){ if(data[index]<data[end]){ //找到一个比选中数小的数 small++; //判断当前位置index和small所指的位置是否相等 if(index!=small){ Swap(&data[index],&data[small]); } } } //定位到新的位置,把选中的数放回去 small++; Swap(&data[small],&data[end]); return small; } void QuickSort(int data[],int length,int start,int end ){ if(start==end) return; int index = Partition(data,length,start,end); if(index>start){ QuickSort(data,length,start,index-1); } if(index<end){ QuickSort(data,length,index+1,end); } } //寻找数组中出现次数超过一半的数字 int findMoreThanHalf(int* data,int length,int start,int end){ int middle = length/2; int index = Partition(data,length,start,end); while(index!=middle){ if(index>middle){ end = index-1; index = Partition(data,length,start,end); }else{ start = index+1; index = Partition(data,length,start,end); } } return data[index]; } int main(){ srand((unsigned)(time(NULL))); int length = Random(1,20); int data[length]; int start =1; int end =1000; for(int i=0;i<length;i++){ data[i]=start+(int)(rand()%(end-start+1)); } int data[10] ={12,34,54,54,67,54,2,54,54,54}; int length=10; for(int i=0;i<length;i++){ cout<<data[i]<<" "; } printf("\n"); cout<<"中位数是"<<findMoreThanHalf(data,length,0,length-1)<<endl; QuickSort(data,length,0,length-1); for(int i=0;i<length;i++){ cout<<data[i]<<" "; } return 0; }