左神:中级提升班4

1.自定义栈

2.队列成栈,栈成队列

3.动态规划的空间压缩技巧

        3.1最小路径和

        3.2装水

4.最大绝对差值

5.互为旋转词

6.咖啡机问题

7.任意相邻两数之积是4的倍数


1.自定义栈

 思路:
    1. 定义两个栈Data和Min
    2. Data正常放入数据
    3. Min在有新数据放入Data时,也会被重复放入当前Data中的最小值,所以Data中的最小值保存在Min栈顶
    4. Data做一次pop操作时,Min也做一次pop操作

2.队列成栈,栈成队列

 

队列成栈:
    class TwoQueueStack {
private:
	queue<int>qu;
	queue<int>help;
public:
	void push(int pushInt) {
		qu.push(pushInt);
	}

	//交换的原因:push操作是把元素插入到qu队列中,让qu一直是保留多个元素的队列,才能保证元素的相对顺序。
	int top() {
		if (qu.empty()) {
			throw runtime_error("Stack is empty");
		}
		while (qu.size() != 1) {
			help.push(qu.front());
			qu.pop();
		}
		int res = qu.front();
		qu.pop();
		swap();
		return res;
	}

	void pop() {
		if (qu.empty()) {
			throw runtime_error("Stack is empty");
		}
		while (qu.size() >1) {
			help.push(qu.front());
			qu.pop();
		}
		qu.pop();
		swap();
	}

	void swap() {
		queue<int>temp = qu;
		qu = help;
		help = temp;
	}
};

栈成队列:
    1. 定义两个栈push和pop
    2. 用户push新数据时就放入push栈
    3. 用户pop时先判断pop栈是否为空,然后再把push栈的全部数据弹出到pop栈中
    
class TwoStackQueue {
private:
	stack<int>stackPush;
	stack<int>stackPop;
	
public:
	void push(int pushInt) {
		stackPush.push(pushInt);
		dao();
	}

	void pop() {
		if (stackPop.empty() && stackPush.empty()) {
			throw runtime_error("Queue is empty!");
		}
		dao();
		stackPop.pop();
	}

	int front() {
		if (stackPop.empty() && stackPush.empty()) {
			throw runtime_error("Queue is empty!");
		}
		dao();
		return stackPop.top();
	}

	void dao() {
		if (stackPop.empty()) {//pop栈为空push栈才能倒数据
			while (!stackPush.empty()) {//一次性倒空
				stackPop.push(stackPush.top());
				stackPush.pop();
			}
		}
	}
};

因为队列和栈可以互通,所以图的深度或广度优先遍历既可以用栈实现,也可以用队列实现。

3.动态规划的空间压缩技巧

3.1最小路径和

思路:
根据转移方程决定需要几行数组
    
int minPathSum(vector<vector<int>>& m) {
	if (m.size() == 0)return 0;
	int more = max(m.size(), m[0].size());
	int less = min(m.size(), m[0].size());
	bool rowmore = more == m.size();
	vector<int>arr(less);
	arr[0] = m[0][0];
	for (int i = 1; i < less; i++) {
		arr[i] = arr[i - 1] + (rowmore ? m[0][i] : m[i][0]);
	}
	for (int i = 1; i < more; i++) {
		arr[0] = arr[0] + (rowmore ? m[0][i] : m[i][0]);
		for (int j = 1; j < less; j++) {
			arr[j] = min(arr[j - 1], arr[j]) + (rowmore ? m[i][j]:m[j][i]);
		}
	}
	return arr[less - 1];
}

3.2装水

思路:
只关心每个位置上方能够留下多少水
V[i]=max(min(左max,右max)-arr[i],0)
    1. 利用双指针L=1、R=arr.size()-2指向数组左右两侧
    2. 定义两变量maxLeft、maxRight,分别记录L、R扫过区域的最大值
    3. 计算水量all:瓶颈由小的确定,哪边高度低就移动哪边的指针
      3.1 maxLeft<maxRight:all+=maxLeft-arr[L]>0?maxLeft-arr[L]:0;右移L,更新maxLeft
      3.2 maxRight<maxLeft:all+=maxRight-arr[R]>0?maxRight-arr[R]:0;左移R,更新maxRight
      3.3 maxRight==maxLeft:all+=maxLeft-arr[L]>0?maxLeft-arr[L]:0;右移L,更新maxLeft;左移
    	  R,更新maxRight
    4. maxLeft是L位置左边真实的最大值,maxRight是R位置右边真实的最大值,所以当哪边真实的最大值更小时,
       对应那边指针的水量才可以计算,因为真实的瓶颈我们已经知道了。
    
