剑指 offer 编程题 C++ 版总结(中)
解題思路:從根結(jié)點(diǎn)開(kāi)始向下進(jìn)行對(duì)比,(1) 若兩邊的結(jié)點(diǎn)為空,返回 true。(2) 一個(gè)結(jié)點(diǎn)為空,而另一個(gè)結(jié)點(diǎn)不為空,此時(shí)二叉樹(shù)不是對(duì)稱(chēng)的,返回 false。(3) 兩邊結(jié)點(diǎn)都不為空,但是結(jié)點(diǎn)的值不相等,返回 false。當(dāng)這三種情況判斷完了后,開(kāi)始判斷左右子樹(shù)是否滿足對(duì)稱(chēng)條件。
/*** 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:bool isSymmetric(TreeNode* root) {return isSymmetric(root, root);}bool isSymmetric(TreeNode* T1, TreeNode* T2){if(T1 == NULL && T2 == NULL)return true;if(T1 == NULL || T2 == NULL)return false;if(T1->val != T2->val)return false;return isSymmetric(T1->left, T2->right) && isSymmetric(T1->right, T2->left);} };?
?
解題思路:可以將矩陣看成若干層,首先打印最外層的元素,其次打印次外層的元素,直到打印最內(nèi)層的元素。對(duì)于每層,從左上方開(kāi)始以順時(shí)針的順序遍歷所有元素。假設(shè)當(dāng)前層的左上角位于 (top,left),右下角位于 (bottom,right),按照如下順序遍歷當(dāng)前層的元素。
class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {if (matrix.size() == 0 || matrix[0].size() == 0) {return {};}int rows = matrix.size(), columns = matrix[0].size();vector<int> order;int left = 0, right = columns - 1, top = 0, bottom = rows - 1;while (left <= right && top <= bottom) {for (int column = left; column <= right; column++) {order.push_back(matrix[top][column]);}for (int row = top + 1; row <= bottom; row++) {order.push_back(matrix[row][right]);}if (left < right && top < bottom) {for (int column = right - 1; column > left; column--) {order.push_back(matrix[bottom][column]);}for (int row = bottom; row > top; row--) {order.push_back(matrix[row][left]);}}left++; //這里比較好理解,就是4個(gè)量是收縮的狀態(tài)。right--;top++;bottom--;}//end whilereturn order;} };時(shí)間復(fù)雜度:O(mn),其中 mm 和 nn 分別是輸入矩陣的行數(shù)和列數(shù)。矩陣中的每個(gè)元素都要被訪問(wèn)一次。
空間復(fù)雜度:O(1)。除了輸出數(shù)組以外,空間復(fù)雜度是常數(shù)。
?
?
解題思路:用兩個(gè)棧完成該功能,stack1 是用來(lái)存放正常的數(shù)據(jù)的,stack2 來(lái)記錄棧中最小的元素。當(dāng)一個(gè)元素入棧時(shí),stack1完成入棧操作。此時(shí) stack2 需要根據(jù)情況完成相應(yīng)的操作。若 stack2 為空或者入棧的元素小于等于 stack2 的棧頂元素,則 stack2 也完成入棧操作。反之,將 stack2 棧頂元素再入棧。出棧時(shí),兩個(gè)棧都要完成 pop 操作。獲取最小值的操作通過(guò)獲取 stack2 棧頂元素即可。
class MinStack { public:/** initialize your data structure here. */stack<int> data, help;MinStack() {}void push(int x) {data.push(x);// 如果x是data和help的第一個(gè)元素,或者x小于等于help的最小元素,入棧if(help.empty() || x <= help.top()) help.push(x);if(x > help.top()){// 如果x大于help的最小元素,向help中再壓入一遍該最小元素,保持兩個(gè)棧元素個(gè)數(shù)相等// 比如data里現(xiàn)在有3,help里現(xiàn)在也有3,data里壓入一個(gè)4,那么help里壓入一個(gè)3// 這樣如果把data的4彈出,我們同步把help的3也彈出int temp = help.top();help.push(temp);}}void pop() {if(!data.empty() && !help.empty()){data.pop();help.pop();}}int top() {return data.top();}int min() {return help.top();} };?
?
解題思路:該題會(huì)用到兩種數(shù)據(jù)結(jié)構(gòu),分別是棧和隊(duì)列。用隊(duì)列存儲(chǔ)棧的彈出序列,用棧存儲(chǔ)棧的壓入序列。開(kāi)始的時(shí)候,先將彈出序列全部入隊(duì)。接著,將壓入序列逐個(gè)入棧。當(dāng)棧的棧頂元素和隊(duì)的隊(duì)首元素相同時(shí),兩者同時(shí)完成 pop 操作。若最終棧為空,則說(shuō)明第二個(gè)序列是棧的彈出序列。反之,則不是。
class Solution { public:bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {stack<int>S;queue<int>Q;for(int i = 0; i < popped.size(); ++i){//Pop序列入隊(duì)Q.push(popped[i]);}for(int i = 0; i< pushed.size(); ++i){ //Push序列入棧S.push(pushed[i]); //push序列入棧,然后和隊(duì)首元素進(jìn)行比較while(!S.empty() && S.top() == Q.front()){ //棧不空,同時(shí)棧的top等于隊(duì)列的frontQ.pop(); //兩者都popS.pop();}}if(!S.empty()){return false;}return true;} };?
?
解題思路:就是二叉樹(shù)的層次遍歷,需要用到隊(duì)列。先將根結(jié)點(diǎn)入隊(duì),然后每次取隊(duì)首元素將其存到 vector 中,然后再將左子樹(shù)和右子樹(shù)分別入隊(duì)(循環(huán)結(jié)束的標(biāo)志是隊(duì)為空)。
/*** 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:vector<int> levelOrder(TreeNode* root) {vector<int> result;if(root == NULL)return result;queue<TreeNode*> que;TreeNode* p = root;que.push(p);while(!que.empty()){p = que.front();que.pop();result.push_back(p->val);if(p->left != NULL)que.push(p->left);if(p->right != NULL)que.push(p->right);}return result;} };?
?
解題思路:是二叉樹(shù)層次遍歷的變形。該題需要在上一題的基礎(chǔ)上獲取遍歷到每一層時(shí)隊(duì)列的長(zhǎng)度,即該層有多少個(gè)元素。然后可以寫(xiě)一個(gè) for 循環(huán)將該層的元素添加到一個(gè) vector 中,遍歷完每一層后需要 vector 添加到一個(gè)二維 vector 中。
class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> result;vector<int> asd;if(root == NULL)return result;queue<TreeNode*> que;TreeNode* p = root;que.push(p);while(!que.empty()){int size = que.size();for(int i = 0; i < size; i++){p = que.front();que.pop();asd.push_back(p->val);if(p->left != NULL)que.push(p->left);if(p->right != NULL)que.push(p->right);}result.push_back(asd);asd.clear();}return result;} };?
?
解題思路:在上一題的基礎(chǔ)上,設(shè)置一個(gè)記錄層數(shù)的變量。根據(jù)層數(shù)的奇偶多加一個(gè) reverse 操作即可。
class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> result;vector<int> asd;if(root == NULL)return result;queue<TreeNode*> que;TreeNode* p = root;que.push(p);int count = 0;while(!que.empty()){int size = que.size();for(int i = 0; i < size; i++){p = que.front();que.pop();asd.push_back(p->val);if(p->left != NULL)que.push(p->left);if(p->right != NULL)que.push(p->right);}if(count % 2 == 0)result.push_back(asd);else{reverse(asd.begin(), asd.end());result.push_back(asd);}asd.clear();count++;}return result;} };?
?
解題思路:二叉搜索樹(shù)(BST)的中序遍歷是有序序列。其后序遍歷結(jié)點(diǎn)的特性是結(jié)點(diǎn)值左 < 中 < 右。
class Solution { public:bool verifyPostorder(vector<int>& postorder) {if(postorder.empty()) return true;int len = postorder.size();return recursion(postorder, 0, len - 1);// 遞歸 + 利用 BST 后序遍歷節(jié)點(diǎn)值 左 < 中 < 右的特性}bool recursion(vector<int>& postorder, int left, int right){if(left >= right) return true; // 沒(méi)有左子樹(shù)(比如[5,4,3,2,1]),trueint root = postorder[right];int pos = left; // 必須要設(shè)置一個(gè) pos 變量保存 left,不能直接操作 left!for(; pos < right; ++pos){// 找到右子樹(shù)起點(diǎn)if(postorder[pos] > root) break;}for(int j = pos; j<right; ++j){// 如果右子樹(shù)部分有小于 root 的,falseif(postorder[j] < root) return false;}// 看左子樹(shù)是否也是后序遍歷。如果前面不設(shè)置 pos 的話,這里就會(huì)是 (postorder,0,left-1),會(huì)嚴(yán)重超時(shí)if(!recursion(postorder, left, pos - 1)) return false; if(!recursion(postorder, pos, right - 1)) return false;// 看右子樹(shù)是否也是后序遍歷return true;} };?
?
解題思路:該題使用 DFS 思路比較清晰。從上往下進(jìn)行深度遍歷,每進(jìn)入下一層時(shí),sum 都要減去當(dāng)前結(jié)點(diǎn)的 val。當(dāng)遍歷到葉子結(jié)點(diǎn)的時(shí)候,若此時(shí)的 sum 和葉子結(jié)點(diǎn)的值相等,表明這是一條滿足條件的路徑。
/*** 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:vector<vector<int>> ans;void DFS(TreeNode* root, vector<int>& t, int sum){if(root == nullptr)return;t.push_back(root->val);if(root->val == sum && root->left == nullptr && root->right == nullptr)// 是葉子節(jié)點(diǎn)且從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)路徑總和=sum -> 符合題目的路徑ans.push_back(t);// if(root->left)DFS(root->left, t, sum - root->val);// if(root->right)DFS(root->right, t, sum - root->val);// 彈出最后一個(gè)元素t.pop_back();}vector<vector<int>> pathSum(TreeNode* root, int sum) {vector<int> t;DFS(root, t, sum);return ans;} };?
?
(1) 首先觀察到題目給的是一棵 BST,這就意味著我們只需要用中序遍歷就能實(shí)現(xiàn)排序的鏈表了。 (2) 最終得到的鏈表的頭節(jié)點(diǎn)必然是 BST 最左邊的節(jié)點(diǎn)(題目示例中就是 1);尾節(jié)點(diǎn)必然是 BST 最右邊的節(jié)點(diǎn)。 (3) 我們先定義 *tail 和 *head,初始它們都是 nullptr,到最后則會(huì)一個(gè)指向尾節(jié)點(diǎn)一個(gè)指向頭節(jié)點(diǎn)。 (4) 所以我們中序遍歷,首先 root 一直往左走,走到了最左邊的 1 處,此時(shí) tail 還是 nullptr,并且整個(gè)遍歷過(guò)程中只有這個(gè)時(shí)候 tail 會(huì)是 nullptr。這個(gè)時(shí)候我們讓 head = root,也就找到了鏈表的頭節(jié)點(diǎn)。 (5) 然后我們更新 tail = root(也就是 1),root 會(huì)回溯到上一級(jí)也就是 2。這時(shí)我們就寫(xiě) tail -> right = root; 和 root -> left = tail;。 (6) 我們此時(shí)接著更新 tail = root(也就是 2)。如此往復(fù)。 (7) 中序遍歷走完之后,鏈表也就構(gòu)造完了,除了 head 和 tail 之間的連接,我們?cè)龠B接一下就好了(代碼對(duì)應(yīng)處有注釋)。解題思路:
(1) 首先觀察到題目給的是一棵 BST,這就意味著我們只需要用中序遍歷就能實(shí)現(xiàn)排序的鏈表了。 (2) 最終得到的鏈表的頭節(jié)點(diǎn)必然是 BST 最左邊的節(jié)點(diǎn)(題目示例中就是 1);尾節(jié)點(diǎn)必然是 BST 最右邊的節(jié)點(diǎn)。 (3) 我們先定義 *tail 和 *head,初始它們都是 nullptr,到最后則會(huì)一個(gè)指向尾節(jié)點(diǎn)一個(gè)指向頭節(jié)點(diǎn)。 (4) 所以我們中序遍歷,首先 root 一直往左走,走到了最左邊的 1 處,此時(shí) tail 還是 nullptr,并且整個(gè)遍歷過(guò)程中只有這個(gè)時(shí)候 tail 會(huì)是 nullptr。這個(gè)時(shí)候我們讓 head = root,也就找到了鏈表的頭節(jié)點(diǎn)。 (5) 然后我們更新 tail = root(也就是 1),root 會(huì)回溯到上一級(jí)也就是 2。這時(shí)我們就寫(xiě) tail -> right = root; 和 root -> left = tail;。 (6) 我們此時(shí)接著更新 tail = root(也就是 2)。如此往復(fù)。 (7) 中序遍歷走完之后,鏈表也就構(gòu)造完了,除了 head 和 tail 之間的連接,我們?cè)龠B接一下就好了(代碼對(duì)應(yīng)處有注釋)。 /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node() {}Node(int _val) {val = _val;left = NULL;right = NULL;}Node(int _val, Node* _left, Node* _right) {val = _val;left = _left;right = _right;} }; */ class Solution { private: Node* head, *tail; public:Node* treeToDoublyList(Node* root) {if(!root) {return nullptr;}inorder(root); // 構(gòu)造出鏈表的所有結(jié)構(gòu),除了頭連尾和尾連頭的兩個(gè)指針head -> left = tail; // 補(bǔ)上頭連尾tail -> right = head; // 補(bǔ)上尾連頭return head; // 返回頭}void inorder(Node* root) {if(!root) {return;}inorder(root -> left); // 左if(!tail) {head = root; // 當(dāng)tail還不存在,也就是root此時(shí)在整個(gè)BST的最左邊的節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)就是head}else {tail -> right = root; // 前一個(gè)節(jié)點(diǎn)的right是當(dāng)前節(jié)點(diǎn)root -> left = tail; // 當(dāng)前節(jié)點(diǎn)的left是前一個(gè)節(jié)點(diǎn)}tail = root; // 將前一個(gè)節(jié)點(diǎn)更新為當(dāng)前節(jié)點(diǎn)(所以到最后,tail就會(huì)挪到整個(gè)BST的最右邊的節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)就是鏈表的尾節(jié)點(diǎn))inorder(root -> right); // 右} };?
?
解題思路:(1) 使用全排列的方法能夠列出所有的情況,但是題中說(shuō)明了不能包含重復(fù)元素。所以可以在每次添加到二維數(shù)組的時(shí)候添加一個(gè)判斷條件,即調(diào)用 find 函數(shù),看已經(jīng)保存的組合中有沒(méi)有當(dāng)前組合。但是這樣時(shí)間復(fù)雜度太大了,在力扣上會(huì)時(shí)間超出限制。
class Solution { private:vector<string> result; public:void permutation(vector<string> &asd, string &s , int k, int m){if(k == m){if( find(result.begin(), result.end(), s) == result.end() )asd.emplace_back(s);return;}else{for(int i = k; i < m; i++){swap(s[k], s[i]);permutation(asd, s, k+1, m);swap(s[k], s[i]);}}}vector<string> permutation(string s) {int size = s.size();permutation(result, s, 0, size);return result;} };(2) 使用回溯法。
回溯法字符串的排列和數(shù)字的排列都屬于回溯的經(jīng)典問(wèn)題回溯算法框架:解決一個(gè)問(wèn)題,實(shí)際上就是一個(gè)決策樹(shù)的遍歷過(guò)程:1. 路徑:做出的選擇2. 選擇列表:當(dāng)前可以做的選擇3. 結(jié)束條件:到達(dá)決策樹(shù)底層,無(wú)法再做選擇的條件偽代碼:result = []def backtrack(路徑,選擇列表):if 滿足結(jié)束條件:result.add(路徑)returnfor 選擇 in 選擇列表:做選擇backtrack(路徑,選擇列表)撤銷(xiāo)選擇核心是for循環(huán)中的遞歸,在遞歸調(diào)用之前“做選擇”,在遞歸調(diào)用之后“撤銷(xiāo)選擇”。字符串的排列可以抽象為一棵決策樹(shù):[ ][a] [b] [c][ab] [ac] [bc] [ba] [ca] [cb][abc] [acb] [bca] [bac] [cab] [cba]考慮字符重復(fù)情況:[ ][a] [a] [c][aa] [ac] [ac] [aa] [ca] [ca][aac] [aca] [aca] [aac] [caa] [caa]字符串在做排列時(shí),等于從a字符開(kāi)始,對(duì)決策樹(shù)進(jìn)行遍歷,"a"就是路徑,"b""c"是"a"的選擇列表,"ab"和"ac"就是做出的選擇,“結(jié)束條件”是遍歷到樹(shù)的底層,此處為選擇列表為空。本題定義backtrack函數(shù)像一個(gè)指針,在樹(shù)上遍歷,同時(shí)維護(hù)每個(gè)點(diǎn)的屬性,每當(dāng)走到樹(shù)的底層,其“路徑”就是一個(gè)全排列。當(dāng)字符出現(xiàn)重復(fù),且重復(fù)位置不一定時(shí),需要先對(duì)字符串進(jìn)行排序,再對(duì)字符串進(jìn)行“去重”處理,之后按照回溯框架即可。 std::vector<std::string> permutation(std::string s) {if(s.empty()){return {};}// 對(duì)字符串進(jìn)行排序std::sort(s.begin(), s.end());std::vector<std::string> res;// 標(biāo)記字符是否遍歷過(guò)std::vector<bool> visit(s.size(), false);std::string track;backtrack(res, s, track, visit);return res; }/** 回溯函數(shù)* 使用sort函數(shù)對(duì)字符串排序,使重復(fù)的字符相鄰,* 使用visit數(shù)組記錄遍歷決策樹(shù)時(shí)每個(gè)節(jié)點(diǎn)的狀態(tài),* 節(jié)點(diǎn)未遍歷且相鄰字符不是重復(fù)字符時(shí),* 則將該字符加入排列字符串中,依次遞歸遍歷。* */ void backtrack(std::vector<std::string> &res, std::string s, std::string &track, std::vector<bool> &visit) {// 回溯結(jié)束條件if(track.size() == s.size()){res.push_back(track);return;}// 選擇和選擇列表for(int i = 0; i < s.size(); i++){// 排除不合法的選擇if(visit[i]){continue;}if(i > 0 && !visit[i-1] && s[i-1] == s[i]){continue;}visit[i] = true;// 做選擇track.push_back(s[i]);// 進(jìn)入下一次決策樹(shù)backtrack(res, s, track, visit);// 撤銷(xiāo)選擇track.pop_back();visit[i] = false;} }?
?
解題思路:設(shè)置一個(gè)計(jì)數(shù)器 count,每遇到一個(gè)和當(dāng)前的數(shù)字相同的數(shù)字,就讓 count 自增,遇到一個(gè)和當(dāng)前數(shù)字不一樣的數(shù)字,就讓 count--,當(dāng) count < 0 時(shí),就將 cur 設(shè)置為當(dāng)前遍歷的數(shù)字。因?yàn)橛幸粋€(gè)數(shù)字出現(xiàn)次數(shù)超過(guò)數(shù)組長(zhǎng)度的一半,最后得到的必然是該數(shù)字。
class Solution { public:int majorityElement(vector<int>& nums) {int count = 1, cur = nums[0];for(int i = 1; i < nums.size(); i++) {if(cur != nums[i]) {--count;if(count < 0) {cur = nums[i];count = 1;}}else ++count;}return cur;} };?
?
解題思路:找出序列中最小的 k 個(gè)數(shù),這和 Top k 問(wèn)題的解題思路一樣。構(gòu)建一個(gè)含有 k 個(gè)元素的大頂堆。先將序列前 k 個(gè)元素放入堆中,剩余的元素入堆時(shí)需要滿足新加入的元素要小于堆頂元素。滿足該條件時(shí),先 pop 堆頂元素,然后完成 push 操作。最終堆內(nèi)的元素就是該序列的最小的 k 個(gè)元素。
class Solution { public:vector<int> getLeastNumbers(vector<int>& arr, int k) {int size = arr.size();vector<int> result;if(k == 0) return result;priority_queue<int, vector<int>, less<int>> asd;for(int i = 0; i < k; i++)asd.push(arr[i]);for(int i = k; i < size; i++){if(arr[i] < asd.top()){asd.pop();asd.push(arr[i]);}}while(k--){result.push_back(asd.top());asd.pop();}return result;} };?
?
解題思路:比較簡(jiǎn)單的動(dòng)態(tài)規(guī)劃問(wèn)題。使用 maxsum 記錄當(dāng)前最大和,bestmax 是 maxsum 中最大的值。從前往后遍歷數(shù)組元素,當(dāng) maxsum 大于零時(shí),令 maxsum += nums[i]。反之,令 maxsum = nums[i]。每遍歷完一個(gè)元素后,用 bestmax 記錄 maxsum 和 bestmax 中較大的值。
class Solution { public:int maxSubArray(vector<int>& nums) {int size = nums.size();int maxsum = 0, bestmax = INT_MIN;for(int i = 0; i < size; i++){if(maxsum > 0) maxsum += nums[i];else maxsum = nums[i];bestmax = std::max(maxsum, bestmax);}return bestmax;} };?
?
解題思路:力扣上常規(guī)方法很難滿足時(shí)間要求,很多人給的答案都是通過(guò)找規(guī)律完成該題的。這里我只將它當(dāng)成大數(shù)問(wèn)題來(lái)做。
class Solution { public:int countDigitOne(int n) {string str = "0";int overten = 0, count = 0;while(n--){int temp;for(int i = 0; i < str.size(); i++){temp = str[i] - '0';temp = temp + 1;if(temp >= 10){temp -= 10;overten = 1;}elseoverten = 0;str[i] = temp + '0';if(overten == 0)break;}if(overten == 1)str.push_back('1');for(int i = 0; i < str.size(); i++){if(str[i] == '1')count++;}}return count;} };?
?
解題思路:不管有多少個(gè)數(shù),也不管這些數(shù)各自分別是幾位數(shù)(個(gè)位 / 十位 / 百位 / ...),我們的判斷方法只有一個(gè)。那就是先比較最高位,按照從小到大排即可。具體步驟如下:
(1)先比較這些數(shù)各自的最高位,按從小到大排。比如 "1"、"32"、"100",我們通過(guò)比較最高位可以排出:一開(kāi)始是 "1" 或 "100",然后是 "32"。 (2)如果遇到了最高位數(shù)字相同的情況,比如上面的 "1" 和 "100",我們自定義比較方法:比較 字符串 1 + 字符串 2 與 字符串 2 + 字符串 1 的大小, 返回小的那個(gè)。在這里我們有 "1" + "100" = "1100" 和 "100" + "1" = "1001"。因?yàn)?"1001" < "1100",所以我們知道應(yīng)該把 "100" 放第一位,"1" 放第二位,"32" 還是在最后。 (3)假如最高位沒(méi)有相同數(shù)字,那么根據(jù)最高位的排序直接就已經(jīng)排好了。 class Solution { public:string minNumber(vector<int>& nums) {vector<string> strs;string ans;//將 int 轉(zhuǎn)變?yōu)?stringfor(int i = 0; i < nums.size(); i ++){strs.push_back(to_string(nums[i]));}//排序sort(strs.begin(), strs.end(), [](string& s1, string& s2){return s1 + s2 < s2 + s1;});for(int i = 0; i < strs.size(); i ++)ans += strs[i];return ans;}static bool strCompare(const string& s1, const string& s2) { // 注意 staticstring add1 = s1 + s2;string add2 = s2 + s1;return add1 < add2;} };?
?
解題思路:代碼中有注釋。
class Solution {/*** 先看這里 先看這里 先看這里 先看這里* 看官方的例子* 輸入: 12258* 輸出: 5* 解釋: 12258有5種不同的翻譯,分別是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"* 也就是數(shù)字搭配可以是一位,可以是兩位(兩位情況下要小于26),也就是0-25取值范圍*/public:int translateNum(int num) { //如果num小于10了,也就沒(méi)有得拆分了,只有一種情況,例如數(shù)字 5 ,只有 f 這個(gè)字母表示if (num < 10) {return 1;}//大于10的情況,就要分情況討論了int re = num % 100;if (re < 10) {//情況1.:128105 % 100 = 5,那么5這個(gè)數(shù)字和無(wú)法和0合體的。那么去掉他 (num / 10),繼續(xù)下一輪遞歸return translateNum(num / 10);} else if (re < 26) {//情況2.:12810 % 100 = 10 ,可以分成兩種情況進(jìn)行討論,就是1281 和 128 進(jìn)行下一輪的遞歸,也就是對(duì)10這兩位數(shù)選擇,可以選擇成為ba和k。兩種情況return translateNum(num / 10) + translateNum(num / 100);} else {//情況.:128 % 100 = 28,這種情況比26大,因?yàn)轭}目要求是字母,28明顯比z大了,所以不能像情況2哪樣進(jìn)行拆分成兩個(gè)遞歸進(jìn)行相加return translateNum(num / 10);}} };?
?
解題思路:從棋盤(pán)的左上角開(kāi)始拿格子里的禮物,并每次向右或者向下移動(dòng)一格、直到到達(dá)棋盤(pán)的右下角。根據(jù)題目說(shuō)明,易得某單元格只可能從上邊單元格或左邊單元格到達(dá)。
? ? ?
class Solution { public:int maxValue(vector<vector<int>>& grid) {int rows = grid.size(), columns = grid[0].size();vector<vector<int>> dp(rows, vector<int>(columns, 0));for(int i = 0; i < rows; i ++)for(int j = 0; j < columns; j++){int left = 0, up = 0;if( i > 0 )up = dp[i-1][j];if( j > 0 )left = dp[i][j-1];dp[i][j] = max(left, up) + grid[i][j];// f(i,j) = max[f(i,j?1),f(i?1,j)] + grid(i,j);}int result = dp[rows-1][columns-1];return result;} };?
?
解題思路:以 abcabcbb 為例,找出從每一個(gè)字符開(kāi)始的,不包含重復(fù)字符的最長(zhǎng)子串,那么其中最長(zhǎng)的那個(gè)字符串即為答案。對(duì)于示例一中的字符串,我們列舉出這些結(jié)果,其中括號(hào)中表示選中的字符以及最長(zhǎng)的字符串:
以 (a)bcabcbb 開(kāi)始的最長(zhǎng)字符串為 (abc)abcbb; 以 a(b)cabcbb 開(kāi)始的最長(zhǎng)字符串為 a(bca)bcbb; 以 ab(c)abcbb 開(kāi)始的最長(zhǎng)字符串為 ab(cab)cbb; 以 abc(a)bcbb 開(kāi)始的最長(zhǎng)字符串為 abc(abc)bb; 以 abca(b)cbb 開(kāi)始的最長(zhǎng)字符串為 abca(bc)bb; 以 abcab(c)bb 開(kāi)始的最長(zhǎng)字符串為 abcab(cb)b; 以 abcabc(b)b 開(kāi)始的最長(zhǎng)字符串為 abcabc(b)b; 以 abcabcb(b) 開(kāi)始的最長(zhǎng)字符串為 abcabcb(b)。若新添加的字符是括號(hào)中的序列所擁有的,則將開(kāi)始的索引指向原序列字符的后一位,記錄相應(yīng)的長(zhǎng)度數(shù)值,然后再繼續(xù)往后尋找。
class Solution { public:int lengthOfLongestSubstring(string s) {int start = 0, end = 0, length = 0, maxlength = 0;int Ssize = s.size();while (end < Ssize){char tmpChar = s[end];for (int index = start; index < end; index++){if (tmpChar == s[index]){start = index + 1; // 若新添加的字符是括號(hào)中的序列所擁有的,則將開(kāi)始的索引指向原序列字符的后一位length = end - start;break; // 不用更新最大值的原因是這種情況下,的length最多和原來(lái)相等}}end++;length++;maxlength = max(maxlength, length);}return maxlength;} };?
?
解題思路:根據(jù)題意直接完成(但是力扣上超時(shí),其他的方法能解決,但是不想花太多時(shí)間去看)。
class Solution { public:bool ugly(int n){while(n%2 == 0)n /= 2;while(n%3 == 0)n /= 3;while(n%5 == 0)n /= 5;return (n == 1)? true:false;}int nthUglyNumber(int index) {if(index <= 0)return 0;int number = 0;int uglynumber = 0;while(uglynumber < index){++number;if(ugly(number))++uglynumber;}return number;} };?
?
解題思路:用一個(gè)數(shù)組記錄字符出現(xiàn)的情況,然后通過(guò)遍歷找到只出現(xiàn)一次的字符。
class Solution { public:char firstUniqChar(string s) {int size = s.size();int a[256];if(s == "")return ' ';for(int i = 0; i < 256; i++)a[i] = 0;const char* p1 = s.c_str();const char* p2 = p1;while(*(p1) != '\0'){a[*(p1)]++; p1++;}while(*p2 != '\0'){if(a[*p2] == 1){return *p2;}p2++;}return ' ';} };?
?
解題思路:(1) 兩層循環(huán)(簡(jiǎn)單,但是超時(shí)了)。(2) 用歸并排序的方法(一時(shí)半會(huì)兒沒(méi)看懂,先做個(gè)記錄吧)。
class Solution { public:int reversePairs(vector<int>& nums) {int count = 0;for(int i = 0; i < nums.size(); i++){for(int j = i+1; j < nums.size(); j++){if(nums[i] > nums[j])count++;}}return count;} };class Solution { public:int coun=0;int temp[50000];void merge(vector<int>&arr, int s, int e) {int mid = (s + e) / 2;int i = s;int j = mid + 1;int k = s;while ((i <= mid) && (j <= e)) {if (arr[i] <=arr[j]) {temp[k++] = arr[i++];}else if (arr[i] > arr[j]) {temp[k++] = arr[j++];coun += (mid - i + 1);}}while (i <= mid) {temp[k++] = arr[i++];}while (j <= e) {temp[k++] = arr[j++];}for (int i = s; i <= e; i++) {arr[i] = temp[i];}}void mergeSort(vector<int>&arr, int s, int e) {if (s >= e) {return;}int mid = (s + e) / 2;mergeSort(arr, s, mid);mergeSort(arr, mid + 1, e);merge(arr, s, e);}int reversePairs(vector<int>& data) {int n=data.size();if(n==0) return 0;int s=0;int e=n-1;mergeSort(data, s, e);return coun;} };?
總結(jié)
以上是生活随笔為你收集整理的剑指 offer 编程题 C++ 版总结(中)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 剑指 offer 编程题 C++ 版总结
- 下一篇: 剑指 offer 编程题 C++ 版总结