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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

剑指 offer 编程题 C++ 版总结(下)

發布時間:2024/2/28 c/c++ 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 剑指 offer 编程题 C++ 版总结(下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

解題思路:讓兩個指針分別指向兩個鏈表,當遍歷到最末尾的結點后,指針指向另一個鏈表的頭結點。例如:A 和 B 開始的時候分別指向 4 和 5。兩個指針分別遍歷鏈表,當 A 遍歷到末尾結點的 5 時,下一步讓其指向頭結點為 5 的結點。而 B,讓其指向頭結點為 4 的結點。這樣,A 和 B 指針在指向公共結點的時候都走了相同的距離,所以它們相遇的時候該結點就是公共結點。

/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *node1 = headA;ListNode *node2 = headB;while (node1 != node2) {if(node1 != NULL)node1 = node1->next;elsenode1 = headB;if(node2 != NULL)node2 = node2->next;elsenode2 = headA;}return node1;} };

?

?

解題思路:由題可知,數組是排好序的,所以可以先用二分查找找出 target 所在的下標,然后根據下標向前向后查找即可。

class Solution { public:int search(vector<int>& nums, int target) {int count = 0, mid, low = 0, high = nums.size()-1;while(low <= high){mid = low + (high - low)/2;if(nums[mid] == target){count = searchcount(nums, target, mid);break;}else if(nums[mid] < target)low = mid + 1;elsehigh = mid - 1;}return count;}int searchcount(vector<int>& nums, int target, int index){int count = 0, left = index - 1, right = index + 1;while( left >= 0 ) {if(nums[left--] == target)count++;else break;}while( right < nums.size() ) {if(nums[right++] == target)count++;else break;}return count + 1;} };

?

?

解題思路:是有序數組,第一個想到的就是二分查找

class Solution { public:int missingNumber(vector<int>& nums) {int left = 0, right = nums.size();while(left < right){int mid = left + (right - left)/2;if(mid == nums[mid]){left = mid+1;}else{right = mid;}}return left;} };

?

?

解題思路:二叉搜索樹中序遍歷可以得到遞增有序序列。將二叉樹中序非遞歸遍歷反向操作,每次先右子樹入棧即可獲得遞減有序序列。這樣就能很快找到第 k 大的結點。

