买卖股票的最佳时机(多解法)
普通暴力解public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } int ans = 0; for (int i = 0; i < prices.length - 1; i++) { for (int j = i + 1; j < prices.length; j++) { if (prices[j] > prices[i]) { ans = Math.max(ans, prices[j] - prices[i]); } } } return ans; }暴力递归解public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } return process(prices, 0, -1, -1); } public int process(int[] prices, int i, int buyDay, int sellDay) { if (prices.length == i) { if (buyDay > -1 && buyDay < sellDay && prices[buyDay] < prices[sellDay]) { return prices[sellDay] - prices[buyDay]; } return 0; } // 在第i天不操作 int p0 = process(prices, i + 1, buyDay, sellDay); // 在第i天买入 int p1 = 0; if (buyDay == -1 && sellDay == -1) { p1 = process(prices, i + 1, i, sellDay); } // 在第i天卖出 int p2 = 0; if (buyDay > -1 && sellDay == -1) { p2 = prices[i] > prices[buyDay] ? prices[i] - prices[buyDay] : 0; } return Math.max(p0, Math.max(p1, p2)); }
另一种暴力递归可推导出动态规划:public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } return process(prices, 0, 0); } public int process(int[] prices, int i, int flag) { // base case if (i == 0) { if (flag == 0) { return 0; } return -prices[0]; } if (flag == 0) { // 第i天不持有股票 return Math.max(process(prices, i - 1, 1) + prices[i], process(prices, i - 1, 0)); } else { return Math.max(process(prices, i - 1, 1), -prices[i]); } }动态规划解
public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } int len = prices.length; // dp[i][0] 下标为 i 这天结束的时候,不持股,手上拥有的现金数 // dp[i][1] 下标为 i 这天结束的时候,持股,手上拥有的现金数 int[][] dp = new int[len][2]; // 初始化:不持股显然为 0,持股就需要减去第 1 天(下标为 0)的股价 dp[0][0] = 0; dp[0][1] = -prices[0]; // 从第 2 天开始遍历 for (int i = 1; i < len; i++) { dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]); dp[i][1] = Math.max(dp[i - 1][1], -prices[i]); } return dp[len - 1][0]; }
在暴力递归的基础上加缓存,转变成记忆化搜索:
最终版的动态规划是怎么写出来的呢?请看下图:
单调栈解
public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } int ans = 0; //int min = 0; //Stack upStack = new Stack(); int topIndex = -1; // 记录栈顶下标 int[] upStack = new int[prices.length]; for (int i = 0; i < prices.length; i++) { // 由小到大的单调栈 //while (!upStack.empty() && upStack.peek() > prices[i]) { while (topIndex > -1 && upStack[topIndex] > prices[i]) { //int p = upStack.pop(); int p = upStack[topIndex--]; int min = upStack[0]; if (p > min) { ans = Math.max(ans, p - min); } } /*if (upStack.empty()) { min = prices[i]; }*/ //upStack.push(prices[i]); upStack[++topIndex] = prices[i]; } // 栈顶元素未被处理 //if (upStack.size() > 1) { if (topIndex > 0) { //int p = upStack.pop(); int p = upStack[topIndex--]; int min = upStack[0]; if (p > min) { ans = Math.max(ans, p - min); } } return ans; }
直接使用 java.util.Stack 会对性能会造成影响,因此这里利用数组来优化。滑动窗口解public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } // minWindow 由小到大 LinkedList minWindow = new LinkedList(); for (int R = 0; R < prices.length; R++) { while (!minWindow.isEmpty() && prices[minWindow.peekLast()] >= prices[R]) { minWindow.pollLast(); } minWindow.addLast(R); int buyPrice = prices[minWindow.peekFirst()]; int sellPrice = prices[R]; if (sellPrice > buyPrice) { ans = Math.max(ans, sellPrice - buyPrice); } } return ans; }线段树解
public int maxProfit(int... prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0] >= prices[1])) { return 0; } int ans = 0; SegmentTree segmentTree = new SegmentTree(prices); for (int i = 0; i < prices.length; ++i) { int buyPrice = prices[i]; int sellPrice = segmentTree.query(i + 1, prices.length, 1, prices.length, 1); ans = Math.max(ans, sellPrice - buyPrice); } return ans; } public class SegmentTree { private int[] data; private int[] max; public SegmentTree(int[] src) { int N = src.length + 1; data = src; max = new int[N << 2]; build(1, N - 1, 1); } private void pushUp(int i) { max[i] = Math.max(max[i << 1], max[i << 1 | 1]); } private void pushDown(int i) { } private void build(int l, int r, int i) { if (l == r) { max[i] = data[l - 1]; return; } int mid = (l + r) >> 1; build(l, mid, i << 1); build(mid + 1, r, i << 1 | 1); pushUp(i); } public int query(int L, int R, int l, int r, int i) { if (L <= l && r <= R) { return max[i]; } int mid = (l + r) >> 1; pushDown(i); int left = 0; int right = 0; if (L <= mid) { left = query(L, R, l, mid, i << 1); } if (R > mid) { right = query(L, R, mid + 1, r, i << 1 | 1); } return Math.max(left, right); } }树状数组解
public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } int ans = 0; IndexTree indexTree = new IndexTree(prices.length); for (int i = 0; i < prices.length; i++) { indexTree.set(i + 1, prices[i]); } for (int i = 1; i < prices.length; i++) { int min = indexTree.min(i + 1); if (prices[i] > min) { ans = Math.max(ans, prices[i] - min); } } return ans; } public static class IndexTree { private int[] tree; private int N; // 0位置弃而不用 public IndexTree(int size) { N = size; tree = new int[N + 1]; for (int i = 1; i <= N; i++) { tree[i] = Integer.MAX_VALUE; } } // 返回 [1, index] 范围最小值 public int min(int index) { int ret = Integer.MAX_VALUE; while (index > 0) { ret = Math.min(tree[index], ret); index -= index & -index; } return ret; } // index & -index : 提取出index最右侧的1出来 // 假设 index 为 : 0011001000 // index & -index : 0000001000 public void set(int index, int v) { while (index <= N) { tree[index] = Math.min(tree[index], v); index += index & -index; } } }最优解public int maxProfit(int[] prices) { if (null == prices || prices.length < 2 || (prices.length == 2 && prices[0]>=prices[1])) { return 0; } int ans = 0; int minPre = prices[0]; for (int i = 1; i < prices.length; i++) { if (prices[i] > minPre) { ans = Math.max(ans, prices[i] - minPre); } else { minPre = prices[i]; } } return ans; }
以上花式炫技,你学废了吗?
江苏扩岗补助已促进超8。7万人就业现代快报讯(记者冯茜)据江苏省财政厅近日消息,江苏省拓宽一次性扩岗补助政策受益范围,由招用2022年度普通高校毕业生拓展至离校两年内未就业普通高校毕业生和登记失业的16至24岁青年
国产超冷门男主,突然走红刺痛了全网十几平米逼仄的房间里,光线昏暗湿热难耐,赤膊光膀的中年男子,正躺在地上午睡。下一秒老板一声人工闹铃,所有人就纷纷爬起来开工。按订单需求走,大家分工明确,有人负责画鼻子有人画眼睛还有
耐克为什么要把鞋越做越丑?妈妈见了要打人,外婆见了要拿针,这说的是破洞牛仔裤。一种长辈无法理解的时尚,裤子破了不补,还要穿出去招摇。往面子上说这是穷到没钱买新衣服的经济状况暴露无遗,往健康上说这是不注重保暖
中国情趣用品第一股冲击IPO折戟背后作者刘工昌这篇文章要从一则微博开始。从微博里看到,配图看起来有点像某些特殊场所,只不过由于材质和做工并不是那么冷色调,看起来可能会有其他感觉。没错,这就是2022年12月1日下午醉
于坚让我永远带着雨而不是伞旅美摄影家多多摄于纽约大都会艺术博物馆,他将于坚与伦勃朗的作品套叠在一起,产生了别样的效果(受访者提供图)是什么定义了诗人?不是光头,不是凸起的肚子,不是宽大的鼻眼和淡到几乎消失的
新兵赵老师武能单杠夺金牌文能架上画油画作者李汉卿梁海浪刘青听说南部战区空军某新训旅有位了不得的新兵赵老师他能文能武武能单杠夺金牌文能架上画油画他是谁呢?先欣赏一下他的作品吧自画像老者民工精神矍铄的老者水手夫妇大家口中的
数字经济,赋智经开52万活跃车辆12亿条售后信息,信息化时代,海量数据能为用户带来什么?2021年,陕汽基于四新引领的双循环智能质量管理模式荣获中国质量奖提名奖,并成为唯一一家获此殊荣的汽车企业。据
遭免董事长率50余人大闹董事会,致3名员工受伤!警方介入这令人匪夷所思的一幕发生在上市公司越博动力(300742。SZ)。12月8日晚,越博动力连发12条公告。其中一条公告披露,12月7日上午8点45分左右,刚遭罢免的前董事长李占江及其
爱他美奶粉有虫,飞鹤和伊利淡定吗?进口奶粉爱他美竟然有虫?出了这么大的事,飞鹤伊利等国产品牌,为何表现如此淡定?最近,爱他美因为活虫登上热搜,吃瓜网友们,迅速分成了两大阵营,有的人质疑视频造假,有的人则认为从这件事
洗羊毛羊绒织物用这几招不变形来源今晚报今晚报讯(记者狄慧)洗护羊毛羊绒制品,为了防止缩水和降低损伤,本市洗涤行业专家张文婷提醒,除了市场上已有的专用洗涤剂外,洗涤时也有注意事项。首先要单独洗涤,水温保持在30
三大航营收环比大涨超70,民航业迎来反转时刻?丨南财号联播mpId201一纸停产公告背后亚洲锂都环保风波调查近期,一场沿着锦江流域的环保检查正在进行,而最早引发关注的是云母提锂龙头永兴材料。11月底,该公司发布停产公告称,高安市在日常监测