分治与递归,大体思想就是将整个问题,分解成2个(一般都是)小问题,再对这2个小问题进行同样的策略,1变2,2变4...最终达到肉眼能解决的地步,将这些子子子子问题逐级往上合并,直到最顶端,回到最初的问题,得到解答。
分治与递归适合解决的经典问题有:merge sort(归并排序)、quick sort(快速排序)、二叉树问题,下面就这几个问题进行说明。
归并排序:数组a[1-n],将其划分为两段,1-n/2和n/2+1-n,似乎变得容易了,继续如此划分下去,直到只有一个数,然后往上合并,合并时采用插入排序,代码如下:
//将arr数组中的first-last这段数据排序放到temp中,最后再复制回arr中
void Merge(int *arr,int first,int mid,int last,int *temp)
{
int i = first,j = mid+1,index = first;
while(i<=mid && j<=last)
{
if(*(arr+i) < *(arr+j))
*(temp+index++)=*(arr+i++);
else
*(temp+index++)=*(arr+j++);
}
while(i<=mid)
*(temp+index++)=*(arr+i++);
while(j<=last)
*(temp+index++)=*(arr+j++);
for(i=first;i<=last;++i)//复制回arr中
*(arr+i) = *(temp+i);
}
void mergeSort(int arr[],int temp[],int first, int last)
{
if(first < last)
{
int mid = (first + last)>>1;
mergeSort(arr,temp,first,mid);//分治
mergeSort(arr,temp,mid+1,last);//分治
Merge(arr,first,mid,last,temp);//合并
}
}
快速排序(quick sort),思路和归并排序是一致的,只是分的方式不一样,归并排序分的方式是平分,任何时候都是一半一半,再对2个一半进行相同的操作。而快速排序不同点在于,他随机选择一个数,并将这个数插到数组的合适位置,即使得位于该数左边的都比他小,位于右边的都比他大,但是所有左边的数并没有有序,然后再对左右两边的剩下的子数组进行快速排序。
int adjustArr(int *s, int first, int last)
{
int temp = s[first];
int i = first,j = last;
while(i<j)
{
while(i<j && s[j] >= temp)//从后往前找,找到第一个小于temp的
j--;
if(i < j)
s[i++] = s[j];
while(i<j && s[i]<temp)//从前往后找,找到第一个大于temp的
i++;
if(i<j)
s[j--] = s[i];//交换上面找到的2个元素,继续循环
}
s[i] = temp;
return i;
}
void quickSort(int *arr, int first, int last)
{
if(first < last)
{
int mid = adjustArr(arr,first,last);
quickSort(arr,first,mid);
quickSort(arr,mid+1,last);
}
}