emmm,头晕,就不多寒暄了。
这篇是两个设计类题目,一题是洗牌算法,一题是最小栈的实现。
第一题:洗牌算法
打乱一个没有重复元素的数组。
示例:
// 以数字集合 1, 2 和 3 初始化数组。
int[] nums = {1,2,3};
Solution solution = new Solution(nums);
// 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。
solution.shuffle();
// 重设数组到它的初始状态[1,2,3]。
solution.reset();
// 随机返回数组[1,2,3]打乱后的结果。
solution.shuffle();
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/shuffle-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处
第二题:最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
示例:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
提示:
pop、top 和 getMin 操作总是在 非空栈 上调用。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/min-stack
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的题解(1)
最开始我的思路呢,是这样
很快我就发现这样不行。
有多快呢?写完就发现了。
因为vector的删除需要迭代器,慢吞吞的爬过去,删除,再爬到另一个位置,删除,再爬···
慢死了。
后来学聪明了,换这套:
当然,每种情况出现概率如何,我觉得是相等的,如果有大神可以算一下,我准备去买那个程序员的数学课了,学完之后,老规矩,等我博客嘿嘿。
看代码:
class Solution {
public:
Solution(vector<int> nums): v(nums) {}
/** Resets the array to its original configuration and return it. */
vector<int> reset() {
return v;
}
/** Returns a random shuffling of the array. */
vector<int> shuffle() {
vector<int> res = v;
for (int i = 0; i < res.size(); ++i) {
int t = i + rand() % (res.size() - i);
swap(res[i], res[t]);
}
return res;
}
private:
vector<int> v;
};
我的题解(2)
看到这题的第一反应,我去搞了一个最小值优先栈。。。。。
搞完之后发现啊,是我看题目不认真了。
当我反应过来的时候,我就换了个思路:
用指针标记当前最小值,并将在最小值指针更换时入栈。
看图:
后来嫌代码太长了,我又换了一套:双栈。普通栈和最小值优先栈并行。
然后:
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
cur.push(x);
int min;
if (!min_p.empty() && min_p.top() <= x){
min = min_p.top();
}
else
{
min = x;
}
min_p.push(min);
}
void pop() {
cur.pop();
min_p.pop();
}
int top() {
return cur.top();
}
int getMin() {
return min_p.top();
}
stack<int> cur;
stack<int> min_p;
};
这样也省的高一堆标志啊、判断啊之类的。又不差那一点空间。
官方题解(1)
方法二: Fisher-Yates 洗牌算法 【通过】
思路
我们可以用一个简单的技巧来降低之前算法的时间复杂度和空间复杂度,那就是让数组中的元素互相交换,这样就可以避免掉每次迭代中用于修改列表的时间了。
算法
Fisher-Yates 洗牌算法跟暴力算法很像。在每次迭代中,生成一个范围在当前下标到数组末尾元素下标之间的随机整数。接下来,将当前元素和随机选出的下标所指的元素互相交换 - 这一步模拟了每次从 “帽子” 里面摸一个元素的过程,其中选取下标范围的依据在于每个被摸出的元素都不可能再被摸出来了。此外还有一个需要注意的细节,当前元素是可以和它本身互相交换的 - 否则生成最后的排列组合的概率就不对了。
作者:LeetCode
链接:https://leetcode-cn.com/problems/shuffle-an-array/solution/da-luan-shu-zu-by-leetcode/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
private int[] array;
private int[] original;
Random rand = new Random();
private int randRange(int min, int max) {
return rand.nextInt(max - min) + min;
}
private void swapAt(int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public Solution(int[] nums) {
array = nums;
original = nums.clone();
}
public int[] reset() {
array = original;
original = original.clone();
return original;
}
public int[] shuffle() {
for (int i = 0; i < array.length; i++) {
swapAt(i, randRange(i, array.length));
}
return array;
}
}
> 作者:LeetCode
> 链接:https://leetcode-cn.com/problems/shuffle-an-array/solution/da-luan-shu-zu-by-leetcode/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
官方题解(2)
很遗憾,木有。不过各大数构书算法书里都有,评论区的大神们也有不少用我那套辅助栈的方式,所以看我的吧。