JS.Day2-堆选(Py)/三路快排-快速选择-215,11,560,21,128,20,121

目录

215.找第k大元素

三路的快速排序

快速选择

法2.堆选

(堆排序)

11.盛更多水的容器

代码1

代码2

560.和为K的子数组(题意!)

惯性思维

正解

21.合并生序链表

递归写法

128.最长连续序列

20.有效的括号

面试的时候不好好审题,太急,直接惯性思维用三个栈了

121.买卖股票的最佳时机


215.找第k大元素

那么这道题想要时间复杂度低,肯定是不能全部排序的

先来讲讲

三路的快速排序

三路快排在两路的基础上加上了=的部分,

为了处理全部元素相同的特殊情况

总的来说就是通过三个指针维护 左边<、中间=、右边> 

如下图的左上角所示

 JS代码如下

function quickSort3Way(arr, left = 0, right = arr.length - 1) {
    if (left >= right) return;

    let lt = left;         // 小于区域的右边界
    let gt = right;        // 大于区域的左边界
    let pivot = arr[left]; // 选取第一个元素为基准
    let i = left + 1;      // lt 至 gt 中、=pivot右边、的当前正在判断的位置

    while (i <= gt) {
        if (arr[i] < pivot) {
            [arr[lt], arr[i]] = [arr[i], arr[lt]];
            lt++;
            i++;
            //原本lt位置是=pivot的,所以<分支中可以直接i++判断下一个

        } else if (arr[i] > pivot) {
            [arr[i], arr[gt]] = [arr[gt], arr[i]];
            gt--;
        } else {
            i++;
            //而gt位置是未知的,不能i++

        }
    }

    quickSort3Way(arr, left, lt - 1);
    quickSort3Way(arr, gt + 1, right);
}


let nums = [3, 5, 2, 3, 1, 3, 4, 2, 3];
quickSort3Way(nums);
console.log(nums);

注意:

//原本lt位置是=pivot的,所以<分支中可以直接i++判断下一个 

//而gt位置是未知的,不能i++

快速选择

快速选择就是在快速排序的基础上,对于最后的部分进行判断分支剪枝,从而只进行必要的排序

if (k_smallset

法2.堆选

JS中没有内置堆模块,python自带了,而且还有两个(heapq、PriorityQueue)

通过最小堆找第k大(用最大堆的话得维护所有元素且弹k次)

将数组压入堆,维护至最大的k个元素(有多于k个:弹出最小的也就是弹出堆顶),那么堆顶就是第k大

import heapq

def findKthLargest(nums, k):
    heap = []
    for num in nums:
        heapq.heappush(heap, num)
        if len(heap) > k:
            heapq.heappop(heap)
    return heap[0]

(堆排序)

堆排序就是一直 heappop

import heapq

def heap_sort(nums):
    heapq.heapify(nums)  # O(n) 建堆
    res = [heapq.heappop(nums) for _ in range(len(nums))]
    return res

11.盛更多水的容器

var maxArea = function(height) {
    let ans=0;
    for(let l=0;ll){
            if (height[r]>hnow){
                ans=Math.max(ans,(r-l)*Math.min(height[l],height[r]));
                hnow=height[r];
            }
            r--;//方向会影响
        }
    }
    return ans;
};

上面我的第一次贪心效果并不是很好

实际上:由于双指针往中间时 d小了,只有 h大 才可能S大

只需要在 h 更大的时候才需要更新 ans 

具体细节:短板效应-那边更小动那边

代码1

var maxArea = function(height) {
    let ans = 0, left = 0, right = height.length - 1;
    while (left < right) {
        const area = (right - left) * Math.min(height[left], height[right]);
        ans = Math.max(ans, area);
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
    }
    return ans;
};

代码2

