对于递归的理解
我的理解是递归有点像DOM事件机制和洋葱模型,但是多少有一点区别
DOM事件的触发过程是捕获 -> 当前节点 -> 冒泡
洋葱模型: 请求 -> next -> 相应
递归操作: 递 -> 节点 -> 归
React组件挂载: componentWillMount -> render -> componentDidMount;
父组件的WillMount早于子组件的WillMount;父组件的DidMount晚于子组件的DidMount;
总结
递归的操作跟上面的这几种都特别相像,不同的是别的可能是不同的函数主体,但是递归是一直调用的自身。
归并
归并原理
将数组中中间分开,一直分,一直分,当分到nums.length <= 1时,开始出栈做合并操作。 所以它的代码步骤是两部分
- 递阶段从中间拆开当前数组
- 归阶段合并左右两个数组 由于合并两个数组的操作,而不是原地位置交换操作,所以需要做一个新的空间来做存储,因此空间复杂度是O(n)
例子理解
// 第一次入栈
var nums = [5,1,1,2,0,0];
a = [5, 1, 1];
b = [2, 0, 0];
// a 分支入栈
// a第一次入栈
a = [5];
b = [1, 1];
// a第二次入栈
a = [1];
b = [1];
// a第一次出栈
merge([1, 1]);
// a第二次出栈
merge([5], [1, 1]);
// b分支第一次入栈
a = [2];
b = [0, 0];
// b分支第二次入栈
a = [0];
b = [0];
// b分支第一次出栈
merge([0], [0])
// b分支第二次出栈
merge([2], [0, 0])
// 最后一次出栈
merge([1, 1, 5], [0, 0, 2]);
代码
var sortArray = function(nums) {
// 1. 递操作
if (nums.length <= 1) return nums;
let i = ~~(nums.length / 2);
let a = nums.slice(0, i);
let b = nums.slice(i);
// 2. 当前节点
a = sortArray(a);
b = sortArray(b);
// 3. 归操作
return merge(a, b);
};
function merge(a, b) {
let result = [];
let m = a.length;
let n = b.length;
let i = 0;
let j = 0;
while(i < m && j < n) {
if (a[i] < b[j]) {
result.push(a[i]);
i++;
} else {
result.push(b[j]);
j++;
}
}
while(i < m) {
result.push(a[i]);
i++;
}
while(j < n) {
result.push(b[j]);
j++;
}
return result;
}
快排
快排的原理
找到一个povit,确定他在数组中的位置,当他的位置确定后,在以它为轴,去左右中在分别确定一个轴,去确定这个轴在数组的位置 因此他的代码操作是
- 在递的阶段找到一个点,确定他的位置
- 归不做操作
因为是原地交换操作,排除递归造成的空间损耗,可以认为它的空间复杂度是O(1);
代码实现
var sortArray = function(nums) {
quickSort(nums, 0, nums.length - 1);
return nums;
};
function quickSort(nums, l, r) {
if (l < r) {
let pos = getPos(nums, l, r);
quickSort(nums, l, pos - 1);
quickSort(nums, pos + 1, r)
}
}
// 如果设置左边为povit,则指针改变应该从右边开始
// 因为从右边的话,最后i == j的那个nums[i] 一定是 nums[i] < povit 的。因为如果nums[j] >= povit的话,一定会向左移动。直到第一个小于
// 对于数据本身比较有序的情况下可以做随机化处理
// 随机化处理,就是先随机获取一个下标index,然后在开始循环前,将他和l交换位置
function getPos(nums, l, r) {
// 随机化处理
randomPovit(nums, l, r);
let povit = nums[l];
let i = l;
let j = r;
while(i < j) {
// 从数组的最后找到第一个小于自己的点
while(i < j && nums[j] >= povit) j--;
// 从数组前面找到第一个大于自己的点
while(i < j && nums[i] <= povit) i++;
// 交换他们两个的位置
if (i !== j) {
swap(nums, i, j);
}
}
// 此时i === j,数组中的情况
// l是povit, l + 1到i是小于povit的,j + 1 到r是大于povit的
// 将l和i交换位置后,则l到i-1是小于povit的,i是当前的povit
swap(nums, l, i);
return i;
}
function randomPovit(nums, l, r) {
let i = ~~(Math.random() * (r - l + 1) + l);
swap(nums, l, i);
}
function swap(nums, i, j) {
let temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}