/*** 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:int kthLargest(TreeNode* root, int k) {stack<TreeNode*> sta;TreeNode* p = root;int count = 0, result;while(!sta.empty() || p != NULL){while(p){sta.push(p);p = p->right;}p = sta.top();if(++count == k)result = p->val;sta.pop();p = p->left;}return result;} };

?

?

解題思路:利用遞歸計算二叉樹的深度。

/*** 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:int maxDepth(TreeNode* root) {if(root == NULL)return 0;int i = maxDepth(root->left);int j = maxDepth(root->right);return i > j ? i+1: j+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:bool isBalanced(TreeNode* root) {return !root ? true : abs(depth(root->left) - depth(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);}int depth(TreeNode* cur) { //計算二叉樹的最大深度return !cur ? 0 : max(depth(cur->left), depth(cur->right)) + 1;} };

?

?

解題思路:(1) 用哈希表。(2) 題目中強調其他數字出現兩次,如果只有一個數字的情況下,我們可是使用異或(相同為0,不同為1)得出要找的數字。但是題中要找的數數字有兩個,所以這個時候還是需要對整個序列求異或。這樣最終得到的是要找的兩個數字的異或的結果。此時,我們可以根據得到的值,將原本序列分成兩個序列(每個序列除了一個單獨數字外,其余的數字都是成對出現的),然后再對這兩個序列進行異或操作就能找出兩個只出現一次的數字。

class Solution { public:vector<int> singleNumbers(vector<int>& nums) {unordered_map<int, int> res;vector<int> number;for (int i = 0; i < nums.size(); i++) {res[nums[i]]++;}for (auto c : nums) {if (res[c] == 1) {number.emplace_back(c);}}return number;} };class Solution { public:vector<int> singleNumbers(vector<int>& nums) {//總共分為兩個步驟:(1) 整個序列求異或 (2) 根據異或值劃分序列后再求異或//第一步:求所有的數異或的結果int k = 0;for (int num : nums) {k ^= num;}//獲得k中最低位的1,k里面兩數不同,故肯定有一位1是不同的,可以以此區分兩個序列int mask = 1;while ((k & mask) == 0) {mask <<= 1;}int a = 0; int b = 0;//第二步:根據 mask 劃分序列后再求異或for (int num : nums) {if ((num & mask) == 0) {a ^= num; // 找到那位不為1的元素, 其余數為偶數,會自動消去} else {b ^= num; // 找到那位為1的元素}}return {a, b};} };

?

?

解題思路:(1) 用哈希表。(2) 使用位運算。得注意的是:如果某個數字出現 3 次,那么這個 3 個數字的和肯定能被 3 整除,則其對應二進制位的每一位的和也能被 3 整除。統計數組中每個數字的二進制中每一位的和,判斷該和是否能被 3 整除。若可以,則只出現一次的數字的二進制數中那一位為 0,否則為 1。

class Solution { public:int singleNumber(vector<int>& nums) {unordered_map<int, int> mp;for(int n : nums) mp[n] ++;int ans;for(auto pr : mp){if(pr.second == 1){ans = pr.first;break;}}return ans;} };class Solution { public:int singleNumber(vector<int>& nums) {int ans = 0;for(int i = 0; i < 32; ++i){int cnt = 0;for(int n : nums){// n & 1 << i 的值大于0即為真if(n & (1 << i)) cnt++;}// 構造只出現一次的那個數字,采用異或的方法生成二進制中的每一位if(cnt % 3 == 1) ans ^= (1 << i);}return ans;} };

?

?

解題思路:題中所給的序列是遞增排序序列,所以可以使用雙指針 low 和 high 解決該問題。若 low 和 high 的兩個元素相加為 target 此時記錄相應的值返回即可。若兩者的和大于 target,令 high--。反之令 low++。

class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {int size = nums.size();vector<int> asd(2, 0); int low = 0, high = size-1;while(low < high){if(nums[low] + nums[high] == target){ asd[0] = nums[low];asd[1] = nums[high];}if(nums[low] + nums[high] > target){high--;}else{low++;}}return asd;} };

?

?

解題思路:該題所求的是所有和為 target 的連續正整數序列的組合。這里可以使用暴力枚舉解決該題。首先需要確定邊界,limit 為 target / 2 向下取整。內層循環完成累加操作,若累加和等于 target,則記錄相應的數字序列。循環終止的條件是累加和大于 target。

class Solution { public:vector<vector<int>> findContinuousSequence(int target) {vector<vector<int>> vec;vector<int> res;int sum = 0, limit = (target - 1) / 2; //(target - 1)/2 等效于 target / 2 下取整for (int i = 1; i <= limit; ++i) {for (int j = i;; ++j) {sum += j; //完成累加操作if (sum > target) {sum = 0;break;}else if (sum == target) { //記錄數字序列res.clear();for (int k = i; k <= j; ++k) res.emplace_back(k);vec.emplace_back(res);sum = 0;break;}}}return vec;} };

?

下面這個方法是對上述代碼的改進。每次操作之后的序列和操作之前的序列相比大部分數字都是一樣的,只是增加或減少了一個數字,因此我們可以在前一個序列的和的基礎上求操作之后的序列的和。這樣可以減少很多不必要的運算,從而提高代碼效率。

class Solution { public:vector<vector<int>> findContinuousSequence(int target) {vector<vector<int>> result;vector<int> asd;if(target < 3)return result;int low = 1, high = 2;int middle = (target+1)/2;int sum = low + high;while(low < middle){if(sum == target){Add(result, asd, low, high);}while(sum > target && low < middle){sum -= low;low++;if(sum == target){Add(result, asd, low, high);} }high++;sum += high;}return result;}void Add(vector<vector<int>>& result, vector<int>& asd, int low, int high){for(int i = low; i <= high ; i++)asd.push_back(i);result.push_back(asd);asd.clear();} };

?

若上面的題目不要求組合內的序列是連續的,那該怎么解決?

#include <iostream> #include <list> #include <cmath> using namespace std;int *a, *b, N, n; int total = 0; void find(int sum, int n, int b[]) {//遞歸出口if(sum <= 0 || n <= 0)return;//輸出找到的結果if(sum == b[n-1]) //表示找到了一個值{a[n-1] = 1;for(int i = 0; i < N; i++){if(a[i] == 1)cout << b[i] <<" ";}cout <<endl;total++;}a[n-1] = 1;find(sum-b[n-1], n-1, b); //如果放入n,則從剩余n-1個數中填滿sum-na[n-1] = 0;find(sum, n-1, b); //如果不放入n,從n-1個數中填滿sum }int main() {int sum;cout<<"Please Input n&sum:"<<endl;cin>>N>>sum;a = new int[N];b = new int[N];for(int i = 0; i < N; i++)cin >> b[i];n = N;find(sum, n, b);cout<<"Total:"<<total<<endl;//cout << Number <<endl;return 0; }

?

?

解題思路:(1) 該題是利用空格進行劃分,所以可以使用雙指針從后往前將字符串進行分割。 (2) 利用 strtok 函數對字符串進行分割,利用 vector 存儲分割后的單詞,接著完成一個 reverse 操作。最后根據題意,添加相應的空格即可。

class Solution { public:string reverseWords(string s) {string str;if(s.empty()) //輸入字符為空,返回空return str;cout << s.size() <<endl;int i{0}, j{0}; //i,j用來表示單詞開始和結束的位置j = s.size() - 1;for(j; j >= 0; --j){if(s[j] != ' ') //遇到不是空格的{i = j;while(i >=0 && s[i] != ' ') //從j開始向左尋找單詞,i>=0防止越界訪問--i;for(int k = i + 1; k <= j; ++k) //單詞序列加入字符串中str.push_back(s[k]);str.push_back(' '); //加入一個空格j = i; //改變j的位置}}if(str.size() > 0)str.pop_back(); //移除末尾空格return str;} };class Solution { public:string reverseWords(string s) {vector<string> asd;int size = s.size();char* p = new char[size+1];strcpy(p, s.c_str());char* token = strtok(p, " ");while(token != NULL){asd.push_back(token);token = strtok(NULL, " ");}reverse(asd.begin(), asd.end());string result;for(int i = 0; i < asd.size(); i++){if(i != asd.size()-1)result += asd[i] + " ";elseresult += asd[i];}delete []p;return result;} };

?

?

class Solution { public:string reverseLeftWords(string s, int n) {reverse(s.begin(), s.begin() + n);reverse(s.begin() + n, s.end());reverse(s.begin(), s.end());return s;} };

?

?

解題思路:直接根據題意做題,用變量記錄劃窗中的最大值和其下標。當劃窗移動的時候,先判斷最值下標是否在區間內。如果在,只用對比新添加的元素和最值的大小即可。如果不在,就需要重新尋找劃窗中的最大值了。

class Solution { public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int>a;if(nums.size() == 0){ return a;}int n = nums.size() - k + 1; // 滑動窗口滾動的次數int t = -1; // 當前滑窗最大值的下標int max; // 當前滑窗最大值// 遍歷,找每個滑窗的最大值for(int i = 0;i < n ;i++){// 上個滑窗的最大值還在當前滑窗內,如果新加入的元素比最大值還大,更新最大值和其下標 if(t >= i){if(nums[i+k-1] >= max){t = i+k-1;max = nums[i+k-1];}}// 不在的時候重新遍歷當前滑窗,這里效率就低else{max = nums[i];for(int j = i + 1;j < k + i;j++){if(nums[j] >= max){t = j;max = nums[j];}}}// 添加最大值a.push_back(max);}return a;} };

?

?

解題思路:解決該題需要用到兩個數據結構,queue 和 deque,其中 queue 用于存儲數據,deque 用于記錄序列的最大值。

push_back():當添加一個新的數據的時候,如果 deque 不為空的情況下且其中的值小于添加的值,那么就將 deque 中的數據 pop_back(),然后將數據同時添加到 deque 和 queue 中。

pop_front():正常 pop 隊列元素即可。需要添加的代碼是,如果 queue 和 deque 隊首元素相等,則兩個都要 pop 元素。

max_value():deque 為空時 return -1 ,不為空時 return deque 的隊首元素。

class MaxQueue {queue<int> q;deque<int> d; public:MaxQueue() {}int max_value() {if (d.empty())return -1;return d.front();}void push_back(int value) {while (!d.empty() && d.back() < value) {d.pop_back();}d.push_back(value);q.push(value);}int pop_front() {if (q.empty())return -1;int ans = q.front();if (ans == d.front()) {d.pop_front();}q.pop();return ans;} };

?

?

解題思路:有一串連續的數字(無重復),這串數字中最大值為 m,最小值為 n,這串數字一共包含 m-n+1 個數字。同樣,如果我們能夠知道 5 張撲克牌中的最大值 maxValue 和最小值 minValue,那我們就知道,要使它為順子需要 maxValue - minValue + 1 張牌。

(1) 在查找 maxValue 和 minValue 過程中,跳過大小王。

(2) 如果 maxValue - minValue + 1 > 5,說明題目給的 5 張牌不足以構成順子,即使里面有大小王,也不夠用來填補使它構成順子,所以返回 false。

(3) 如果 maxValue - minValue + 1 <= 5,說明 5 張牌足以構成順子(里面的大小王能填補在合適位置),返回 true。

class Solution { public:bool isStraight(vector<int>& nums) {bool m[15];memset(m, 0, sizeof(m));int minValue = 14, maxValue = 0;for (int item : nums) {if (item == 0) {continue;}if (m[item]) {return false;}m[item] = true;minValue = min(minValue, item);maxValue = max(maxValue, item); }return maxValue - minValue + 1 <= 5;} };

?

?

解題思路:這個問題實際上是約瑟夫問題。下面這個例子是N=8 m=3的例子。我們定義F(n,m)表示最后剩下那個人的索引號,因此我們只關系最后剩下來這個人的索引號的變化情況即可。

現在我們知道了G的索引號的變化過程,那么我們反推一下從N = 7 到N = 8 的過程。我們先把被刪除的C補充回來,然后右移m個人,發現溢出了,再把溢出的補充在最前面,經過這個操作就恢復了N = 8 的排列了!

因此我們可以推出遞推公式 f(8,3) = [ f(7, 3) + 3 ] %?8。進行推廣泛化,即 f(n,m) = [ f(n?1,m) + m ]%n 。

法一:遞推公式 class Solution { public:int lastRemaining(int n, int m) {int pos = 0; // 最終留下的初始位置for(int i = 2; i <= n; i++){pos = (pos + m) % i; // 每次循環右移}return pos;} };法二:用鏈表 int lastRemaining(int n, int m) { // n個數字,每次刪除第m個數字if(n < 1 || m < 1)return -1;int i = 0;list<int> a;for(i = 0; i < n; i++)a.push_back(i);list<int>::iterator current = a.begin();list<int>::iterator next;while(a.size() > 1){for(int i = 1; i < m; i++){current++;if(current == a.end())current = a.begin();}next = ++current;if(next == a.end())next = a.begin();--current;a.erase(current);current = next; }//end whilereturn *(current); }

?

?

解題思路:最簡單的辦法是使用兩層循環解決該問題,即外層循環從 0 開始,內層循環使用一個變量記錄循環過程中最大的利潤。這樣算法復雜度就是 O(n^2)。其實在遍歷過程中記錄當前元素之前的最小值,那么就可以優化掉一次循環。

class Solution { public:int maxProfit(vector<int>& prices) {if(prices.size() == 0)return 0;int minPrice = prices[0];int maxGap = 0;for(int i = 0; i < prices.size(); i++){minPrice = min(minPrice, prices[i]); // 更新股票最小值maxGap = max(maxGap, prices[i] - minPrice); // 更新利潤最大值}return maxGap;} };

?

?

解題思路:根據題意做題即可。

class Solution { public:int strToInt(string str) {int i = 0, flag = 1;long res = 0; //默認flag = 1,正數while (str[i] == ' ') i++;if (str[i] == '-') flag = -1;if (str[i] == '-' || str[i] == '+') i ++;for (; i < str.size() && (str[i] >= '0' && str[i] <= '9'); i++) {res = res * 10 + (str[i] - '0');if (res >= INT_MAX && flag == 1) return INT_MAX;if (res > INT_MAX && flag == -1) return INT_MIN;} return flag * res;} };

?

?

解題思路:二叉搜索樹,其中序遍歷有序。所以,當左右子樹的值都大于根的值。則移動至根的右子樹。當左右子樹的值都小于根的值,則移動至根的左子樹。若此時,一個結點的值比根大,另一個結點的值比根小,則 這個根就是兩者最近祖先結點。

/*** 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root == NULL)return NULL;if(p->val > root->val && q->val > root->val) return lowestCommonAncestor(root->right, p, q);else if(p->val < root->val && q->val < root->val)return lowestCommonAncestor(root->left, p, q);else return root;} };

?

?

解題思路:遞歸查詢兩個節點 p 和 q,如果某個節點等于節點 p 或節點 q,則返回該節點的值給父節點。如果當前節點的左右子樹分別包括 p 和 q 節點,那么這個節點必然是所求的解。如果當前節點有一個子樹的返回值為 p 或 q 節點,則返回該值。(告訴父節點有一個節點存在其子樹中)如果當前節點的兩個子樹返回值都為空,則返回空指針。

/*** 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root == NULL)return NULL; if(root == p||root == q)return root;TreeNode* left = lowestCommonAncestor(root->left, p, q);TreeNode* right = lowestCommonAncestor(root->right, p, q);if(left && right) return root;return left ? left : right; // 只有一個非空則返回該指針,兩個都為空則返回空指針} };

?

超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生

總結

以上是生活随笔為你收集整理的剑指 offer 编程题 C++ 版总结(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

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