算法世界是一个增加知识以及增加思想的地方。深入学习算法,可以从底层夯实自己的编程能力和解决问题的能力。今天来谈一谈经典算法之一——分治法。
分治的核心思想:将一个大问题分割为一些小问题,并且递归的解决后,再利用这些小问题的解合并成原来的大问题的解。其中怎样分割是最重要的核心。
伪代码模板:
function divideAndConquer(参数列表){
if(分割终止条件){
return
}
分割操作(比如中间分割)
const left
const right
//递归调用并且返回合并结果
return merge(divideAndConquer(left),divideAndConquer(right))
}
//辅助合并函数
function merge(简单参数列表){
具体操作
返回结果
}
1.求整型数组中的最大(最小)数字
思路:将数组分割为两份,找出这两份数字的最大值,取一个最大值即可,对于分好的两份数字,递归继续分来求出每份的最大值。
上代码:
const arr = [1,3,2,7,4,6,8,100]
function max(arr){
if(arr.length == 1)return arr[0]
if(arr.length == 2)return Math.max(arr[0],arr[1])
let mid = arr.length / 2 | 0,
left = arr.slice(0,mid),
right = arr.slice(mid)
return Math.max(max(left),max(right))
}
2.快速排序和归并排序
当然,用快速排序的时间复杂度是不确定的。要想让快排发挥最大的作用,选择一个分割的值是非常重要的。快排的核心思想就是将数组按照一个标准数分为两类,左边小,右边大,然后将这两类继续分割,可以想见,最后分割的情景就是从左到右是一个分隔的有序数组。合并即可。归并也是分治的一种体现,将数组无差别的从中间分割,直至每个到单一个元素(有序)。然后可以合并成两个元素的有序对,然后两个有序数组再进行合并。
上代码:
//快速排序
function quickSort(array) {
if (array.length < 2) {
return array;
}
const target = array[0];
const left = [];
const right = [];
for (let i = 1; i < array.length; i++) {
if (array[i] < target) {
left.push(array[i]);
} else {
right.push(array[i]);
}
}
return quickSort(left).concat([target], quickSort(right));
}
//归并排序
function mergeSort(array) {
if (array.length < 2) {
return array;
}
const mid = Math.floor(array.length / 2);
const front = array.slice(0, mid);
const end = array.slice(mid);
return merge(mergeSort(front), mergeSort(end));
}
//合并有序数组的函数
function merge(front, end) {
const temp = [];
while (front.length && end.length) {
if (front[0] < end[0]) {
temp.push(front.shift());
} else {
temp.push(end.shift());
}
}
while (front.length) {
temp.push(front.shift());
}
while (end.length) {
temp.push(end.shift());
}
return temp;
}
快速排序的时间复杂度是一个变值。最坏的情况就是选择分割的元素是一个数组里面最大或者最小的元素。最优的情况是,分割的元素即是平均值。
分治法的核心,就是在问题可以分割成简单问题的情况下,根据某一个标准进行分割的一个思路。本质上也是将一个复杂的问题“转变”为一系列简单的问题。