您的位置:首页 > 其它

分治法-基于分之策略的归并排序和快速排序

2017-05-18 09:23 761 查看
分治法
分治法在算法结构上是递归的.为了解决一个给定的问题,算法一次或多次递归调用其本身以解决紧密相关的若干子问题.
思想:
将原问题分解为几个规模较小但类似有原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原有问题的解.
分治法每层递归的步骤:
1.分解原问题为若干子问题,这些子问题是原问题的规模较小的实例.
2.解决这些子问题,递归的求解各子问题.如果子问题的规模足够小,则直接求解.
3.合并这些子问题的解构成原问题的解.

1.基于分之策略的归并排序:

不断的将一个数组从中间分为两部分,直到其成为单个数字,在对其进行归并,最后得到一个有序数组.

public class MergeSort {
public static void main(String[] args) {
int[] arr = { 21, 34, 46, 243, 54654, 3, 43, 564, 253, 54, 345, 0, 654,
343, 23, 89 };
merge_sort(arr, 0, arr.length - 1);
for (int cnt : arr) {
System.out.print(cnt + " ");
}
}
//递归分解
private static void merge_sort(int[] arr, int low, int high) {
if (low < high) {
int center = (low + high) / 2;
merge_sort(arr, low, center);
merge_sort(arr, center + 1, high);
merge(arr, low, center, high);
}
}
// 拆分数组
private static void merge(int[] arr, int low, int center, int high) {
int lowLength = center - low + 1; // 计算上半部分数组的长度
int highLength = high - center; // 计算下半部分数组的长度
int[] lowArray = new int[lowLength + 1];// 设置数组长度+1,将多余的一个设置为哨兵,便于归并排序
int[] highArray = new int[highLength + 1];
for (int i = low; i <= center; i++) {// 将上半部分数组赋值个给lowArray
lowArray[i - low] = arr[i];
}
for (int i = center + 1; i <= high; i++) {// 将下半部分数组赋值个给highArray
highArray[i - center - 1] = arr[i];
}
mergeArray(arr, lowArray, highArray, low, high);
}
// 合并数组
private static void mergeArray(int[] arr, int[] lowArray, int[] highArray,
int low, int high) {
lowArray[lowArray.length - 1] = Integer.MAX_VALUE;// 哨兵
highArray[highArray.length - 1] = Integer.MAX_VALUE;
int indexLow = 0;// 表示lowArray数组下标
int indexHigh = 0;// 表示highArray数组下标
for (int i = low; i <= high; i++) {// 归并
if (lowArray[indexLow] <= highArray[indexHigh]) {
arr[i] = lowArray[indexLow];
indexLow++;
} else {
arr[i] = highArray[indexHigh];
indexHigh++;
}
}
}
}


2.基与分之策略的快速排序
快速排序的思想:

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,

  然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列.

  步骤:

  设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,

  然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。快速排序不是

  一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

  一趟快速排序的步骤是:

  1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;

  2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];

  3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;

  4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;

  5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,

  4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,

  进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

public class PartitionQuickSort {
public static void main(String[] args) {
int[] arr = {1,234,345,2,2422,42,1,432,4232,5,4,64,756,6,8,6,86,46,56};
quickSort(arr,0,arr.length-1);
for(int i:arr){
System.out.print(i + " ");
}
}
//递归调用
private static void quickSort(int[] arr, int low, int high) {
if(low < high){
int p = getPoint(arr,low,high);
quickSort(arr, p+1, high);
quickSort(arr, low, p-1);
}
}
//获取哨兵在这个有序队列中的位置
private static int getPoint(int[] arr, int low, int high) {
int var = arr[low];
while(low < high){
while(low < high && arr[high] >= var){
high--;
}
swap(arr,low,high);
while(low < high && arr[low] < var){
low ++;
}
swap(arr,low,high);
}
return low;
}
//交换数组中两个数
private static void swap(int[] arr, int low, int high) {
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
}

3.数组中的最大连续子序列
对于一个数组,求它最大的连续子序列.例如在数组{1,5,-3,4,-5}这个数组中最大连续子序列为{1,5,-3,4}和为7.

对于一个数组,它的最大连续子序列可分为三种情况:在上半部分,在下半部分,跨过中间位置包含上半部分和下半部分.

/*
* 求数组中的最大连续子序列
*/
//定义一个类用来存储结果
class Result {
int sum;// 记录字序列的和
int low;// 记录子序列的下界
int high;// 记录子序列的上界
}

public class FindMaxSubarray {
// 包含中间位置的最大子序列
public static Result findMaxCrossSubarray(int[] arr, int low, int mid,
int high) {
Result re = new Result();
int leftsum = Integer.MIN_VALUE; // 设定最小值
int maxleft = 0;
int maxright = 0;
int sum = 0;
for (int i = mid; i >= low; i--) {// 从中间位置开始往前求最大序列
sum = sum + arr[i];
if (sum > leftsum) {
leftsum = sum;
maxleft = i;
}
}
int rightsum = Integer.MIN_VALUE; // 设定最小值
sum = 0;
for (int i = mid + 1; i <= high; i++) {// 从中间位置开始往后求最大序列
sum = sum + arr[i];
if (sum > rightsum) {
rightsum = sum;
maxright = i;
}
}
re.low = maxleft;
re.high = maxright;
re.sum = leftsum + rightsum;// 得到包含中间位置的最大子序列
return re;
}

public static Result findMaxSubarray(int[] arr, int low, int high) {
if (high == low) {// 递归出口
Result re = new Result();
re.low = low;
re.high = high;
re.sum = arr[low];
return re;
} else {
int mid = (low + high) / 2;
Result ResultLeft = findMaxSubarray(arr, low, mid);
Result ResultRight = findMaxSubarray(arr, mid + 1, high);
Result ResultCross = findMaxCrossSubarray(arr, low, mid, high);
if (ResultLeft.sum >= ResultRight.sum
&& ResultLeft.sum >= ResultCross.sum) {
return ResultLeft;// 返回最大的值所在的对象
} else if (ResultRight.sum >= ResultLeft.sum
&& ResultRight.sum >= ResultCross.sum) {
return ResultRight;// 返回最大的值所在的对象
} else
return ResultCross;// 返回最大的值所在的对象
}
}

public static void main(String[] args) {
int[] arr = { 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22,
15, -4, 7 };
// int[] arr ={-1,-2,-3,-4};
Result re = new Result();
re = findMaxSubarray(arr, 0, arr.length - 1);
System.out.println(re.sum + " " + re.low + " " + re.high);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息