日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

面经算法题手撕补充

發布時間:2023/12/20 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面经算法题手撕补充 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

Perfect Squares 完全平方數

合并K個鏈表(hard)

相同原理:最小區間原題

LRU Cache(雙向鏈表+hash map)

查找兄弟單詞

快速冪

找出字符數組中出現次數最多的字符

給你一個數組,求一個k值,使得前k個數的方差 + 后面n-k個數的方差最小 。

我手中有一堆撲克牌, 但是觀眾不知道它的順序。

兩個排序好的數組,找第k小的數字,要求logn的時間復雜度,二分搜索變形。

兩個排好序的數組,找中位數。

蓄水池抽樣:給出從n個數中隨機選擇1個的方法。注意,n非常大,并且一開始不知道其具體值。數字是一個一個給你的,當給完之后,你必須立刻給出隨機的結果。

蓄水池采樣應用:

鏈表隨機數 Linked List Random Node

完全二叉樹最后一個節點

字典序數字 Lexicographical Numbers

找字典序的第k個數 K-th Smallest in Lexicographical Order

最少移動次數01序列

最長AB子串

數組兩兩元素求異或,求最大的異或值(這題沒來得及寫)

最大全1矩形

全1連通塊數目

最小覆蓋子串

最長連續序列

LeetCode40 最小缺失正數


Perfect Squares 完全平方數

問題描述:給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, …)使得它們的和等于 n。你需要讓組成和的完全平方數的個數最少。

Example 1:

Input: n = 12Output: 3 Explanation: 12 = 4 + 4 + 4.

Example 2:

Input: n = 13Output: 2 Explanation: 13 = 4 + 9.

思路1:

dp

