排序算法的应用-leetcode

排序算法的应用

2020.01.06 add桶排序应用

荷兰国旗问题-leetcode-75-颜色分类

references:

颜色分类

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
这道题有意思的点在于:区别于普通排序算法,这个题只有三种0 1 2数字,因此常规的排序算法可以使用,但是借助于移动指针,我们也能找到一种时间复杂度为O(N)的算法

image

const sortColors1=nums=>{
    // 对于所有idx<p0 nums[idx]=0,0的右边界p0
    // 对于所有的idx>p2 nums[idx]=2,2的左边界p2
    let p0=0,curr=0,p2=nums.length-1;
    while(curr<=p2){
        if(nums[curr]===0){
            // 交换p0 curr 然后分别右移指针
            let temp=nums[p0];
            nums[p0]=0;
            nums[curr]=temp;
            p0++;
            curr++;
        }else if(nums[curr]===2){
            // 交换p2 curr 然后p2指针左移
            nums[curr]=nums[p2];
            nums[p2]=2;
            p2--;
        }else{
            // 如果是1,不移动即可
            curr++;
        }
    }
    return nums;
};

leetcode-56-合并区间

总结: 对数组进行排序,然后通过检测对相邻元素进行合并

关键点:

  • 进行排序
    • 以右边元素为基准进行排序会造成一次遍历无法全部merge的情况,因此需要递归
    • 以左边元素为基准进行排序则可以实现遍历一次达成merger的效果。

方法0:基本思路是:分成两边,把不合适的剔除然后重组:

        var merge = function(intervals) {
          var res=[];
          var left=[];
          var right=[];
          for (var i=0;i<intervals.length;i++){
            left.push(intervals[i][0]);
            right.push(intervals[i][1]);
          }
          left.sort((a,b)=>a-b);
          right.sort((a,b)=>a-b);
          console.info(left);
          console.info(right);
          for (var i=0;i<intervals.length-1;i++){
            for(var j=i+1;j<intervals.length;j++){
              while (left[j]<=right[i]){
                left.splice(j,1);
                right.splice(i,1);
              }
            }
          }
          for (var k=0;k<left.length;k++){
            var res0=[];
            res0.push(left[k]);
            res0.push(right[k]);
            res.push(res0);
          }
          return (res);
        };
/**
 * 方法1:以数组的第二个元素为基准对数组进行排序,
 * 如果后一位的index=0<当前位的index=1,必定能进行merge
 * 缺点:总是向后遍历,会出现一次遍历后仍有待遍历的情况,
 * 如:[[2,3],[4,5],[6,7],[8,9],[1,10]]
 * 因此不得不递归再重新遍历
 * 时间复杂度:主要取决于sort函数
 * 空间复杂度:O(1) 就地merge
 * @param intervals
 */
const merge = intervals=> {
    let len=intervals.length;
    if(len===1) return intervals;
    // 涉及了排序操作
    intervals.sort((a,b)=>a[1]-b[1]);
    // console.info('intervals==>',intervals);
    for(let i=0;i<intervals.length-1;i++){
        // check
        if (intervals[i+1][0]<=intervals[i][1]){
            intervals[i]=[Math.min(intervals[i][0],intervals[i+1][0]),intervals[i+1][1]];
            intervals.splice(i+1,1);
            i--;
        }
    }
    // console.info(intervals);
    // 涉及了递归操作
    if(intervals.length===len){
        return intervals;
    }else{
        return merge(intervals);
    }
};
/**
 * 方法2:以数组的第一个元素为基准对数组进行排序
 * 此后以与方法1相同的套路进行核查是否需要merge
 * 优点是一次遍历之后所有能合并的都已经进行了合并,此方法最佳
 * @param intervals
 * @returns {*}
 */
const merge1 = intervals=> {
    let len=intervals.length;
    if(len===1) return intervals;
    // 涉及了排序操作
    intervals.sort((a,b)=>a[0]-b[0]);
    // console.info('intervals==>',intervals);
    for(let i=0;i<intervals.length-1;i++){
        // check
        if (intervals[i+1][0]<=intervals[i][1]){
            // intervals[i]=[Math.min(intervals[i][0],intervals[i+1][0]),intervals[i+1][1]];
            intervals[i]=[intervals[i][0],Math.max(intervals[i][1],intervals[i+1][1])];
            intervals.splice(i+1,1);
            i--;
        }
    }
    return intervals;
};

对链表进行插入排序-leetcode-147

首先要明确插入排序的本质,本质1. 迭代移动元素 本质2. 向前比较已经有序的数列

详情参看:

数据结构javascript描述

leetcode-220-存在重复元素 III

从这个题引入Java容器treeSet以及自平衡二叉树的理解(私以为这种做法非常的投机,但又不能说不对)

Java treeSet-平衡BST-红黑树

对桶排序的应用,既然题目要求值的间隔不超过t,并且索引间隔不超过k,那么我们可以安排最多k个桶,每个桶的值的区间是[0,t],[t+1,2t+1],[2t+2,3t+2]...,key值可以是Math.floor(nums[i]/(t+1)),也正是因为除的是t+1因此获取答案的时候也需要向周边的值进行对比。

/**
 * 和桶排序的不同之处在于每次我们的桶里只需要包含最多一个元素就可以了,
 * 因为如果任意一个桶中包含了两个元素,那么这也就是意味着这两个元素是 足够接近的 了,这时候我们就直接得到答案了。
 * 因此,我们只需使用一个标签为桶序号的散列表就可以了。
 * 时间复杂度:O(n)
 * 对于这 n 个元素中的任意一个元素来说,我们最多只需要在散列表中做三次 搜索,一次 插入 和一次 删除。这些操作是常量时间复杂度的。因此,整个算法的时间复杂度为 O(n)。
 * 空间复杂度:O(min(n,k))
 * 需要开辟的额外空间取决了散列表的大小,其大小跟它所包含的元素数量成线性关系。散列表的大小的上限同时由 n 和 k决定。因此,空间复杂度为 O(min(n,k))。
 * @param nums
 * @param k
 * @param t
 * @returns {boolean}
 */
const containsNearbyAlmostDuplicate1=(nums,k,t)=>{
    if(t<0) return false;
    let d=new Map();
    // 包含了对t===0的情况的处理
    let w=t+1;
    for(let i=0;i<nums.length;i++){
        let m=getId(nums[i],w);
        if(d.has(m)) return true;
        if(d.has(m-1)&&Math.abs(nums[i]-d.get(m-1))<w){
            return true;
        }
        if(d.has(m+1)&&Math.abs(nums[i]-d.get(m+1))<w){
            return true;
        }
        d.set(m,nums[i]);
        if(i>=k) d.delete(getId(nums[i-k],w));
    }
    return false;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值