[路飞_js算法:分隔链表 设计循环队列 设计循环双端队列 最近的请求次数 设计前中后队列]

104 阅读4分钟

2934349b033b5bb5ead03e603ed3d539b600bc2c.jfif

>> 分隔链表

问题描述: 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。(by leetcode 86

示例 1:

img

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

思路:

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} x
 * @return {ListNode}
 */
var partition = function(head, x) {
    let lsn=new ListNode(),ls=lsn,lbn=new ListNode(),lb=lbn,p=head,q;
    while(p){
        q=p.next;
         p.next=null;
        if(p.val<x){
            ls.next=p;
            ls=p
        }
        else{
            lb.next=p;
            lb=p;
        }
        p=q;

    }
    ls.next=lbn.next;
    return lsn.next;
};

>> 设计循环队列

**问题描述:**设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。(by leetcode 622

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。

  • Front: 从队首获取元素。如果队列为空,返回 -1 。

  • Rear: 获取队尾元素。如果队列为空,返回 -1 。

  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。

  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。

  • isEmpty(): 检查循环队列是否为空。

  • isFull(): 检查循环队列是否已满。

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1);  // 返回 true
circularQueue.enQueue(2);  // 返回 true
circularQueue.enQueue(3);  // 返回 true
circularQueue.enQueue(4);  // 返回 false,队列已满
circularQueue.Rear();  // 返回 3
circularQueue.isFull();  // 返回 true
circularQueue.deQueue();  // 返回 true
circularQueue.enQueue(4);  // 返回 true
circularQueue.Rear();  // 返回 4

思路: 封装的思想

/**
 * @param {number} k
 */
var MyCircularQueue = function(k) {
    this.count=0;
    this.size=k;
    this.head=0;
    this.tail=0;
    this.data=[]
};

/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularQueue.prototype.enQueue = function(value) {
    if(this.isFull()){
        return false
    }
this.data[this.tail]=value;
this.tail++;
this.count++;
if(this.tail==this.size)this.tail=0;
return true;

};

/**
 * @return {boolean}
 */
MyCircularQueue.prototype.deQueue = function() {
    if(this.isEmpty()){
        return false;
    }
    // this.data[this.head]=null;
    this.head++;
    this.count--;
    if(this.head==this.size)this.head=0;
    return true;
};

/**
 * @return {number}
 */
MyCircularQueue.prototype.Front = function() {
    if(this.isEmpty()){
        return -1
    }
    return this.data[this.head]

};

/**
 * @return {number}
 */
MyCircularQueue.prototype.Rear = function() {
 if(this.isEmpty()){
        return -1
    }
    return this.data[this.tail-1<0?this.size-1:this.tail-1]
};

/**
 * @return {boolean}
 */
MyCircularQueue.prototype.isEmpty = function() {
    return this.count==0

};

/**
 * @return {boolean}
 */
MyCircularQueue.prototype.isFull = function() {
    return this.count==this.size
// if(this.count==this.size)return true
};

设计循环双端队列

问题描述: 设计实现双端队列。 你的实现需要支持以下操作:

  • MyCircularDeque(k):构造函数,双端队列的大小为k。
  • insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
  • insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
  • deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
  • deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
  • getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
  • getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
  • isEmpty():检查双端队列是否为空。
  • isFull():检查双端队列是否满了。(by leetcode 641

示例:

MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1);			        // 返回 true
circularDeque.insertLast(2);			        // 返回 true
circularDeque.insertFront(3);			        // 返回 true
circularDeque.insertFront(4);			        // 已经满了,返回 false
circularDeque.getRear();  				// 返回 2
circularDeque.isFull();				        // 返回 true
circularDeque.deleteLast();			        // 返回 true
circularDeque.insertFront(4);			        // 返回 true
circularDeque.getFront();				// 返回 4
 

思路: 这里需要注意的两点:1.一个是尾指针指向的是最后一个节点的下一个位置,因此在尾部插入节点的时候,应该先放值,再移动指针。2.指针移动的时候要主要-1跟this.size的值的处理。

/**
 * @param {number} k
 */
var MyCircularDeque = function(k) {
    this.tail=0;//尾部指针指向最后一个节点的后一个位置
    this.head=0;
    this.count=0;
    this.data=[];
    this.size=k;

};

/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularDeque.prototype.insertFront = function(value) {
    if(this.isFull()) return false;
    this.head=(this.head-1+this.size)%this.size;
    this.data[this.head]=value;
    this.count++;
    return true
};

