算法-分治算法

/**
 * 分治算法
 * 将原问题划分成n个规模较小,并且结构与原问题相似的子问题,递归的解决这些子问题,然后再合并其结果,就得到
 * 原问题的解。
 * 分解:将原问题分解成一系列的子问题;
 * 解决:递归地求解各个子问题。若子问题足够小,则直接求解;
 * 合并:将子问题的结果合并成原问题。
 */

/**
 * 使用归并排序 来求逆序对的个数
 * 
 */
 function getReverseDegree(nums){
    return mergeSort(nums,0,nums.length-1)
 }

 function mergeSort(nums,p,r){
    if(p>=r) return 0;
    let q = Math.floor((p+r)/2);
    let left = mergeSort(nums,p,q);
    let right  = mergeSort(nums,q+1,r);
    let m = merge(nums,p,q,r);
    return left+right+m;
     
 }
 function merge(array,p,q,r){
    let i=p,j=q+1,k=0,result = [],count = 0;
    while(i<=q && j<=r){
        if(array[i] <= array[j]){
            result.push(array[i]);
            i++;
        }else{
            count += (q -i+1);//统计p-q之间 比a[j]大的个数,因为数组本来是已经有序了,既然当前这个数比他大,那么在它右边的都比它大
            result.push(array[j]);
            j++;
        }
    }
    if(i<=q){
        result =  result.concat(array.slice(i,q+1));
    }
    if(j<=r){
        result = result.concat(array.slice(j,r+1));
    }
    for( i = 0;i<result.length;i++){
        array[i + p] = result[i]
    }
    return count;
 }
 console.log(getReverseDegree([1,5,6,2,3,4]))


 /**
  * 有两个 n*n 的矩阵 A,B,如何快速求解两个矩阵的乘积 C=A*B
  * 矩阵的乘积
  * C = A * B
  * C = [
  *       C11  C12
  *       C21  C22
  *     ]
  * A = [
  *      A11  A12
  *      A21  A22
  * ]
  * 
  * B = [
  *      B11 B12
  *      B21 B22
  * ]
  * 
  * 那么C11 = A11 * B11 + A12 * B21
  *     C12 = A11 * B12 + A12 * B22
  *     C21 = A21 * B11 + A22 * B21
  *     C22 = A21 * B12 + A22 * B22
  * 
  * n *n 矩阵被分成4个(n/2)*(n/2)的子矩阵(假设是正方形的矩阵)
  * 
  * 
  */

  function matrixMultiply(a,b){
    let n = a.length,c ;
    if(n == 1) {
        c = makeArray(1);
        c[0][0] = a[0][0] * b[0][0]
    }else{
        c = makeArray(n)
        //将a,b分别分区成四个a11,a12,a21,a22
        let [a11,a12,a21,a22] = partitionMatrix(a);
        let [b11,b12,b21,b22] = partitionMatrix(b);
        let [c11,c12,c21,c22] = partitionMatrix(c);
        c11 = addMatrix(matrixMultiply(a11,b11),matrixMultiply(a12,b21));
        c12 = addMatrix(matrixMultiply(a11,b21),matrixMultiply(a12,b22));
        c21 = addMatrix(matrixMultiply(a21,b11),matrixMultiply(a22,b12));
        c22 = addMatrix(matrixMultiply(a21,b12),matrixMultiply(a21,b22));
        c = mergeMatrix(c11,c12,c21,c22);
    }
    return c;

}
function partitionMatrix(a){
    //将一个矩阵分成四个
    let n = a.length,n2 = n/2,a11 = makeArray(n2),a12 = makeArray(n2),a21 = makeArray(n2),a22 = makeArray(n2);
    for(let i = 0;i<n2;i++){
        for(let j = 0;j<n2;j++){
            let value1 = a[i][j],value2 = a[i][j+n2],value3 = a[i+n2][j],value4 = a[i+n2][j+n2];
            a11[i][j] = value1;
            a12[i][j] = value2;
            a21[i][j] = value3;
            a22[i][j] = value4;
        }
    }
    return [a11,a12,a21,a22];
}
function makeArray(n){
    let newarray = [];
    for(let i = 0;i<n;i++){
        let array = [];
        for(j=0;j<n;j++){
            array[j] = 0
        }
        newarray.push(array);
    }
    return newarray;
}
function addMatrix(a,b){
    //将两个矩阵相加
    let n = a.length,c = makeArray(n);
    for(let i = 0;i<n;i++){
        for(let j = 0;j<n;j++){
            c[i][j] = a[i][j ] + b[i][j]
        }
    }
    return c;
}
function mergeMatrix(a11,a12,a21,a22){
    //将四个小矩阵 整合成一个大的矩阵。
    // console.log(a11,a12,a21,a22)
    let n2 = a11.length,n = n2 *2,array = makeArray(n);
    for(let i = 0;i<n;i++){
        for(let j = 0;j<n;j++){
            if(i<=n2-1 && j<=n2-1){
                array[i][j] = a11[i][j];
            }else if(i<=n2-1 && j>n2-1){
                array[i][j] = a12[i][j-n2];
            }else if(i>n2-1 && j<=n2-1){
                array[i][j] = a21[i-n2][j];
            }else{
                array[i][j] = a22[i-n2][j-n2];
            }
        }
    }
    return array;
}
let array = [
    [1,2,3,4],
    [5,6,7,8],
    [9,10,11,12],
    [13,14,15,16]
]
console.log(matrixMultiply(array,array))



 /**
  * 二维平面上有 n 个点,如何快速计算出两个距离最近的点对?
  * https://zhuanlan.zhihu.com/p/296231213
  */

  function distance(x,y){
    //根据两个点的坐标求两点之间直线距离
    return Math.sqrt( Math.pow((x[0] - y[0]),2)+ Math.pow(x[1] - y[1],2) );
 }
 function divideMinDistance(points){
     let n = points.length;
     if(n<2){
         return 99999
     }else if(n == 2){
         return (distance(points[0],points[1]))
     }
     let newpoints = points.sort((a,b)=>a[0]-b[0]);//点根据x轴坐标排序
     //根据x轴分开两边分别求最短距离
     let half = Math.floor(n/2);
     let lefnew = newpoints.slice(0,half);
     let rightnew = newpoints.slice(half,n)
     let d1 = divideMinDistance(lefnew);
     let d2 = divideMinDistance(rightnew);
     let d = Math.min(d1,d2);
     console.log(n,half,d1,d2,d,newpoints,lefnew,rightnew)
     //中间线的点
     let calibration = newpoints[half][0];
     let left = [],right = [];
     for(let u in newpoints){
         if(calibration - d < u[0] && u[0] < calibration){
             left.push(u);
         }else if(calibration <= u[0] && u[0] < calibration + d){
             right.push(u);
         }
     }
     right = right.sort((a,b)=>a[0]-b[0]);//右侧点集根据x轴坐标排序
     let res = d;
     for(let u in left){
         let l = -1,r = right.length-1
         while(r - l >1){
             let m = (l + r) >> 1 ;// /2
             if(right [m][1] <= u[1] -d){
                 //在框内
                 l = m;
             }else{
                 r = m;
             }
         }
         let idx = r;
         for(let j = 0;j< 7;j++){
             if(j + idx >= right.length){
                 break;
             }
             let dd = distance(u,right[idx+j])
             if( dd < res){
                 res = dd;
             }
         }
     }
     return res;

 }
 
 function getPoints(n){
    let points = [];
    for(let i = 0;i<n;i++){
        let point = [Math.ceil(Math.random()*100),Math.ceil(Math.random()*100)];
        points.push(point);
    }
    return points;
 }
 let points = getPoints(20);
//  console.log(points)
 console.log(divideMinDistance(points))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值