LeetCode刷題記錄(三)
阿新 • • 發佈:2022-03-06
劍指 Offer 30. 包含min函式的棧
利用輔助桟s2來存放最小值,如果有比s2更小的則讓更小的進s2,當原s1中最小值出棧時,判斷如果同s2值相當,代表需要更新最小值,則s2棧頂出棧
class MinStack { public: stack<int>s1; stack<int>s2; /** initialize your data structure here. */ MinStack() { s2.push(INT_MAX); } void push(int x) { s1.push(x); if(x<=s2.top()){ s2.push(x); } } void pop() { if(s1.top()==s2.top()) s2.pop(); //s1出棧元素同s2棧頂儲存的最小值相等 s1.pop(); } int top() { return s1.top(); } int min() { return s2.top(); //s2的棧頂始終儲存最小值 } };
面試題32 - I. 從上到下列印二叉樹
二叉樹的層次遍歷 3,9,20,15,7依次入隊
class Solution { public: vector<int> levelOrder(TreeNode* root) { vector<int>v; if(root == NULL) return v; queue<TreeNode*>q; //初始化佇列 q.push(root); //根結點入隊 while(!q.empty()){ TreeNode * node = q.front(); //隊首出隊 q.pop(); v.push_back(node->val); if(node->left) q.push(node->left); //該結點的左孩子入隊 if(node->right) q.push(node->right); //右孩子入隊 } return v; } };
劍指 Offer 32 - II. 從上到下列印二叉樹 II
和上一題差不多一模一樣,就是多了一個分層輸出,怎麼分層:根結點入隊,佇列size()為1,根結點左右孩子入隊,佇列size()為2,假設為滿二叉樹,則再向下,size()為4.......一直這樣,每層有幾個結點就i=q.size(),再i--把每個結點輸出
class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { // 層次遍歷問題 vector<vector<int>> v; if(root==NULL){ return v; } queue<TreeNode *>q; q.push(root); while(!q.empty()){ //佇列非空時 vector<int>temp; for(int i=q.size();i>0;i--) //關鍵的分層的辦法 { TreeNode * node = q.front(); q.pop(); temp.push_back(node->val); if(node->left) q.push(node->left); if(node->right) q.push(node->right); } v.push_back(temp); //儲存這一層的結果 } return v; } };
劍指 Offer 32 - III. 從上到下列印二叉樹 III![image-20220306113134809]
方法一:和上一題一樣,但是用一個數字來記錄層數,然後奇數層正常,偶數層反轉一下就可以了
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
// 層次遍歷問題
vector<vector<int>> v;
if(root==NULL){
return v;
}
queue<TreeNode *>q;
q.push(root);
int deep = 1;
while(!q.empty()){//佇列非空時
vector<int>temp;
for(int i=q.size();i>0;i--)//關鍵的分層的辦法
{
TreeNode * node = q.front();
q.pop();
temp.push_back(node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
if(deep%2 ==0){
reverse(temp.begin(),temp.end());
}
v.push_back(temp);
deep++;
}
return v;
}
};
還可以用雙端佇列
// 使用雙端佇列 (樹的偶層: 尾入(先左子結點再右子結點)頭出; 樹的奇層: 頭入(先右子結點再左子結點)尾出)
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<vector<int>> vec;
deque<TreeNode*>dqe;
int level = 0;
dqe.push_back(root); // root結點在第0層(偶層),所以從佇列尾入
while (!dqe.empty()) {
int level_nodes = dqe.size();
vec.push_back({});
if (level % 2 != 0) { // 奇層: 從佇列頭入(先右子結點再左子結點),佇列尾出
while (level_nodes) {
TreeNode* p_node = dqe.back();
if (p_node->right != nullptr) dqe.push_front(p_node->right);
if (p_node->left != nullptr) dqe.push_front(p_node->left);
vec[level].push_back(p_node->val);
dqe.pop_back();
--level_nodes;
}
++level;
}
else { // 偶層: 從佇列尾入(先左子結點再右子結點),佇列頭出
while (level_nodes) {
TreeNode* p_node = dqe.front();
if (p_node->left != nullptr) dqe.push_back(p_node->left);
if (p_node->right != nullptr) dqe.push_back(p_node->right);
vec[level].push_back(p_node->val);
dqe.pop_front();
--level_nodes;
}
++level;
}
}
return vec;
}
};
88. 合併兩個有序陣列
就是說給兩個非遞減的陣列,然後讓你把第一個陣列的前n位和第二個陣列的前m位合併到一個數組1中(陣列1的長度為M+N)
逆序雙指標(雙指標的思想常用)
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = nums1.size() - 1;
m--; //下標從0開始,所以先減1
n--;
while (n >= 0) {
while (m >= 0 && nums1[m] > nums2[n]) {
nums1[i--]=nums1[m--];
}
nums1[i--]=nums2[n--];
}
}
劍指 Offer 39. 陣列中出現次數超過一半的數字
這題是408出現過的原題,最好想的是做統計,排序,但這題用的是投票法
因為一定出現眾數,每次出現眾數就+1,不是就-1,最後一定大於0
class Solution {
public:
int majorityElement(vector<int>& nums) {
int res = nums[0]; //先設nums[0]為眾數
int count = 1;
for(int i = 1;i<nums.size();i++){
if(nums[i]==res){ //值相等++
count++;
}else{ //值不等進一步判斷
if(count==1){ //僅剩1次,那麼將眾數更新
res = nums[i];
}else{
count--; //出現次數--
}
}
}
return res;
}
};
大佬的寫法:本題具有特殊性,即不用判斷是否為眾數,最好還是可以加上一個判斷
class Solution {
public:
int majorityElement(vector<int>& nums) {
int x = 0, votes = 0, count = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
// 驗證 x 是否為眾數
for(int num : nums)
if(num == x) count++;
return count > nums.size() / 2 ? x : 0; // 當無眾數時返回 0
}
};
劍指 Offer 40. 最小的k個數
4種解法秒殺TopK(快排/堆/二叉搜尋樹/計數排序)❤️ - 最小的k個數 - 力扣(LeetCode) (leetcode-cn.com)
劍指 Offer 40. 最小的 k 個數(基於快速排序的陣列劃分,清晰圖解) - 最小的k個數 - 力扣(LeetCode) (leetcode-cn.com)
1.快速選擇演算法
優化了的快速排序,因為只需要找出最小的k個,並不關心他們的順序
同快排一樣使用頻率很高
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
if(k>=arr.size()) return arr;
return quick_select(arr, k, 0, arr.size() - 1);
}
vector<int>quick_select(vector<int>&arr,int k,int low,int high){
int i = low;
int j = high;
int pivot = arr[i];//選定樞軸
while(i<j){
while(i<j && arr[j]>=pivot) --j;
arr[i] = arr[j] ;
while(i<j && arr[i]<=pivot) ++i;
arr[j] = arr[i];
}
arr[i] = pivot; //樞軸歸位
// 再判斷是否需要繼續,如果i>k,代表範圍大了,減小範圍
//若果i<k代表我們的範圍小了,需要再次劃分
if (i > k) return quick_select(arr, k, low, i - 1);
if (i < k) return quick_select(arr, k, i + 1, high);
vector<int> res;
res.assign(arr.begin(), arr.begin() + k); //返回前K個
return res;
}
};
414. 第三大的數
方法一:使用集合(預設排序),且僅儲存3個數
class Solution {
public:
int thirdMax(vector<int> &nums) {
set<int> s;
for (int num : nums) {
s.insert(num);
if (s.size() > 3) { //超過3個元素就刪除其中最小的
s.erase(s.begin());
}
}
return s.size() == 3 ? *s.begin() : *s.rbegin(); //不足3個數就返回最大的(例二的情況)
}
};
解法二:用三個指標,僅用一輪掃描就可以找到
class Solution {
public:
int thirdMax(vector<int> &nums) {
int *a = nullptr, *b = nullptr, *c = nullptr;
for (int &num : nums) {
if (a == nullptr || num > *a) {
c = b;
b = a;
a = #
} else if (*a > num && (b == nullptr || num > *b)) {
c = b;
b = #
} else if (b != nullptr && *b > num && (c == nullptr || num > *c)) {
c = #
}
}
return c == nullptr ? *a : *c;
}
};
215. 陣列中的第K個最大元素(這個條件又比上面的苛刻一些了)
解法一:還是直接快排
解法二:快速選擇
class Solution {
public:
int quickSelect(vector<int>& a, int l, int r, int index) {
int q = randomPartition(a, l, r);
if (q == index) {
return a[q];
} else {
return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index);
}
}
inline int randomPartition(vector<int>& a, int l, int r) {
int i = rand() % (r - l + 1) + l;//引入隨機化 快排的時間複雜度取決於劃分
swap(a[i], a[r]);
return partition(a, l, r);
}
inline int partition(vector<int>& a, int l, int r) {//劃分
int x = a[r], i = l - 1;
for (int j = l; j < r; ++j) {
if (a[j] <= x) {
swap(a[++i], a[j]);
}
}
swap(a[i + 1], a[r]);
return i + 1;
}
int findKthLargest(vector<int>& nums, int k) {
srand(time(0));
return quickSelect(nums, 0, nums.size() - 1, nums.size() - k);
}
};
解法三:利用大根堆
class Solution {
public:
void maxHeapify(vector<int>& a, int i, int heapSize) {
int l = i * 2 + 1, r = i * 2 + 2, largest = i;
if (l < heapSize && a[l] > a[largest]) {
largest = l;
}
if (r < heapSize && a[r] > a[largest]) {
largest = r;
}
if (largest != i) {
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
void buildMaxHeap(vector<int>& a, int heapSize) { //建堆操作
for (int i = heapSize / 2; i >= 0; --i) {
maxHeapify(a, i, heapSize);
}
}
int findKthLargest(vector<int>& nums, int k) {
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i) {
swap(nums[0], nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
};
keep real ,stay hungry ,and dream future