int getWater(vector<int>& arr) {
	if (arr.size() < 3) return 0;
	int L = 1;
	int R = arr.size() - 2;
	int maxLeft = arr[0];
	int maxRight = arr[arr.size() - 1];
	int all = 0;
	while (L <= R) {
		if (maxLeft <= maxRight) {
			all += max(maxLeft - arr[L], 0);
			maxLeft = max(maxLeft, arr[L++]);
		}
		else {
			all += max(maxRight - arr[R], 0);
			maxRight = max(maxRight, arr[R--]);
		}
	}
	return all;
}

4.最大绝对差值

思路:
    1.遍历数组找到最大值maxVal
    2.由于maxVal是全局最大值,所以maxVal必定是左部分最大值或者右部分最大值
    3.又因为无论怎么划分,arr[0]和arr[arr.size()-1]必定分别在左部分和右部分
    4.所以左部分的最大值要么是arr[0],要么就是比arr[0]更大的数,右部分同理
    5.最终答案就是:max(maxVal-arr[0],maxVal-arr[arr.size()-1])

5.互为旋转词

 思路:
    1. 判断a、b长度是否一样
    2. a+=a;
    3. 利用kmp算法判断b是否为a的子串

6.咖啡机问题

(1)vector<int>arr:数组大小代表咖啡机数量,数据代表冲一杯咖啡所需时间
(2)int n:n个人要喝咖啡,每人一杯
(3)int a:洗杯器(洗一个杯子所需时间a),只能串行,一次只能洗一个杯子
(4)int b:咖啡杯自然挥发变干净的时间
问题:从第一个人开始泡咖啡开始,到所有杯子都变干净至少需要多少时间?

思路:
    1.创建一个小根堆,如上图所示,第一个数代表开始时刻,第二个数代表所需时间,按结束时间早晚来排序。
    2.每个人拿完咖啡(取堆顶)后,更新小根堆里的数据,例如第一个人拿完后,(0,2)更新成(2.2)
    3.将每个人拿到咖啡的时间放入drinks数组中,该数据也是开始洗杯子的时间
    
//洗杯子
int process(vector<int>&drinks,int a,int b,int index,int washLine){
    if(index==drinks.size()-1){
        return min(max(washLine,drinks[index])+a,drinks[index]+b);
    }
    //用洗杯器
    int wash=max(washLine,drinks[index])+a;//洗完该杯子的结束时间,喝完才能洗
    int next1=process(drinks,a,b,index+1,wash);//洗完剩下杯子所需时间
    int p1=max(wash,next1);//全部事情做完所需时间
    
    //自然风干
    int dry=drink[index]+b;
    int next2=process(drinks,a,b,index+1,washLine);
    int p2=max(dry,next2);
    
    return min(p1,p2);//p1和p2是当前杯子选择不同干净的方式所需的总时间,返回最小的即可
}

由于上述递归只有两个可变参数,因此可以利用一张二维表动态规划完成
index:0~n-1
washLine:0~drinks[n-1]+n*a(准备足够用的表即可)

7.任意相邻两数之积是4的倍数

 思路:
    1.统计奇数个数(a)、偶数个数(2的数量(b)、包含4因子的数(c))
    2.讨论b:
      2.1 b==0:奇4奇4奇4奇4奇4奇4奇4奇4……;a==1(c>=1),a>1(c>=a-1)
      2.2 b==1:a==0(c>=1),a==1(c>=1),a>1(c>=a)
      2.3 b>1:22222224奇4奇4奇4奇4……;a==0(c>=0),a==1(c>=1),a>1(c>=a)=>c>=a
    3.if(b==0)return a==1?c>=1:c>=a-1;
      else if(b==1)return a==0?c>=1:(a==1?c>=1:c>=a)
      else(b>1)return c>=a;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jomo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值