/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularDeque.prototype.insertLast = function(value) {
    if(this.isFull())return false;
    this.data[this.tail]=value;//先赋值 再移动指针
    this.tail=(this.tail+1)%this.size;
    this.count++;
    return true;
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.deleteFront = function() {
    if(this.isEmpty())return false;
    this.head=(this.head+1)%this.size;
    this.count--;
    return true;
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.deleteLast = function() {
    if(this.isEmpty())return false;
    this.tail=(this.tail-1+this.size)%this.size;
    this.count--;
    return true
};

/**
 * @return {number}
 */
MyCircularDeque.prototype.getFront = function() {
    if(this.isEmpty())return -1;
    return this.data[this.head]
};

/**
 * @return {number}
 */
MyCircularDeque.prototype.getRear = function() {
    if(this.isEmpty())return -1;
    return this.data[(this.tail-1+this.size)%this.size]
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.isEmpty = function() {
    return this.count==0
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.isFull = function() {
    return this.count==this.size
};

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * var obj = new MyCircularDeque(k)
 * var param_1 = obj.insertFront(value)
 * var param_2 = obj.insertLast(value)
 * var param_3 = obj.deleteFront()
 * var param_4 = obj.deleteLast()
 * var param_5 = obj.getFront()
 * var param_6 = obj.getRear()
 * var param_7 = obj.isEmpty()
 * var param_8 = obj.isFull()
 */

最近的请求次数

问题描述: 写一个 RecentCounter 类来计算特定时间范围内最近的请求。(by leeetcode 933)

请你实现 RecentCounter 类:

  • RecentCounter() 初始化计数器,请求数为 0 。
  • int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。

保证 每次对 ping 的调用都使用比之前更大的 t 值。

示例:

输入:
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
输出:
[null, 1, 2, 3, 3]

解释:
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1);     // requests = [1],范围是 [-2999,1],返回 1
recentCounter.ping(100);   // requests = [1, 100],范围是 [-2900,100],返回 2
recentCounter.ping(3001);  // requests = [1, 100, 3001],范围是 [1,3001],返回 3
recentCounter.ping(3002);  // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3

思路: 通过队列的思想,当ping一个时间节点的时候,将队列中大于t-3000的节点去出队,最后队列长度就是要求的值。

var RecentCounter = function() {  
    this.count=[]                                          

};

/** 
 \* @param {number} t
 \* @return {number}
 */
RecentCounter.prototype.ping = function(t) {
    this.count.push(t);
    while(t-this.count[0]>3000){
        this.count.shift();
    }
    return this.count.length;
};

/**
 \* Your RecentCounter object will be instantiated and called as such:
 \* var obj = new RecentCounter()
 \* var param_1 = obj.ping(t)
 */

设计前中后队列

问题描述: 请你设计一个队列,支持在前,中,后三个位置的 pushpop 操作。(by leetcode 1670

请你完成 FrontMiddleBack 类:

  • FrontMiddleBack() 初始化队列。
  • void pushFront(int val)val 添加到队列的 最前面
  • void pushMiddle(int val)val 添加到队列的 正中间
  • void pushBack(int val)val 添加到队里的 最后面
  • int popFront()最前面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1
  • int popMiddle()正中间 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1
  • int popBack()最后面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1

请注意当有 两个 中间位置的时候,选择靠前面的位置进行操作。比方说:

  • 6 添加到 [1, 2, 3, 4, 5] 的中间位置,结果数组为 [1, 2, **6**, 3, 4, 5]

  • [1, 2, **3**, 4, 5, 6] 的中间位置弹出元素,返回 3 ,数组变为 [1, 2, 4, 5, 6]

示例 1:

输入:
["FrontMiddleBackQueue", "pushFront", "pushBack", "pushMiddle", "pushMiddle", "popFront", "popMiddle", "popMiddle", "popBack", "popFront"]
[[], [1], [2], [3], [4], [], [], [], [], []]
输出:
[null, null, null, null, null, 1, 3, 4, 2, -1]

解释:
FrontMiddleBackQueue q = new FrontMiddleBackQueue();
q.pushFront(1);   // [1]
q.pushBack(2);    // [1, 2]
q.pushMiddle(3);  // [1, 3, 2]
q.pushMiddle(4);  // [1, 4, 3, 2]
q.popFront();     // 返回 1 -> [4, 3, 2]
q.popMiddle();    // 返回 3 -> [4, 2]
q.popMiddle();    // 返回 4 -> [2]
q.popBack();      // 返回 2 -> []
q.popFront();     // 返回 -1 -> [] (队列为空)

思路: 用双向链表来实现,注重封装思想

// 实现链表节点
class NodeList{
    constructor(val,next,pre){
        this.val=(val===undefined ? 0 : val)
        this.next=(next===undefined ? null : next)
        this.pre=(pre===undefined ? null : pre)
    }
    // 节点之后插入
    insert_back(p){
      p.pre=this;
      p.next=this.next;
      if(this.next)this.next.pre=p;
      this.next=p;
      return true;
    }
    // 节点之前插入
    insert_front(p){
        p.pre=this.pre;
        p.next=this;
        if(this.pre)this.pre.next=p;
        this.pre=p;
        return true;
    }
    // 删除之前节点
    delete_front(){
        if(this.pre){
              let p=this.pre;
            p.pre.next=this;
            this.pre=p.pre;
        }
        return true;
    }
    // 删除之后节点
    delete_back(){
        if(this.next){
            let p=this.next;
            this.next=p.next;
            if(p.next)p.next.pre=this
        }
        return true;
    }

}
// 实现双向链表
class dbQueue{
    constructor(){
        this.tail=new NodeList();
        this.head=new NodeList();
        this.head.next=this.tail;
        this.head.pre=null;
        this.tail.next=null;
        this.tail.pre=this.head;
        this.count=0;
    }
    isEmpty(){
        return this.head.next==this.tail;
    }
    insert_front(val){
        // 在头节点后面插入值
        this.head.insert_back(new NodeList(val));
        this.count++;
        return true;
    }
    insert_back(val){
        // 在尾节点之前插入节点
       this.tail.insert_front(new NodeList(val))
       this.count++;
       return true;
    }
    delete_back(){
        //尾节点之前删除-尾部出队
        if(this.isEmpty())return -1;
        this.count--;
        let val=this.tail.pre.val;
        this.tail.delete_front();
        return val
        

    }
    delete_front(){
        // 头节点之后删除-头部出队
        if(this.isEmpty())return -1;
        this.count--;
        let val=this.head.next.val;
        this.head.delete_back();
        return val
    }
    front(){
            return this.head.next.val;
    }
    rear(){
            return this.tail.pre.val;
    }
    size(){
        return this.count;
    }
}

// 双向链表实现
var FrontMiddleBackQueue = function() {
    this.l1=new dbQueue();
    this.l2=new dbQueue();
};

FrontMiddleBackQueue.prototype.isEmpty = function(val) {
   return this.l1.size()==0
   
};
FrontMiddleBackQueue.prototype.update = function(val) {
   if(this.l1.size()<this.l2.size()){
       this.l1.insert_back(this.l2.front())
       this.l2.delete_front()
   }
   else  if(this.l1.size()>=this.l2.size()+2){
       this.l2.insert_front(this.l1.rear())
       this.l1.delete_back()
   }
};
/** 
 * @param {number} val
 * @return {void}
 */
FrontMiddleBackQueue.prototype.pushFront = function(val) {
    this.l1.insert_front(val)
    this.update();
   // console.log(this.l1)
    return true;
};

/** 
 * @param {number} val
 * @return {void}
 */
FrontMiddleBackQueue.prototype.pushMiddle = function(val) {
    if(this.l1.size()>this.l2.size()){
    this.l2.insert_front(this.l1.rear());
    this.l1.delete_back();
    }
    this.l1.insert_back(val);
    return true;
};

/** 
 * @param {number} val
 * @return {void}
 */
FrontMiddleBackQueue.prototype.pushBack = function(val) {
    this.l2.insert_back(val)
    this.update();
    return true;
};

/**
 * @return {number}
 */
FrontMiddleBackQueue.prototype.popFront = function() {
    if(this.isEmpty()) return -1
    let val=this.l1.delete_front();
    this.update();
    return val;
};

/**
 * @return {number}
 */
FrontMiddleBackQueue.prototype.popMiddle = function() {
    if(this.isEmpty()) return -1;
    var val=this.l1.delete_back();
    this.update();
    return val;
};

/**
 * @return {number}
 */
FrontMiddleBackQueue.prototype.popBack = function() {
   if(this.isEmpty()) return -1;
    let val=0;
    if(this.l2.size()){val=this.l2.delete_back();}
    else{
        val=this.l1.delete_back();
    }
    this.update();
    return val;
};