class Solution { public:int numSquares(int n) {vector<int> dp(n+1,INT_MAX);dp[0] = 0;// 從i = 0開始,第一遍可以把所有平方數最少次數標為1for(int i = 0;i < n;i++){for(int j = 1;i+j*j <= n;j++){dp[i+j*j] = min(dp[i+j*j],dp[i]+1);}}return dp[n];} };

?

思路2:

根據四平方和定理,任意一個正整數均可表示為4個整數的平方和,其實是可以表示為4個以內的平方數之和,那么就是說返回結果只有 1,2,3 或4其中的一個

首先我們將數字化簡一下,由于一個數如果含有因子4,那么我們可以把4都去掉,并不影響結果,比如2和8,3和12等等,返回的結果都相同,讀者可自行舉更多的栗子。

還有一個可以化簡的地方就是,如果一個數除以8余7的話,那么肯定是由4個完全平方數組成,這里就不證明了,因為我也不會證明,讀者可自行舉例驗證。

那么做完兩步后,一個很大的數有可能就會變得很小了,大大減少了運算時間,下面我們就來嘗試的將其拆為兩個平方數之和,如果拆成功了那么就會返回1或2,因為其中一個平方數可能為0. (注:由于輸入的n是正整數,所以不存在兩個平方數均為0的情況)。注意下面的?!!a + !!b?這個表達式,可能很多人不太理解這個的意思,其實很簡單,感嘆號!表示邏輯取反,那么一個正整數邏輯取反為0,再取反為1,所以用兩個感嘆號!!的作用就是看a和b是否為正整數,都為正整數的話返回2,只有一個是正整數的話返回1

class Solution { public:int numSquares(int n) {while(n % 4==0) n = n/4;if(n % 8 == 7) return 4;// a可以從0開始,n本身就是平方數情況for(int a = 0;a*a <= n;a++){int b = sqrt(n-a*a);if(a*a+b*b == n){return !!a+!!b;}}return 3;} };

?

合并K個鏈表(hard)

Input: [1->4->5,1->3->4,2->6 ] Output: 1->1->2->3->4->4->5->6

問題:

N個目標怎樣最快速的兩兩合并

不管合并幾個,基本還是要兩兩合并,兩個先合并,合并好了再跟第三個,然后第四個直到第k個。這樣的思路是對的,但是效率不高(時間復雜度為O(nk*nk)

思路:

?

使用堆:

把k個鏈表開頭的值排個序,每次取最小的一個值放到答案鏈表中,這次取完之后更新這個值為它后面的一個值。接著這么取一直到全部取完。

那么每次更新之后怎么對當前這k個值重新排序以便知道當前最小的是誰呢?用優先隊列(或者堆)來維護這k個值就好啦!

/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ struct cmp{bool operator()(ListNode* h1,ListNode* h2){return h1->val > h2->val;} };class Solution { public:ListNode* mergeKLists(vector<ListNode*>& lists) {if(lists.empty()) return NULL;if(lists.size() < 2) return lists[0];priority_queue<ListNode*,vector<ListNode*>,cmp> q;ListNode* dummy_head = new ListNode(-1);ListNode* pre = dummy_head,*cur;for(auto &node:lists){if(node) q.push(node);}while(!q.empty()){cur = q.top(); q.pop();pre->next = cur;pre = pre->next;if(cur->next) q.push(cur->next);}return dummy_head->next;} };

復雜度分析:

由于每個值都要取一次,一共取nk次。每次更新優先隊列要logk的復雜度。所以總時間復雜度為O(nklogk);

空間復雜度為優先隊列所占空間,為O(k)。

?

2.遞歸

將原鏈表分成兩段,然后對每段調用遞歸函數,suppose返回的left和right已經合并好了,然后再對left和right進行合并

class Solution { public:ListNode* mergeKLists(vector<ListNode*>& lists) {if(lists.empty()) return NULL;return mergeNLists(lists,0,lists.size()-1);}ListNode* mergeNLists(vector<ListNode*>& lists,int l,int r) {if(l > r) return NULL;if(l == r) return lists[l];if(l+1 == r) return merge(lists[l],lists[r]);else{int mid = (l+r)/2;ListNode* left = mergeNLists(lists,l,mid);ListNode* right = mergeNLists(lists,mid+1,r);return merge(left,right);} }// 合并兩個鏈表ListNode* merge(ListNode* h1,ListNode* h2){ListNode* dummy_head = new ListNode(-1);ListNode* cur = dummy_head;while(h1 && h2){if(h1->val < h2->val){cur->next = h1;h1=h1->next;}else{cur->next = h2;h2=h2->next;}cur=cur->next;}if(h1) cur->next = h1;if(h2) cur->next = h2;return dummy_head->next;} };

復雜度:

時間復雜度:

每合并一次,需要nk時間復雜度

遞歸方法需要合并logk次

故總時間復雜度:nklogk

空間復雜度:

棧深度:logk

?

相同原理:最小區間原題

k個有序的數組,找到最小的區間范圍使得這k個數組中,每個數組至少有一個數字在這個區間范圍內。比如:

  • 數組1:[4, 10, 15, 24, 26]
  • 數組2:[0, 9, 12, 20]
  • 數組3:[5, 18, 22, 30]

最小的區間是[20, 24],這個區間包含了數組1中的24,數組2中的20,數組3中的22

思路:

用多路歸并,每次比較每個數組k個的頭組成的區間大小

?

LRU Cache(雙向鏈表+hash map)

題意:

為LRU Cache設計一個數據結構,它支持兩個操作:

(1)get(key):如果key在cache中,則返回對應的value值,否則返回-1

(2)set(key,value):如果key不在cache中,則將該(key,value)插入cache中(注意,如果cache已滿,則必須把最近最久未使用的元素從cache中刪除);如果key在cache中,則重置value的值。

思路:

數組:

用什么數據結構來實現LRU算法呢?可能大多數人都會想到:用一個數組來存儲數據,給每一個數據項標記一個訪問時間戳,每次插入新數據項的時候,先把數組中存在的數據項的時間戳自增,并將新數據項的時間戳置為0并插入到數組中。每次訪問數組中的數據項的時候,將被訪問的數據項的時間戳置為0。當數組空間已滿時,將時間戳最大的數據項淘汰。

這種實現思路很簡單,但是有什么缺陷呢?需要不停地維護數據項的訪問時間戳,另外,在插入數據、刪除數據以及訪問數據時,時間復雜度都是O(n)。

鏈表:

  那么有沒有更好的實現辦法呢?

  那就是利用雙向鏈表和hashmap(雙向使得刪除的復雜度為O(1))

(1)插入新的數據項:

如果新數據項在鏈表中存在(一般稱為命中),則把該節點移到鏈表尾部,且如果value值不同,修改對應value值

如果不存在,則新建一個節點,放到鏈表尾部

若緩存滿了,則把鏈表第一個節點刪除即可。

(2)訪問數據時:

如果數據項在鏈表中存在,則把該節點移到鏈表尾部,否則返回-1。

這樣一來在鏈表頭部的節點就是最近最久未訪問的數據項。

復雜度:均為O(1)

struct Node{int key,value;Node *pre,*next;Node(int x,int y){key = x;value = y;pre = NULL;next = NULL;} };class LRUCache { public:LRUCache(int cap) {capacity = cap;// 雙向鏈表// 頭指向最不常使用節點(滿時從頭的next開始刪除)// 尾的pre指向最常使用節點head->next = tail;tail->pre = head;}int get(int key) {// 沒找到if(m.find(key) == m.end()){return -1;}// 找到Node *cur = get_cur(m[key]);move_to_tail(cur);return m[key]->value;}void put(int key, int value) {// 已經存在key,只是value的值需要變更if(m.find(key) != m.end()){m[key]->value = value;Node *cur = get_cur(m[key]);move_to_tail(cur);return;}// 已滿,刪除最不常使用節點(head指向節點)else if(m.size() >= capacity){m.erase(head->next->key);head->next = head->next->next;head->next->pre = head;}Node *new_node = new Node(key,value);m[key] = new_node;move_to_tail(new_node);}private:int capacity;unordered_map<int,Node*> m;Node *head = new Node(-1,-1);Node *tail = new Node(-1,-1);// 將一個節點從雙向鏈表摘下Node * get_cur(Node *cur){cur->pre->next = cur->next;cur->next->pre = cur->pre;return cur;}// 將摘下節點移到最末尾(表示它是最新的)void move_to_tail(Node *cur){cur->pre = tail->pre;cur->pre->next = cur;tail->pre = cur;cur->next = tail;} };

?

查找兄弟單詞

live, evil, veil 這些字母順序不同的詞定義為 brother words,

給定一個詞典, 編程輸出所有的 brother words(以字典序輸出)。

#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std;bool isBrother(string s1,string s2){if(s1.size() != s2.size()) return false;if(s1 == s2) return false;sort(s1.begin(),s1.end());sort(s2.begin(),s2.end());return s1==s2; }int main(){int n;while(cin >> n){// 注意這些參數寫在里面初始化,因為傳參形式是循環調用int idx;string str,word,idx_word;vector<string> vs;for(int i = 0;i < n;i++){cin >> str;vs.push_back(str);}cin >> word;cin >> idx;// 字典排序,為了輸出要求sort(vs.begin(),vs.end());// 逐個判斷int count = 0;for(auto &s:vs){if(isBrother(s,word)){count++;if(count == idx){idx_word = s;}}}if(!vs.empty()) cout << count << endl;if(count >= idx){cout << idx_word << endl;}}return 0; }

?

快速冪

1.O(n)做法

double my_power(double x,int n){double res = 1;for(int i = 0;i < n;i++){res *= x;}return res; }

2.O(logn)做法

double my_power(double x,int n){// remind從x的一次開始,不斷翻倍,x的2,x的4,x的8,理由是每個n都可以被拆成二進制數double res = 1,remind = x;while(n){if(n & 1) res *= remind;n = n >> 1;remind *= remind;}return res; }

?

找出字符數組中出現次數最多的字符

思路:

哈希表映射一次,計數

int findMost(vector<int> nums){if(nums.empty()) return -1;unordered_map<int,int> m;int max_count = 0,max_num;for(auto &n:nums){if(m.count(n)) m[n]++;else m[n] = 1;}for(auto iter = m.begin();iter != m.end();iter++){if(iter->second > max_count){max_count = iter->second;max_num = iter->first;}}return max_num; }

?

給你一個數組,求一個k值,使得前k個數的方差 + 后面n-k個數的方差最小 。

思路:

如 nums = [1,3,2,4]

運用公式D(x) = E(x*x)-(E(X))^2

我們可以先從左向右求出各個子段和各子段平方和,然后再從右向左求出各個子段和和各子段平方和,運用公式我們只需要正反遍歷數組兩次,就可以求得結果。

?

手中有一堆撲克牌, 但是觀眾不知道它的順序。

第一步, 我從牌頂拿出一張牌, 放到桌子上。

第二步, 我從牌頂再拿一張牌, 放在手上牌的底部。

第三步, 重復前兩步的操作, 直到我手中所有的牌都放到了桌子上。

最后, 觀眾可以看到桌子上牌的順序是:13\12\11\10\9\8\7\6\5\4\3\2\1

請問, 我剛開始拿在手里的牌的順序是什么?請用一個完整的函數來實現這一過程

正向過程:??????????手?????????????????????????????????????桌子

????????????????????????1->2->3->4

?????????????????????????????2->3->4????????????????????????????1

???????????????????????????3->4->2????????????????????????????????1

????????????????????????????4->2?????????????????????????????????????3->1

?????????????????????????????2->4????????????????????????????????????3->1

??????????????????????????????4???????????????????????????????????????2->3->1

???????????????????????????????????????????????????????????????????????4->2->3->1

最后觀眾看牌順序是4231,因為最先放在桌子上的在牌底

算法思想在于:
從手到桌子上進行兩步操作:

手中第一張放在桌子上
手中第一張放在牌底


所以如果逆向思維:
1.手中最后一張放在第一張(此步需要考慮手中的牌是否大于1)
2.桌子上拿一張放在手中第一張

反向操作:??????手???????????????????????????????????????????????桌子

??????????????????????4????????????????????????????????????????????????2->3->1

??????????????????????2->4?????????????????????????????????????????????3->1

?????????????????????4->2??????????????????????????????????????????????3->1

?????????????????????3->4->2???????????????????????????????????????????1

?????????????????????2->3->4????????????????????????????????????????????1

??????????????????????1->2->3->4

?

在這里是否要使用數組來完成?

1、數組概念是一組連續的內存空間,存儲相同的類型的數據,那么對于這么多的數據移動操作來說,數組需要不停的重新分配的新的內存空間,對于內存來說不是很友好

2、LinkedList的本質是一個雙向鏈表,鏈表中對內存空間的連續性并沒有要求,且中間結點的增加與刪除,那效率優于數組,而且本身提供了pollFirst?和?pollLast方法

基于以上兩點,鏈表更適合當前這個場景

代碼實現思路:

寫出上面的過程后,根據逆過程去看怎樣還原,這里我用了雙端隊列來做

deque<int> get_hand_pocker(vector<int> pockers){deque<int> hand_pockers;int cur;for(auto &pocker:pockers){//如果大于一張,則把手牌的最后一張放在最上面if(hand_pockers.size() > 1){cur = hand_pockers.back();hand_pockers.pop_back();hand_pockers.push_front(cur);}//從桌子上拿一張牌放在手上hand_pockers.push_front(pocker);}return hand_pockers; }int main(){vector<int> num = {4,2,3,1};deque<int> res = get_hand_pocker(num);for(auto &n:res) cout << n ;cout << endl; }

?

兩個排序好的數組,找第k小的數字,要求logn的時間復雜度,二分搜索變形。

兩個排好序的數組,找中位數。

class Solution { public:double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {int m = nums1.size(),n = nums2.size();return (findKth(nums1,0,nums2,0,(m+n+1)/2)+findKth(nums1,0,nums2,0,(m+n+2)/2))/2.0;}int findKth(vector<int>& nums1,int i, vector<int>& nums2,int j,int k){if(i >= nums1.size()) return nums2[j+k-1];if(j >= nums2.size()) return nums1[i+k-1];if(k == 1) return min(nums1[i],nums2[j]);int mid1,mid2;if(k/2 > nums1.size()) mid1 = INT_MAX;else mid1 = nums1[i+k/2-1];if(k/2 > nums2.size()) mid2 = INT_MAX;else mid2 = nums2[j+k/2-1];if(mid1 > mid2) j = j+k/2;else i=i+k/2;k -= k/2;return findKth(nums1,i,nums2,j,k);} };

?

?

蓄水池抽樣:給出從n個數中隨機選擇1個的方法。注意,n非常大,并且一開始不知道其具體值數字是一個一個給你的,當給完之后,你必須立刻給出隨機的結果。

由于n非常大并且需要立即給出答案,因此無法把所有數字都保存起來然后進行隨機。

再者n的具體值未知,因此任意時刻都需要有當前的結果。

?

于是第1個數字肯定選。那么問題就變成了當第i個數字到來時,是保留當前數字還是選擇第i個數字的問題,i=2,3,...,n。此過程必須保證截止目前為止所見到的數字被選中的概率都相等。

假設當第i個數字到來時,前i-1個數字被選中的概率相等,此時只需要第i個數字被選中的概率為1/i即可保證當前的i個數字被選中的概率相等。

?

證明:

代碼實現:

?

蓄水池采樣應用:

鏈表隨機數 Linked List Random Node

/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:/** @param head The linked list's head.Note that the head is guaranteed to be not null, so it contains at least one node. */Solution(ListNode* head) {this->head = head;}/** Returns a random node's value. */// 蓄水池采樣,每次以1/len的概率替換res為當前數int getRandom() {int res = head->val,len = 2;ListNode* cur = head->next;while(cur){int i = rand() % len;if(i == 0) res = cur->val;len++;cur = cur->next;}return res;}private:ListNode* head; };

?

完全二叉樹最后一個節點

完全二叉樹和滿二叉樹Full Binary Tree的唯一區別是,完全二叉樹最后一層的節點不滿,而且假設最后一層有節點,都是從左邊開始。 這樣我們可以利用這個性質得到下面兩個結論:

  • 假如左子樹高度等于右子樹高度,則右子樹為完全二叉樹,左子樹為滿二叉樹。
  • 假如高度不等,則左子樹為完全二叉樹,右子樹為滿二叉樹。
  • 高度的時候只往左子樹來找
  • Time Complexity - O(logn * logn), Space Complexity - O(1) (不考慮遞歸棧)

    /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/ class Solution { public:TreeNode* getLastNode(TreeNode* root) {if(!root) return NULL;TreeNode* cur = root;int left,right;// 二分查找while(cur){if(!cur->left && !cur->right) return cur;left = get_height(cur->left);right = get_height(cur->right);// 左子樹為滿二叉樹,最后節點在右子樹if(left == right) cur = cur->right;// 最后節點在左子樹else cur = cur->left;}return NULL;}int get_height(TreeNode* root){TreeNode* cur = root;int level = 0;while(cur){level++;cur = cur->left;}return level;} };

    ?

    字典序數字 Lexicographical Numbers

    Given an integer?n, return 1 -?n?in lexicographical order.

    For example, given 13, return: [1,10,11,12,13,2,3,4,5,6,7,8,9].

    Please optimize your algorithm to use less time and space. The input size may be as large as 5,000,000.

    以780為例

    1,10,100,101...109,11,110,111...119,12,120,121..129...199

    class Solution { public:vector<int> lexicalOrder(int n) {vector<int> res(n);int cur = 1;// 順序生成for(int i = 0;i < n;i++){res[i] = cur;if(cur*10 <= n) cur *= 10;else{if(cur >= n) cur /= 10;cur += 1;while (cur % 10 == 0) cur /= 10;}}return res;} };

    ?

    找字典序的第k個數 K-th Smallest in Lexicographical Order

    其實這是個十叉樹Denary Tree,就是每個節點的子節點可以有十個,比如數字1的子節點就是10到19,數字10的子節點可以是100到109,但是由于n大小的限制,構成的并不是一個滿十叉樹。

    我們分析題目中給的例子可以知道,數字1的子節點有4個(10,11,12,13),而后面的數字2到9都沒有子節點,那么這道題實際上就變成了一個先序遍歷十叉樹的問題,那么難點就變成了如何計算出每個節點的子節點的個數,我們不停的用k減去子節點的個數,當k減到0的時候,當前位置的數字即為所求

    現在我們來看如何求子節點個數,比如數字1和數字2,我們要求按字典遍歷順序從1到2需要經過多少個數字,首先把1本身這一個數字加到step中,然后我們把范圍擴大十倍,范圍變成10到20之前,但是由于我們要考慮n的大小,由于n為13,所以只有4個子節點,這樣我們就知道從數字1遍歷到數字2需要經過5個數字,然后我們看step是否小于等于k,如果是,我們cur自增1,k減去step;如果不是,說明要求的數字在子節點中,我們此時cur乘以10,k自減1,以此類推,直到k為0推出循環,此時cur即為所求

    class Solution { public:int findKthNumber(int n, int k) {int cur = 1;--k;while(k > 0){long long step = 0,first = cur,last = cur+1;// 遍歷1開頭樹,2開頭樹。。。while(fisrt < n){step += min((long long)n+1,last)-first;first *= 10;last *= 10;}// 1開頭樹數量小于kif(step <= k){++cur;k -= step;} // 否則,在樹內,找10開頭的數else{cur *= 10;--k;}}} };

    ?

    最少移動次數01序列

    問題描述:

    給定一個01序列串,現在需要將這個字符串改為“非遞減“有序序列,請問其最小交換次數(任意兩個位置可互換)?

    分析:此題要求將01串按照非遞減順序排序的最小交換次數,只能是在需要交換的時候才交換,也就是將所有0移到左邊,所有1放到右邊所需的最小交換次數。可以利用雙指針來處理。

    ?

    最長AB子串

    給定一個數組,數組中只包含0和1。請找到一個最長的子序列,其中0和1的數量是相同的。
    例1:10101010 結果就是其本身。
    例2:1101000 結果是110100
    請大家展開自己的思路。

    解:
    這個題目,看起來比較簡單,一些同學可能認為題目的描述符合動態規劃的特征,然后就開始用動態規劃解,努力找狀態轉移方程。這些同學的感覺,是很正確的。但,找狀態轉移方程,我們要對原來的數組進行變換一下。

    原來是0和1的串,我們將0都換為-1。這樣題目目標就變成,找到一個最長的子串,子串數字和是0。設原數組為A, DP[i]表示從0開始到i的子數組和。DP遍歷一遍數組即可。例1中的數組產生的DP為:

    0?? ?1?? ?2?? ?3?? ?4?? ?5?? ?6?? ?7
    1?? ?0?? ?1?? ?0?? ?1?? ?0?? ?1?? ?0
    這個例子,最后一個值是0,并且長度是偶數位。直接滿足了結果。

    再看例子2:

    0?? ?1?? ?2?? ?3?? ?4?? ?5?? ?6
    1?? ?2?? ?1?? ?2?? ?1?? ?0?? ?-1
    5的位置為0,最長子串從0開始到5,長度為6。

    上面這兩個例子,所求的子串都是從頭開始,如果不是從頭開始,會是什么樣的呢?看這個例子:1101100

    0?? ?1?? ?2?? ?3?? ?4?? ?5?? ?6
    1?? ?2?? ?1?? ?2?? ?3?? ?2?? ?1
    通過觀察上面的表格,我們可以得到,DP[0]==DP[6]==DP[2],DP[1]==DP[3]. 根據DP的定義,如果DP[i]==DP[j],i 一種方法,我們用map保存DP的值到位置的映射,如下表:

    我們最終的算法,要綜合考慮最常穿是否從頭開始的。 上面的這個思路,時間復雜度是O(n),空間復雜度也是O(n).

    class Solution:"""@param S: a String consists of a and b@return: the longest of the longest string that meets the condition"""def getAns(self, S):# Write your code hereans = 0#定義一個字典,key是AB之間的差距,val是在字符對應坐標D = {0: -1}if not S:return anscnt = 0for i in range(len(S)):if S[i] == 'A':cnt += 1else:cnt -= 1#當cnt在字典中的時候,證明該情況出現過,i - D[cnt]之間的字符,AB的數量是相等的,if cnt in D:ans = max(ans, i - D[cnt])else:D[cnt] = ireturn ans

    拓展:

    升級版abc三個字符咋辦,還是on,思考了6分鐘我說思路,延續前面思路,要按(ab,ac)的差值來哈希和查找,是不是On我還得思考下細節

    ?

    數組兩兩元素求異或,求最大的異或值(這題沒來得及寫)

    用 Trie 來做,首先把所有的數的二進制存到 Trie 里面去,然后對于數組中的每個數 x,和 x 一起異或結果最大的 y 就是用 x 的二進制的反碼在Trie 里面搜索盡可能的與 x 的反碼匹配,這樣當走到葉子節點時,葉子節點對應的數就是 y。然后遍歷一遍數組,求出 max(x ^ y)

    class Solution(object):def findMaximumXOR(self, nums):Trie = {}for x in nums:cur = Triefor bit in format(x, '032b'):if bit not in cur:cur[bit] = {}cur = cur[bit]cur[''] = xdef matchMaxXOR(x):cur = Triefor bit in format(x, '032b'):rev = '0' if bit == '1' else '1'if rev in cur:cur = cur[rev]else:cur = cur[bit]return cur['']res = 0for x in nums:res = max(res, x ^ matchMaxXOR(x))return res

    ?

    最大全1矩形

    Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.

    Example:

    Input: [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"] ] Output: 6

    思路:這道題的二維矩陣每一層向上都可以看做一個直方圖,輸入矩陣有多少行,就可以形成多少個直方圖,對每個直方圖都調用?Largest Rectangle in Histogram 直方圖中最大的矩形?中的方法,就可以得到最大的矩形面積。

    (1)那么這道題唯一要做的就是將每一層構成直方圖,由于題目限定了輸入矩陣的字符只有 '0' 和 '1' 兩種,所以處理起來也相對簡單。方法是,對于每一個點,如果是‘0’,則賦0,如果是 ‘1’,就賦 之前的height值加上1。

    (2)對每一行調用計算最大直方圖

    class Solution { public:int maximalRectangle(vector<vector<char>>& matrix) {if(matrix.empty() || matrix[0].empty()) return 0;int n = matrix.size(),m = matrix[0].size(),max_rec = 0;vector<vector<int>> height(n,vector<int>(m));// 計算高度for(int i = 0;i < n;i++){for(int j = 0;j < m;j++){if(matrix[i][j] == '1'){height[i][j] = i == 0? 1:height[i-1][j]+1;}else height[i][j] = 0;}}// 每一行都可以調用計算直方圖的最大矩形for(int i = 0;i < n;i++){max_rec = max(max_rec,getMaxRectangle(height[i]));}return max_rec;}int getMaxRectangle(vector<int> row){int n = row.size();int max_rec = 0,minH;for(int i = 0;i < n;i++){while(i < n-1 && row[i+1] >= row[i]) i++;minH = row[i];for(int j = i;j >= 0;j--){minH = min(row[j],minH);max_rec = max(max_rec,minH*(i-j+1));}}return max_rec;} };

    ?

    全1連通塊數目

    Given a 2d grid map of?'1's (land) and?'0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

    Example 1:

    Input: 11110 11010 11000 00000Output:?1

    Example 2:

    Input: 11000 11000 00100 00011Output: 3

    思路:DFS或者BFS都可以實現

    class Solution { public:int numIslands(vector<vector<char>>& grid) {if(grid.empty() || grid[0].empty()) return 0;int m = grid.size(),n = grid[0].size(),cnt = 0;vector<vector<bool>> visited(m,vector<bool>(n,false));for(int i = 0;i < m;i++){for(int j = 0;j < n;j++){if(grid[i][j] == '1' && visited[i][j] == false) {cnt++;bfs(i,j,grid,visited);}}}return cnt;}void bfs(int i,int j,vector<vector<char>>& grid,vector<vector<bool>> &visited){if(i < grid.size() && j < grid[0].size() && grid[i][j] == '1' && visited[i][j] == false){visited[i][j] = true;bfs(i-1,j,grid,visited);bfs(i+1,j,grid,visited);bfs(i,j-1,grid,visited);bfs(i,j+1,grid,visited);}} };

    ?

    最小覆蓋子串

    給你一個字符串 S、一個字符串 T,請在字符串 S 里面找出:包含 T 所有字母的最小子串

    示例:

    輸入: S = "ADOBECODEBANC", T = "ABC"
    輸出: "BANC"
    說明:

    如果 S 中不存這樣的子串,則返回空字符串 ""。
    如果 S 中存在這樣的子串,我們保證它是唯一的答案。

    思路:

    這題要注意T里面的字符可能有多次出現,不能用set而要用map去存

    class Solution { public:string minWindow(string s, string t) {string res;if(s.empty() || t.empty() || s.size() < t.size()) return res;// 使用字典存而不是set,因為t可能內部字符有重復unordered_map<char,int> word_map;int left = 0,cnt = 0,min_len = INT_MAX;for(auto &w:t) word_map[w]++;for(int i = 0;i < s.size();i++){if(word_map[s[i]] > 0) cnt++;word_map[s[i]]--;while(cnt == t.size()){if(min_len > i-left+1){min_len = i-left+1;// substr[begin,seq_len]注意!!!不是[begin,end]res = s.substr(left,min_len);}if(++word_map[s[left]] > 0) cnt--;left++;}}return res;} };

    ?

    最長連續序列

    Given an unsorted array of integers, find the length of the longest consecutive elements sequence.

    Your algorithm should run in O(n) complexity.

    Example:

    Input:?[100, 4, 200, 1, 3, 2] Output: 4 Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

    思路:時間要求在O(1),所以必須用額外的空間換時間

    class Solution { public:int longestConsecutive(vector<int>& nums) {if(nums.size() < 2) return nums.size();unordered_set<int> num_set(nums.begin(),nums.end());int max_len = 0;for(auto val:nums){if(!num_set.count(val)) continue;num_set.erase(val);int left = val-1,right = val+1;// 如果能找到左右界,持續往前往后找,直到把這個連續串窮舉完while(num_set.find(left)) num_set.erase(left--);while(num_set.find(right) num_set.erase(right++);max_len = max(max_len,right-left-1);}return max_len;} };

    ?

    LeetCode40 最小缺失正數

    Given an unsorted integer array, find the smallest missing?positive integer.

    Example 1:

    Input: [1,2,0] Output: 3

    Example 2:

    Input: [3,4,-1,1] Output: 2

    Example 3:

    Input: [7,8,9,11,12] Output: 1

    Note:

    Your algorithm should run in?O(n) time and uses constant extra space.

    思路:事實上,缺失的最小正數,肯定在1到n+1之間,故這里用了下標信息

    class Solution { public:int firstMissingPositive(vector<int>& nums) {if(nums.empty()) return 1;// 事實上,缺失的最小正數,肯定在1到n+1之間int n = nums.size(),miss = n+1;// 把每個數如果能歸到它對應的順序位置,之間歸過去for(int i = 0;i < n;i++){if(nums[i] > 0 && nums[i] < n && nums[nums[i]-1] != nums[i]){swap(nums[i],nums[nums[i]-1]);i--;}}// 第一個沒有放對應正數的,即為缺失正數for(int i = 0;i < n;i++){if(nums[i] != i+1){miss = i+1;break;} }return miss;} };

    ?

    ?

    (0-1背包)--- 改變一組數的正負號使得它們的和為一給定數

    You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols?+?and?-. For each integer, you should choose one from?+?and?-?as its new symbol.

    Find out how many ways to assign symbols to make sum of integers equal to target S.

    Example 1:

    Input: nums is [1, 1, 1, 1, 1], S is 3. Output: 5 Explanation: -1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3There are 5 ways to assign symbols to make the sum of nums be target 3.

    思路:

    sum(P) - sum(N) = target sum(P) + sum(N) = sum

    ??于是有sum(P) = (target + sum) / 2

    那么不妨這樣理解題意,從一個數組中選定一些數,使它們的和為sum(P),如此就變成了很經典的0/1背包問題,從一個n大小的背包中選出總和為sum(P)的方案個數。

    ??狀態表示:dp[i] [j]代表前i個數中和為j的方案個數。
    ??狀態轉移方程:dp [i] [j] = dp[i-1] [j] + dp[i-1] [j-nums[i]],dp [0] [0] = 1
    ??返回結果:dp[n] [target],n為數組大小,target為sum(P)。

    class Solution { public:int findTargetSumWays(vector<int>& nums, int S) {if(nums.empty()) return 0;int sum = 0;for(auto n:nums) sum += n;if((sum < S || (sum+S)%2)) return 0;int target = (sum+S)/2,n = nums.size();vector<vector<int>> dp(target+1,vector<int>(n+1,0));dp[0][0] = 1;for(int i = 0;i <= target;i++){for(int j = 1;j <= n;j++){int cur = nums[j-1];dp[i][j] = dp[i][j-1];if(cur <= i){dp[i][j] += dp[i-cur][j-1];}}}return dp[target][n];} };

    空間優化:

    class Solution { public:int findTargetSumWays(vector<int>& nums, int S) {if(nums.empty()) return 0;int sum = 0;for(auto n:nums) sum += n;if((sum < S || (sum+S)%2)) return 0;int target = (sum+S)/2,n = nums.size();vector<int> dp(target+1,0);dp[0] = 1;for(auto num:nums){for(int i = target;i >= num;i--){dp[i] = dp[i] + dp[i-num];}}return dp[target];} };

    ?

    最大時間

    Given an array of 4 digits, return the largest 24 hour time that can be made.

    The smallest 24 hour time is 00:00, and the largest is 23:59.? Starting from 00:00, a time is larger if more time has elapsed since midnight.

    Return the answer as a string of length 5.? If no valid time can be made, return an empty string.

    Example 1:

    Input: [1,2,3,4] Output: "23:41"

    Example 2:

    Input: [5,5,5,5] Output: ""

    思路:

    不要用規則去貪心枚舉!!!會有問題!!!

    反例:0,2,6,6

    規則枚舉會產生20:然后后面非法,就會返回空,事實上會有06:26

    這題應該用dfs去枚舉,一共才4! = 24種情況

    class Solution { public:vector<string> possible_res; string largestTimeFromDigits(vector<int>& A) {vector<bool> visited(4,false);string res;dfs(A,visited,"");if(possible_res.empty()) return res;sort(possible_res.begin(),possible_res.end());res = possible_res[possible_res.size()-1];res.insert(2,":");return res;}// 暴力枚舉void dfs(vector<int>& A,vector<bool>& visited,string out){if(out.size() == 4){if(isValid(out)) possible_res.push_back(out);return;}for(int i = 0;i < 4;i++){if(!visited[i]){visited[i] = true;dfs(A,visited,out + to_string(A[i]));visited[i] = false;}}}// 判斷是否合法bool isValid(string s){string s1 = s.substr(0,2);string s2 = s.substr(2,2);if(s1 >= "24" || s2 >= "60") return false;return true;} };

    ?

    最長連續線段

    https://blog.csdn.net/starstar1992/article/details/65745533

    ?

    射氣球(原理同上)

    There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided input is the start and end coordinates of the horizontal diameter. Since it's horizontal, y-coordinates don't matter and hence the x-coordinates of start and end of the diameter suffice. Start is always smaller than end. There will be at most 104?balloons.

    An arrow can be shot up exactly vertically from different points along the x-axis. A balloon with xstart?and xend?bursts by an arrow shot at x if xstart?≤ x ≤ xend. There is no limit to the number of arrows that can be shot. An arrow once shot keeps travelling up infinitely. The problem is to find the minimum number of arrows that must be shot to burst all balloons.

    Example:

    Input: [[10,16], [2,8], [1,6], [7,12]]Output: 2Explanation: One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] and [1,6]) and another arrow at x = 11 (bursting the other two balloons).

    思路:先給線段排序,然后O(n)遍歷就可以了

    class Solution { public:int findMinArrowShots(vector<vector<int>>& points) {if(points.size() < 2) return points.size();// pair排序默認就是按照第一個元素排的sort(points.begin(),points.end());int cnt = 1,first = points[0][0],second = points[0][1];for(int i = 1;i < points.size();i++){if(points[i][0] <= second){first = max(first,points[i][0]);second = min(second,points[i][1]);}else{cnt++;first = points[i][0];second = points[i][1];}}return cnt;} };

    ?

    一個鏈表,奇數位升序,偶數位降序,將其轉化成完全升序的鏈表

    直接拆分+反轉+歸并

    ?

    O(1)空間判斷整數是否為回文

    class Solution { public:bool isPalindrome(int x) {// 負數和末尾有0直接就不是回文if(x < 0) return false;if(x == 0) return true;if(x % 10 == 0) return false;// 拆分兩邊,判斷回文int half = 0;while(x > half){half = half * 10 + x % 10;x /= 10;}if(half == x) return true;half /= 10;return half == x;} };

    ?

    有序鏈表轉二叉搜索樹

    思路:

    快慢指針找到中點作為根節點,然后遞歸

    class Solution { public:TreeNode* sortedListToBST(ListNode* head) {if(!head) return NULL;// 找到中點ListNode* fast = head,*slow = head,*pre;while(fast && fast->next){pre = slow;slow = slow->next;fast = fast->next->next;}TreeNode* root = new TreeNode(slow->val);// 只有一個節點情況,直接返回if(!head->next) return root;// 否則就遞歸pre->next = NULL;root->left = sortedListToBST(head);root->right = sortedListToBST(slow->next);return root;} };

    ?

    logN解法

    def getSum(x,n):if n == 1:return x,xif n % 2 == 0:pre_sum,last = getSum(x,n/2)sum = pre_sum + pre_sum *lastlast = last*lastelse:pre_sum,last = getSum(x,n-1)last = last*xsum = pre_sum + lastreturn sum,lastprint(getSum(2,10)[0])

    ?

    給定一個數組,問是否能把這個數組一份為二,且兩個子數組和相同

    https://leetcode.com/problems/partition-equal-subset-sum/

    思路:

    數組中和為sum/2的組合數有多少種

    這里不需要dfs,因為只是種類數,這是一個典型的01背包題

    class Solution { public:bool canPartition(vector<int>& nums) {int m = nums.size();if(m == 0) return true;int sum = 0;for(auto n:nums) sum += n;if(sum&1) return false;sum /= 2;vector<vector<int>> dp(sum+1,vector<int>(m+1,0));dp[0][0] = 1;for(int i = 1;i <= sum;i++){for(int j = 1;j <= m;j++){if(nums[j-1] <= i){dp[i][j] = max(dp[i-nums[j-1]][j-1],dp[i][j-1]);}else{dp[i][j] = dp[i][j-1];}}}return dp[sum][m] > 0;} };

    ?

    判斷合法ip地址

    Given a string containing only digits, restore it by returning all possible valid IP address combinations.

    Example:

    Input: "25525511135" Output: ["255.255.11.135", "255.255.111.35"] class Solution(object):def __init__(self):self.res = []def restoreIpAddresses(self, s):""":type s: str:rtype: List[str]"""self.dfs(s,[],0)return self.resdef dfs(self,s,out,begin):if len(out) == 4:if begin == len(s):out = '.'.join(out)self.res.append(out)returnif begin >= len(s):return# 注意對0的特殊處理,因為01這種是非法的if s[begin] == '0':out.append('0')self.dfs(s,out,begin+1)else:for i in range(1,4):cur = s[begin:begin+i]if int(cur) >= 1 and int(cur) <= 255:self.dfs(s,out+[cur],begin+i)

    ?

    中文字符串,比如一千五百億八千九百萬六百二十這種形式,轉換成long long的整數,要我考慮很多非法輸入

    用棧來實現算法,N表示當前的數,如果數字則直接入棧;如果是百千萬億這種單位,就把棧中比N小的數彈出累加之后的和,乘上N再入棧。?
    最后把棧中元素累加就得出結果了。?

    如果不用棧也可以做,要記錄下最近最大的單位是多少,才能保存前面的累加和。如果一億三千二百萬,當掃描到萬的時候,要知道前面是三千二百,千的單位比萬小的情況,所以要把累加和變成 ” 三千二百 * 萬 ”,而不能只是 “二百 * 萬”,也不能是 ”一億三千二百 * 萬”

    def getNum(line):stack = []val1 = '零一二三四五六七八九'val2 = '十百千萬億'bases = [10,100,1000,10000,100000000]for s in line:# 數字if s in val1:stack.append(val1.index(s))# 不是數字else:s = val2.index(s)base = bases[s]sum = 0#while len(stack) > 0 and stack[-1] < base:cur = stack.pop()sum += curif sum > 0:sum *= baseelse:sum = basestack.append(sum)sum = 0for cur in stack:sum += curreturn sumif __name__ == '__main__':line = '一億二千三百八十萬九千'res = getNum(line)print(res)

    ?

    ?

    總結

    以上是生活随笔為你收集整理的面经算法题手撕补充的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。