力扣刷題筆記(12)

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)

很遺憾,木有。不過各大數構書算法書裏都有,評論區的大神們也有不少用我那套輔助棧的方式,所以看我的吧。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章