var maxArea=function(height){
    let l=0;
    let r=height.length-1;
    let lh=height[l];
    let rh=height[r];
    let ans=(r-l)*Math.min(lh,rh);
    while(llh){
                lh=lnow;
                ans=Math.max(ans,(r-l)*Math.min(lh,rh));
            }
        }else{
            r--;
            rnow=height[r];
            if(rnow>rh){
                rh=rnow;
                ans=Math.max(ans,(r-l)*Math.min(lh,rh));
            }
        }
    }
    return ans;
}

看上去代码能节省更新 ans 的次数,但是实际上 代码1更快

简洁的代码更强健

560.和为K的子数组(题意!)

不急
上来就写了一发DFS,但是题意是连续子数组

惯性思维

var subarraySum = function(nums, k) {
    let ans = [];

    function dfs(step, sum, choice) {
        if (sum === k) {
            ans.push([...choice]);  // 通过展开运算符,拷贝,避免后续更改
        }

        if (step === nums.length) {
            return;
        }

        choice.push(nums[step]);
        dfs(step + 1, sum + nums[step], choice);
        choice.pop();  // 直接pop,恢复现场
        dfs(step + 1, sum, choice);
    }

    dfs(0, 0, []);
    return ans;
};

正解

连续:前缀和

通过 哈希表 优化:找目标

var subarraySum = function(nums, k) {
    let count = 0;
    let sum = 0;
    let map = new Map();
    map.set(0, 1);  // 前缀和为0出现1次

    for (let num of nums) {
        sum += num;
        if (map.has(sum - k)) {
            count += map.get(sum - k);
        }
        map.set(sum, (map.get(sum) || 0) + 1);
    }
    return count;
};

(map.get(sum) || 0) 通过或语句将未出现时的 undifined 转为 0

21.合并生序链表

一看就是双指针

注意力扣的链表类

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }

当最后一方没有时,返回另一方,直接接在答案后面即可

递归写法

var mergeTwoLists = function(l1, l2) {
    if (l1 === null) {
        return l2;
    } else if (l2 === null) {
        return l1;
    } else if (l1.val < l2.val) {
        l1.next = mergeTwoLists(l1.next, l2);
        return l1;
    } else {
        l2.next = mergeTwoLists(l1, l2.next);
        return l2;
    }
};

复杂度比下面的迭代要好

var mergeTwoLists = function(l1, l2) {
    const prehead = new ListNode(-1);

    let prev = prehead;
    while (l1 != null && l2 != null) {
        if (l1.val <= l2.val) {
            prev.next = l1;
            l1 = l1.next;
        } else {
            prev.next = l2;
            l2 = l2.next;
        }
        prev = prev.next;
    }
    prev.next = l1 === null ? l2 : l1;

    return prehead.next;
};

128.最长连续序列

Set去重后

通过哈希表判断连续

var longestConsecutive=function(nums){
    const st=new Set(nums);
    let ans=0;
    for(const x of st){//注意下面用的都是st
        if(st.has(x-1)){
            continue;//上一个一定已经判断过了
        }
        let y=x+1;
        while(st.has(y)){
            y++;
        }
        ans=Math.max(ans,y-x);
    }
    return ans;
}

20.有效的括号

面试的时候不好好审题,太急,直接惯性思维用三个栈了

但是这怎么能三个栈呢,题目里面说了是正确的闭合,也就是 [ ( ] ) 是错的

直接开一个就够了

然后用哈希表优化逻辑

var isValid = function(s) {
    if (s.length % 2) { // s 长度必须是偶数
        return false;
    }
    const mp = {'(': ')', '[': ']', '{': '}'};
    const st = [];
    for (const c of s) {
        if (mp[c]) { // c 是左括号
            st.push(mp[c]); // 入栈
        } else if (st.length === 0 || st.pop() !== c) { // c 是右括号
            return false; // 没有左括号,或者左括号类型不对
        }
    }
    return st.length === 0; // 所有左括号必须匹配完毕
};

121.买卖股票的最佳时机

维护之前最小值这一变量

var maxProfit = function(prices) {
    let ans=0;
    let min=prices[0];
    for(let i=1;i

你可能感兴趣的:(JavaScript,前端,javascript,开发语言,ecmascript)