实现快速排序算法
今天记录两种递归快排算法和一种非递归快排算法,我们用JavaScript来实现。
(一)空间复杂度更高(递归版)
function quickSort1(arr){
if(arr.length <= 1){
return arr;
}
var left = [];
var right = [];
var base = arr[0];
for(var i = 1; i < arr.length; i++){
if(arr[i]<=base){
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort1(left).concat(base, quickSort1(right));
}
(二)空间复杂度更低(递归版)
start和end的初始值分别为0,arr.length-1.
function quickSort2(arr,start,end){
if(end - start < 1){return }
const target = arr[start];
let [l,r] = [start,end];
while(l < r){
while(l < r && arr[r]>=target){
r--;
}
arr[l] = arr[r];
while(l < r && arr[l] < target){
l++;
}
arr[r] = arr[l]
}
arr[l] = target;
quickSort2(arr,start,l-1);
quickSort2(arr,l+1,end);
return arr;
}
非递归版
function quickSort3(arr,s,e){
let list = [[s,e]];
while(list.length){
let now = list.pop();
if(now[0] >= now[1]){
continue;
}
let [l,r] = [now[0],now[1]];
let target = arr[now[0]];
while(l < r){
while(l < r && arr[r] >= target){
r--;
}
arr[l] = arr[r];
while(l < r && arr[l] < target){
l++;
}
arr[r] = arr[l]
}
arr[l] = target;
list.push([now[0],l-1]);
list.push([l+1,now[1]]);
}
return arr
}
- 快排是否稳定: 不稳定
- 最优时间复杂度: O(nlog2n),即每次选取的基数都能平分整个数组。
- 最差时间复杂度:O(n2),即每次选取的基数都是数组中的最大值或者最小值。
冒泡排序
(一)基础版
function bubbleSort(arr){
for(var j = arr.length - 1; j >= 1; j--){
for(var i = 0; i <= j - 1; i++){
if(arr[i] > arr[i + 1]){
var temp = arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = temp;
}
}
}
return arr;
}
console.log(bubbleSort([89,67,54,12,45,5,1,4,3,2]));
//[1, 2, 3, 4, 5, 12, 45, 54, 67, 89]
(二)加强版
function bubbleSort(arr){
for(var j = arr.length - 1; j >= 1; j--){
var done = true;
for(var i = 0; i <= j - 1; i++){
if(arr[i] > arr[i + 1]){
var temp = arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = temp;
done = false;
}
}
if(done){
break;
}
}
return arr;
}
对比基础版和加强版,可以发现,加强版在每一轮循环中设置了一个标志位。它的作用是:当排序在某次循环中已全部完成时,就无需再次循环直至结束。例如:[1,2,4,3],在第一轮循环后就变成了[1,2,3,4],就无需进入接下来的循环了。
- 冒泡排序是否稳定:稳定
- 最优时间复杂度: O(n)
- 最差时间复杂度: O(n2)
- 平均时间复杂度: O(n2)
空间复杂度这里指临时变量temp所占内存.
- 最优空间复杂度: 0(已经全部有序)
- 最差空间复杂度: O(n)
- 平均空间复杂度: O(1)
选择排序
function selectionSort(arr) {
var temp;
for(var i = 0; i < arr.length - 1; i++){
for(var j = i + 1; j < arr.length; j++){
if(arr[i] > arr[j]){
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
- 选择排序是否稳定:不稳定
- 最优时间复杂度: O(n2)
- 最差时间复杂度: O(n2)
- 平均时间复杂度: O(n2)
空间复杂度这里指临时变量temp所占内存.
- 最优空间复杂度: 0(已经全部有序)
- 最差空间复杂度: O(n)
- 平均空间复杂度: O(1)
插入排序
function insertSort(arr){
for(var i = 1; i <= arr.length - 1; i++){
var temp = arr[i];
var j = i;
while(j > 0 && (arr[j - 1] >= temp)){
arr[j] = arr[j - 1];
j--;
}
arr[j] = temp;
}
return arr;
}
- 插入排序是否稳定:稳定
- 最优时间复杂度: O(n)
- 最差时间复杂度: O(n2)
- 平均时间复杂度: O(n2)
空间复杂度这里指临时变量temp所占内存.
- 平均空间复杂度: O(1)
当数据量较大时,速度上,插入>选择>冒泡。
大数相乘
function fn(nums1, nums2){
let res = new Array(nums1.length + nums2.length).fill(0);
for(let i = 0; i < nums1.length; i++){
for(let j = 0; j < nums2.length; j++){
res[i+j+1] += nums1[i] * nums2[j];
}
}
for(let k = res.length-1; k > 0; k--){
if(res[k] >= 10){
res[k-1] += Math.floor(res[k] / 10);
res[k] = res[k] % 10;
}
}
return res;
}
数组去重的四种方法
(一)直接遍历
function duplicate(numbers) {
if(numbers.length < 2){return false;}
var res = [numbers[0]];
for(var i = 1; i < numbers.length; i++){
var repeat = false;
for(var j = 0; j < res.length; j++){
if(numbers[i]==res[j]){
repeat = true;
break;
}
}
if(!repeat){
res.push(numbers[i]);
}
}
return res;
}
(二)先排序
function duplicate(numbers) {
if(numbers.length < 2){return false;}
numbers.sort((a,b) => a-b);
var res = [numbers[0]];
for(var i = 1; i < numbers.length; i++){
if(numbers[i]!==res[res.length - 1]){
res.push(numbers[i]);
}
}
return res;
}
(三)利用对象的属性
function duplicate(numbers) {
if(numbers.length < 2){return false;}
var obj = {};
var res = [];
for(var i = 0; i < numbers.length; i++){
if(!obj[numbers[i]]){
obj[numbers[i]] = 1;
res.push(numbers[i]);
}
}
return res;
}
(四)利用下标查找
function duplicate(numbers) {
if(numbers.length < 2){return false;}
var res = [numbers[0]];
for(var i = 1; i < numbers.length; i++){
if(res.indexOf(numbers[i])===-1){
res.push(numbers[i]);
}
}
return res;
}
二叉树的遍历
(一)前序遍历
function preOrderTraversal(pRoot){
if(!pRoot){
return;
}
console.log(pRoot.val);
var left = pRoot.left;
var right = pRoot.right;
preOrderTraversal(left);
preOrderTraversal(right);
}
(二)中序遍历
var inorderTraversal = function(root) {
var arr = [];
inorder(root,arr);
return arr;
};
function inorder(root,arr){
if(!root){return;}
var left = root.left;
var right = root.right;
inorder(left,arr);
arr.push(root.val);
inorder(right,arr);
}
(三)后序遍历
unction postOrderTraversal(pRoot){
if(!pRoot){
return;
}
var left = pRoot.left;
var right = pRoot.right;
postOrderTraversal(left);
postOrderTraversal(right);
console.log(pRoot.val);
}
斐波那契数列用JS闭包实现
function fib(n){
if(n < 0){
throw new Error('BBB')
}
let arr = [0,1];
function fibnaci(n){
if(arr[n] !== undefined){
return arr[n];
}
let data = fibnaci(n - 1) + fibnaci(n - 2);
arr[n] = data;
return data;
}
return fibnaci(n);
}
贪心算法,利用二分查找
小Q的父母要出差N天,走之前给小Q留下了M块巧克力。小Q决定每天吃的巧克力数量不少于前一天吃
的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力
function total(n,mid){
let sum = 0;
for(let i = 1; i <=n; i++){
sum += mid;
mid = Math.ceil(mid/2);
}
return sum;
}
function maxChocolate(n,M){
let first = 1;
let last = M;
let sum = 0;
let mid;
if(n === 1){return M}else{
while(first <= last){
mid = Math.ceil((first+last)/2);
sum = total(n,mid);
if(sum < M){
first = mid + 1 ;
}else if(sum === M){
return mid;
}else{
last = mid - 1;
}
}
return first-1;
}
}
编码
假定一种编码的编码范围是a ~ y的25个字母,从1位到4位的编码,如果我们把该编码按字典序排序,
形成一个数组如下: a, aa, aaa, aaaa, aaab, aaac, … …, b, ba, baa, baaa, baab,
baac … …, yyyw, yyyx, yyyy 其中a的Index为0,aa的Index为1,aaa的Index为2,以此类推。
编写一个函数,输入是任意一个编码,输出这个编码对应的Index.
function f(str){
let carry = [1+25+25*25+25*25*25,1+25+25*25,1+25,1];
let res = 0;
for(let i = 0; i < str.length; i++){
res += (str.charCodeAt(i) - 'a'.charCodeAt(0))*carry[i] + 1;
}
return res-1;
}