1.鏈表反轉

public: ListNode* reverseList(ListNode* head) { ListNode* pre = NULL; ListNode* cur = head; ListNode* nex = NULL; while(cur!=NULL){ nex = cur->next; cur->next = pre; pre = cur; cur = nex; } return pre; } };
TreeNode* pre = NULL;
2.如何判斷一個單鏈表是有環的

class Solution { public: bool hasCycle(ListNode *head) { if(!head) return 0; auto fast = head->next; auto slow = head; while(slow!=fast){ if(fast==NULL||fast->next==NULL) return 0; slow=slow->next; fast=fast->next->next; } return true; } };
輸出有環的結點位置

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode* fastPtr=head, *slowPtr=head; // 讓fast與slow指針第一次相遇 while (fastPtr!=NULL && fastPtr->next!=NULL) { fastPtr = fastPtr->next->next; slowPtr = slowPtr->next; if (fastPtr==slowPtr) { // 從相遇點再走“非環部分長度”一定可以再次走到環起點 fastPtr = head; while (fastPtr != slowPtr) { fastPtr = fastPtr->next; slowPtr = slowPtr->next; } return fastPtr; break; } } return nullptr; } }; /*很多題解沒有講清楚非環部分的長度與相遇點到環起點那部分環之間為何是相等的這個數學關系。這里我就補充下為何他們是相等的。 假設非環部分的長度是x,從環起點到相遇點的長度是y。環的長度是c。 現在走的慢的那個指針走過的長度肯定是x+n1*c+y,走的快的那個指針的速度是走的慢的那個指針速度的兩倍。這意味着走的快的那個指針走的長度是2(x+n1*c+y)。 還有一個約束就是走的快的那個指針比走的慢的那個指針多走的路程一定是環長度的整數倍。根據上面那個式子可以知道2(x+n1*c+y)-x+n1*c+y=x+n1*c+y=n2*c。 所以有x+y=(n2-n1)*c,這意味着什么?我們解讀下這個數學公式:非環部分的長度+環起點到相遇點之間的長度就是環的整數倍。這在數據結構上的意義是什么?現在我們知道兩個指針都在離環起點距離是y的那個相遇點,而現在x+y是環長度的整數倍,這意味着他們從相遇點再走x距離就剛剛走了很多圈,這意味着他們如果從相遇點再走x就到了起點。 那怎么才能再走x步呢?答:讓一個指針從頭部開始走,另一個指針從相遇點走,等這兩個指針相遇那就走了x步此時就是環的起點。*/
3.編寫算法,從10億個浮點數當中,選出其中最大的10000個
用外部排序,在《數據結構》書上有
《計算方法導論》在找到第n大的數的算法上加工 (注意:先將數據進行分割成數據量小的一些文件,如1000000個數據為一個文件,然后將每個文件數據進行排序,用快速排序法排序,然后使用K路合並法將其合並到一個文件下,取出排序好的最大的10000個數據)
4.最長回文子串

class Solution { public: string longestPalindrome(string s) { int len=s.size(); if(len==0||len==1) return s; int start = 0; int end = 0; int mlen = 0; for(int i=0;i<len;i++){ int len1 = help(s,i,i); int len2 = help(s,i,i+1); mlen = max(max(len1,len2),mlen); if(mlen>end-start+1){ start = i-(mlen-1)/2; end = i+mlen/2; } } return s.substr(start,mlen); } int help(string s,int l,int r){ int L=l; int R=r; while(l>=0&&r<s.length()&&s[L]==s[R]){ L--; R++; } return R-L-1; } };
5.插入排序和選擇排序
插入排序基本思想:
(假定從大到小排序)依次從后面拿一個數和前面已經排好序的數進行比較,比較的過程是從已經排好序的數中最后一個數開始比較,如果比這個數,繼續往前面比較,直到找到比它大的數,然后就放在它的后面,如果一直沒有找到,肯定這個數已經比較到了第一個數,那就放到第一個數的前面。
選擇排序(Selection Sort)
是一種簡單直觀的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續尋找最小元素,然后放到排序序列末尾。以此類推,直到所有元素均排序完畢。
6.-1,2,7,28,,126請問28和126中間那個數是什么?為什么?
答 、應該是4^3-1=63
規律是n^3-1(當n為偶數0,2,4)
n^3+1(當n為奇數1,3,5)
9.排序:
7.冒泡排序算法的時間復雜度是什么?
o(n^2)
排序深入探討(https://blog.csdn.net/morewindows/article/details/7961256)
- 插入排序
每次將一個待排序的數據,跟前面已經有序的序列的數字一一比較找到自己合適的位置,插入到序列中,直到全部數據插入完成。
- 希爾排序
先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然后依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。由於希爾排序是對相隔若干距離的數據進行直接插入排序,因此可以形象的稱希爾排序為“跳着插”
- 冒泡排序
通過交換使相鄰的兩個數變成小數在前大數在后,這樣每次遍歷后,最大的數就“沉”到最后面了。重復N次即可以使數組有序。
冒泡排序改進1:在某次遍歷中如果沒有數據交換,說明整個數組已經有序。因此通過設置標志位來記錄此次遍歷有無數據交換就可以判斷是否要繼續循環。
冒泡排序改進2:記錄某次遍歷時最后發生數據交換的位置,這個位置之后的數據顯然已經有序了。因此通過記錄最后發生數據交換的位置就可以確定下次循環的范圍了。
- 快速排序
“挖坑填數+分治法”,首先令i =L; j = R; 將a[i]挖出形成第一個坑,稱a[i]為基准數。然后j--由后向前找比基准數小的數,找到后挖出此數填入前一個坑a[i]中,再i++由前向后找比基准數大的數,找到后也挖出此數填到前一個坑a[j]中。重復進行這種“挖坑填數”直到i==j。再將基准數填入a[i]中,這樣i之前的數都比基准數小,i之后的數都比基准數大。因此將數組分成二部分再分別重復上述步驟就完成了排序。
- 選擇排序
數組分成有序區和無序區,初始時整個數組都是無序區,然后每次從無序區選一個最小的元素直接放到有序區的最后,直到整個數組變有序區。
- 堆排序
堆的插入就是——每次插入都是將新數據放在數組最后,而從這個新數據的父結點到根結點必定是一個有序的數列,因此只要將這個新數據插入到這個有序數列中即可。
堆的刪除就是——堆的刪除就是將最后一個數據的值賦給根結點,然后再從根結點開始進行一次從上向下的調整。調整時先在左右兒子結點中找最小的,如果父結點比這個最小的子結點還小說明不需要調整了,反之將父結點和它交換后再考慮后面的結點。相當於從根結點開始將一個數據在有序數列中進行“下沉”。
因此,堆的插入和刪除非常類似直接插入排序,只不是在二叉樹上進行插入過程。所以可以將堆排序形容為“樹上插”
- 歸並排序
歸並排序主要分為兩步:分數列(divide),每次把數列一分為二,然后分到只有兩個元素的小數列;合數列(Merge),合並兩個已經內部有序的子序列,直至所有數字有序。用遞歸可以實現。
- 基數排序(桶排序)
基數排序,第一步根據數字的個位分配到每個桶里,在桶內部排序,然后將數字再輸出(串起來);然后根據十位分桶,繼續排序,再串起來。直至所有位被比較完,所有數字已經有序。
8.貪心算法
貪心算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的是在某種意義上的局部最優解。
貪心算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具備無后效性,即某個狀態以前的過程不會影響以后的狀態,只與當前狀態有關。
貪心算法兩個重要的特點是:
(1)貪心策略
(2)通過局部最優解能夠得到全局最優解
給定一個非負整數數組,你最初位於數組的第一個位置。數組中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最后一個位置。

class Solution { public: bool canJump(vector<int>& nums) { int k = 0; for (int i = 0; i < nums.size(); i++) { if (i > k) return false;//這里類似於 11101 在第四個斷了 k = max(k, i + nums[i]); } return true; } }; /*如果某一個作為 起跳點 的格子可以跳躍的距離是 3,那么表示后面 3 個格子都可以作為 起跳點。 可以對每一個能作為 起跳點 的格子都嘗試跳一次,把 能跳到最遠的距離 不斷更新。 如果可以一直跳到最后,就成功了。 //1341 在3時 k為2+3=5 下一個k=max(4+3,5)=7 //即遇到一個數之后 不是一次算好范圍 而是接着遍歷 比大小 即整個過程只關注能走的最遠距離 然后與i 作比較 */
9.動態規划
給你一根長度為 n 的繩子,請把繩子剪成整數長度的 m 段(m、n都是整數,n>1並且m>1),每段繩子的長度記為 k[0],k[1]...k[m - 1] 。請問 k[0]*k[1]*...*k[m - 1] 可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別為2、3、3的三段,此時得到的最大乘積是18。
答案需要取模 1e9+7(1000000007),如計算初始結果為:1000000008,請返回 1。

class Solution { public: int cuttingRope(int n) { if(n<4) return n-1; int ans1 = n/3-1; long ans2 =1; while(ans1--){ ans2 = ans2 % 1000000007; ans2 = ans2*3; } if(n%3==1) return (ans2*4)%1000000007; else if(n%3==2) return (ans2*6)%1000000007; else return (ans2*3)%1000000007; } }; //盡可能都是3的組合 這樣的乘積才是最大的
10.快速排序

#include<vector> #include<stack> #include<iostream> #include<algorithm> typedef std::stack<int> Stack; using namespace std; void help(vector<int> &v,int left,int right); int fun(vector<int>&v,int left,int right); int main(){ vector<int> v; int temp; int j=0; while(cin>>temp){ v.push_back(temp); j++; } int right = j-1; help(v,0,right); for(int i=0;i<v.size();i++){ cout<<v[i]<<" "; } return 0; } void help(vector<int> &v,int left,int right){ Stack s; int temp; if(left<right){ int point = fun(v,left,right); s.push(left); s.push(point-1); s.push(point+1); s.push(right); while(!s.empty()){ right = s.top(); s.pop(); left = s.top(); s.pop(); if(left<right){ point = fun(v,left,right); temp = point-1; if(left<temp){ s.push(left); s.push(temp); } temp = point +1; if(temp<right){ s.push(temp); s.push(right); } } } } } int fun(vector<int>&v,int left,int right){ int key = v[left]; while(left<right){ while(left<right&&v[right]>=key) right--; swap(v[left],v[right]); while(left<right&&v[left]<=key) left++; swap(v[left],v[right]); } return left; } /* int main(){ vector<int> v; int temp; int j=0; while(cin>>temp){ v.push_back(temp); j++; } sort(v.begin(),v.end());//哈哈哈 for(auto s:v){ cout<<s<<" "; } return 0; } */
非遞歸形式下的 快速快速排序

#include<iostream> #include<vector> using namespace std; void quickSort(int left, int right, int arr[]) { if (left >= right) return; int i, j, base, temp; i = left, j = right; base = arr[left]; //取最左邊的數為基准數 while (i < j) { while (arr[j] >= base && i < j) j--; while (arr[i] <= base && i < j) i++; if (i < j) { swap(arr[i], arr[j]); } } arr[left] = arr[i]; arr[i] = base; quickSort(left, i - 1, arr);//遞歸左邊 左邊的都是小於基准數的 quickSort(i + 1, right, arr);//遞歸右邊 右邊都是大於基准數的 } int main() { int num[10] = { 21,23,4,7,13,79,33,78,39,89 }; for (auto s : num) cout << s << " "; cout << endl; quickSort(0, 9, num); for (auto s : num) cout << s << " "; cout << endl; return 0; }
while (i < j) { while (arr[j] >= base && i < j) j--; while (arr[i] <= base&&i<j) i++; //這兩個調換位置會出現錯誤 if (i < j) { swap(arr[i], arr[j]); } }
6 1 2 7 9 3 4 5 10 8 第一次交換 6 1 2 5 9 3 4 7 10 8 第二次交換 6 1 2 5 4 3 9 7 10 8 剩下的是3 注意: 他發現了3(比基准數6要小,滿足要求)之后又停了下來。哨兵i繼續向右移動,糟啦!此時哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。說明此時“探測”結束。我們將基准數6和3進行交換。交換之后的序列如下。
3 1 2 5 4 6 9 7 10 8
base = arr[left]
arr[left] = arr[i];
arr[i] = base; 選好基准 然后將基准放在中間 小的放在左邊 大的放在右邊
3 1 2 5 4 6 9 7 10 8
以6為分界點拆分成了兩個序列,左邊的序列是“3 1 2 5 4”,右邊的序列是“9 7 10 8”。接下來還需要分別處理這兩個序列。因為6左邊和右邊的序列目前都還是很混亂的。不過不要緊,我們已經掌握了方法,接下來只要模擬剛才的方法分別處理6左邊和右邊的序列即可。現在先來處理6左邊的序列現吧。
左邊的序列是“3 1 2 5 4”。請將這個序列以3為基准數進行調整,使得3左邊的數都小於等於3,3右邊的數都大於等於3。好了開始動筆吧。
如果你模擬的沒有錯,調整完畢之后的序列的順序應該是。
2 1 3 5 4
OK,現在3已經歸位。接下來需要處理3左邊的序列“2 1”和右邊的序列“5 4”。對序列“2 1”以2為基准數進行調整,處理完畢之后的序列為“1 2”,到此2已經歸位。序列“1”只有一個數,也不需要進行任何處理。至此我們對序列“2 1”已全部處理完畢,得到序列是“1 2”。序列“5 4”的處理也仿照此方法,最后得到的序列如下。
1 2 3 4 5 6 9 7 10 8
對於序列“9 7 10 8”也模擬剛才的過程,直到不可拆分出新的子序列為止。最終將會得到這樣的序列,如下。
1 2 3 4 5 6 7 8 9 10
優點:速度快,剩空間,缺點:非常脆弱,在實現時一定要注意幾個小細節。
什么情況下是最好的呢:
待排序列升序有序O(n),即,1 2 3 4 5 6 7,這種情況下,基准選擇第一個數,調整次數最少,注意只是調試次數減少,比較次數沒變少,
所以從理論上來說,比較只需要把數據放入寄存器,然后比較。
mov ax, mov cx, cmp ax,cx
但實際情況下,因為有L1,L2的存在,然后你的程序又存在進程切換,現場恢復等等各種復雜因素,實際上的速度就好說了。
什么情況下是最差的呢:
待排序序列降序有序O(n2),即,7 6 5 4 3 2 1,這種情況就退化為冒泡排序。
10 冒泡排序

#include <iostream> using namespace std; int main() { int num[10] = {78,12,45,1,23,9,68,456,789,74}; for (int i = 0; i < 10; i++) { for (int j = 0; j < 9 - i; j++) { if (num[j] > num[j + 1]) swap(num[j], num[j + 1]); } } for (auto s : num) { cout << s << " "; } return 0; }
快速排序

int main() { int num[8] = { 2,1,3,4,10,2,4,8 }; help(num,0,7); for (auto s : num) cout << s<<endl; return 0; } void help(int num[], int left, int right){ int i = left; int j = right; int base = num[left]; if (i >= j) return; while (i < j) { while (i<j && num[j]>=base) j--; while (i < j && num[i] <= base) i++; if (i < j) swap(num[i], num[j]); } swap(num[left], num[i]); help(num, left, i - 1); help(num, i + 1, right); } //3個while一個if //開始進去先判斷 swap里面的數 只能是 num[i] num[j] num[left] 即內存的改變
希爾排序

#include<iostream> #include<vector> #include<algorithm> #include<iostream> #include <iostream> using namespace std; void ShellSort(int* iArray, int length) { //初始化jump等於length int jump = length; //標記本趟檢測是否進行了交換, // 若進行了 則還有下次從頭開始的檢測, // 否則停止,繼續改變jump的值 做另一趟排序 bool isSwap; while (jump != 0) { //jump每次/2 jump = jump / 2; do { int i = 1; //初始化表示沒有進行交換 isSwap = false; while (i <= length - jump) { if (iArray[i] > iArray[i + jump]) { int temp = iArray[i]; iArray[i] = iArray[i + jump]; iArray[i + jump] = temp; isSwap = true; } i++; } } while (isSwap == true);//如果進行了交換說明 增量為jump的序列 //可能存在不是有序的 在檢測一遍 //否則說明增量為jump的序列是有序的 } } int main() { int iArray[] = { 0,50,123,36,25,200,36,95,70,14,10,321,1,3,5,8 }; ShellSort(iArray, 15); for (int i = 1; i <= 15; i++) { cout << iArray[i] << " "; } cout << endl; return 0; }
11.寫一個在一個字符串(n)中尋找一個子串(m)第一個位置的函數。
KMP算法 程序
12.如何判斷一個單鏈表是有環的?
class Solution { public: bool hasCycle(ListNode *head) { if(!head) return 0; auto fast = head->next; auto slow = head; while(slow!=fast){ if(fast==NULL||fast->next==NULL) return 0; slow=slow->next; fast=fast->next->next; } return true; } };
13.背包問題

#include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { int m, n1, n; cin >> n1 >> m; n = n1; //int c1, v1; int ans; vector<vector<int>> v(n+1, vector<int>(m+1, 0)); int i, j, temp; vector<int> c_1; vector<int> v_1; for (int i = 0; i < n; i++) { cin >> temp; c_1.push_back(temp); } for (int i = 0; i < n; i++) { cin >> temp; v_1.push_back(temp); } for (i = 1; i <= n1; i++) { for (j = 1; j <= m; j++) { if (c_1[i - 1] <= j) v[i][j] = max(v[i-1][j], v[i - 1][j - c_1[i - 1]] + v_1[i - 1]); else v[i][j] = v[i - 1][j]; } } cout << v[n1][m]; return 0; }
行成 m+1 n+1 矩陣 外圍為 0 然后對於每一個i 比較第i行的數據 然后 j變化 第一個出現的數 可以認為是 在i個物體的情況下,權重w為j的情況下,即前i個物體中,最大值放在了第一個,然后依次類推,放兩個 放3個 放i個物體。
//n個物體 m是容量 接下來都是容量和價值。兩個vector 構建的結果矩陣是v 最后的元素就是最大價值。
//設f[i][j]表示前 i 件物品 總重量不超過 j 的最大價值 可得出狀態轉移方程 //f[i][j] = max{ f[i - 1][j - a[i]] + b[i], f[i - 1][j] }
14. 小島數量問題
出現的小島標記,然后把與之連接的小島都變成水。

class Solution { public: int numIslands(vector<vector<char>>& grid) { int ans = 0; for(int i=0;i<grid.size();i++){ for(int j=0;j<grid[0].size();j++){ if(grid[i][j]=='1') {ans++; help(grid,i,j);} } } return ans; } void help(vector<vector<char>>& grid,int cur_i, int cur_j){ if(cur_i<0||cur_j<0||cur_i==grid.size()||cur_j==grid[0].size()||grid[cur_i][cur_j]=='0') return; grid[cur_i][cur_j] = '0'; int di[4] = {0,0,1,-1}; int dj[4] = {1,-1,0,0}; for(int i=0;i<4;i++){ help(grid,cur_i+di[i],cur_j+dj[i]); } return; } };
15.動態規划
動態規划就是先取得當前情況下的最優解 成立就在原來的基礎上加一 不成立就原地變成1

class Solution { public: int lengthOfLIS(vector<int>& nums) { int n=(int)nums.size(); if (n == 0) return 0; vector<int> dp(n, 1); for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { if (nums[j] < nums[i]) { dp[i] = max(dp[i], dp[j] + 1); } } } sort(dp.begin(),dp.end()); return dp[n-1]; ///return *max_element(dp.begin(), dp.end()); } }; 12301234 11111111 12312345 12345123 11111111 12345123 所以需要用max來計算數組的最大值
16.樹的兩種遍歷得到樹 並按照第三種遍歷方式輸出

#include <iostream> #include<vector> using namespace std; struct TreeNode { char val; TreeNode* left; TreeNode* right; TreeNode(char x) :val(x), left(NULL), right(NULL) {} }; TreeNode* creat_Node1(vector<char>& v1, vector<char>& v2); TreeNode* inOrder(vector<char>& v1, int inL, int inR, vector<char>& v2, int pL, int pR); vector<char> help(TreeNode* root); int main() { string s1, s2; cin >> s1 >> s2; vector<char> v1; vector<char> v2; for (int i = 0; i < s1.size(); i++) { v1.push_back(s1[i]); } for (int i = 0; i < s2.size(); i++) { v2.push_back(s2[i]); } TreeNode* res = creat_Node1(v1, v2); vector<char> ans; ans = help(res); for (auto s : ans) cout << s; return 0; } //創建二叉樹 TreeNode* creat_Node1(vector<char>& v1, vector<char>& v2) { int size = v1.size(); if (size < 1) return nullptr; return inOrder(v1, 0, size - 1, v2, 0, size - 1); } //輸入某二叉樹的前序遍歷pre 和 中序遍歷in 的結果,請重建該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。 TreeNode* inOrder(vector<char>& v1, int inL, int inR, vector<char>& v2, int pL, int pR) { if (inL > inR) return nullptr; TreeNode* root = new TreeNode(v2[pR]); int mid = inL; for (; mid <= inR; ++mid) if (v1[mid] == root->val) break; int leftsize = mid - inL; root->left = inOrder(v1, inL, mid - 1, v2, pL, pL + leftsize - 1); root->right = inOrder(v1, mid + 1, inR, v2, pL + leftsize, pR - 1); return root; } // vector<char> help(TreeNode* root) { vector<char> ans; vector<TreeNode*> first; if (root == nullptr) return ans; first.push_back(root); while (first.size() > 0) { root = first.back(); first.pop_back(); ans.push_back(root->val); if (root->right) first.push_back(root->right); if (root->left) first.push_back(root->left); } return ans; }
17.unordered_map計統計string中出現的char的次數

class Solution { public: int findRepeatNumber(vector<int>& nums) { unordered_map<int,int> m; for(auto s:nums) if(m[s]++ == 1) return s; return -1; } };
18.string中的最長的不重復子串(雙指針+unordered_map)

class Solution { public: int lengthOfLongestSubstring(string s) { int maxLen = 0; if(s.size() == 0) return 0; unordered_set<char> chars; int right = 0; for(int left = 0; left < s.size(); left ++){ while(right < s.size() && !chars.count(s[right])){ chars.insert(s[right]); right ++; } maxLen = max(maxLen, right - left); if(right == s.size()) break; chars.erase(s[left]); } return maxLen; } };
abcddefghi 算法過程就是 abcdd(4) bcdd(4) cdd(4) dd(4) d(4) ………………defghi(6) 結果就是6;
雙指針:left是0—————size()-1 內部是right 隨時比較大小,出現重復就從最左刪除,因為既然有重復,代表重復出現的那一刻就已經有了相對最好解,需要從重復的位置開始找下一個 找重復的位置就是從左到右一個個刪除直到把重復的刪除。問題:是否一旦出現重復就讓left直接定位到重復的下一個 下面的解法用到了

#include<iostream> #include<vector> #include<string> #include<algorithm> using namespace std; int main(){ string s; cin >> s; vector<int> m(128, -1); int res = 0, left = -1; for (int i = 0; i < s.size(); ++i) { left = max(left, m[s[i]]); m[s[i]] = i; res = max(res, i - left); } cout<<res<<endl; return 0; };
abcdabcd vector對每一個的初值為-1 然后計算在賦值為i 這里的i為char的index 這樣的話 一個vector實現了char和index的插入
abcdefcccc
012345678
-1-1-1-1-1-1-1-1
但是都是先-1然后再把-1替換成i什么時候left變???當遇到下一個之前出現過的的 因為i>=0 而初始值為-1 這樣的left就變成了第一個重復的字母的位置,然后再計算。
19.樹的問題
簡單 樹的深度
class Solution { public: int maxDepth(TreeNode* root) { if(!root) return 0; return max(maxDepth(root->left),maxDepth(root->right))+1; } };
二叉樹的鏡像

class Solution { public: TreeNode* mirrorTree(TreeNode* root) { if(!root) return root; help(root); return root; } void help(TreeNode* root){ if(root==nullptr) return; //TreeNode* temp=root->left; //root->left = root->right; //root->right = temp; swap(root->left,root->right); help(root->left); help(root->right); } };
本質上就是swap和迭代 直到nullptr 然后 return
簡單的方法 這個是計算得到數組中的最長的連續遞增子序列的最大長度 遞增的間隔是1

class Solution { public: int longestConsecutive(vector<int>& nums) { // 對整個數組進行排序 sort(nums.begin(), nums.end());//一個sort就是 nlogn;n // 去重操作 unique把不重復的排序 但是長度不變 nums.erase(unique(nums.begin(), nums.end()), nums.end()); // ans是最終答案,cnt用於記錄臨時答案 int ans = 0, cnt = 0; for (int i = 0; i < nums.size(); i++){ if (i > 0 && nums[i] == nums[i-1] + 1) cnt++; else cnt = 0; ans = max(ans, cnt+1); } return ans; } }; //unique()函數是將重復的元素折疊縮編,使成唯一
使用count,返回的是被查找元素的個數。如果有,返回1;否則,返回0。注意,map中不存在相同元素,所以返回值只能是1或0。
使用find,返回的是被查找元素的位置,沒有則返回map.end()。
最長的連續字符子串 區別在於 else 后面的結果直接就是0 不需要再比較大小 1143. 最長公共子序列

class Solution { public: int findLength(vector<int>& A, vector<int>& B) { int n = A.size(), m = B.size(); vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0)); int ans = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if(A[i-1]==B[j-1]) dp[i][j] = dp[i-1][j-1]+1; else dp[i][j] = 0; //連續的就是0 不連續就是max(dp[i-1][j],dp[i][j-1]) ans = max(ans, dp[i][j]); } } return ans; } };
129.有效的括號

class Solution { public: bool isValid(string s) { if(s=="") return true; for(int i=1;i<s.size();){ if(s[i]-s[i-1]==1||s[i]-s[i-1]==2){ s.erase(i-1,2); if(s=="") return true; if(i>1) i--; } else i++; } if(s=="") return true; return false; } };
130.三數之和

class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> result; sort(nums.begin(), nums.end()); // 找出a + b + c = 0 // a = nums[i], b = nums[left], c = nums[right] for (int i = 0; i < nums.size(); i++) { // 排序之后如果第一個元素已經大於零,那么無論如何組合都不可能湊成三元組,直接返回結果就可以了 if (nums[i] > 0) { return result; } // 錯誤去重方法,將會漏掉-1,-1,2 這種情況 /* if (nums[i] == nums[i + 1]) { continue; } */ // 正確去重方法 if (i > 0 && nums[i] == nums[i - 1]) { continue; } int left = i + 1; int right = nums.size() - 1; while (right > left) { // 去重復邏輯如果放在這里,0,0,0 的情況,可能直接導致 right<=left 了,從而漏掉了 0,0,0 這種三元組 /* while (right > left && nums[right] == nums[right - 1]) right--; while (right > left && nums[left] == nums[left + 1]) left++; */ if (nums[i] + nums[left] + nums[right] > 0) { right--; } else if (nums[i] + nums[left] + nums[right] < 0) { left++; } else { result.push_back(vector<int>{nums[i], nums[left], nums[right]}); // 去重邏輯應該放在找到一個三元組之后 while (right > left && nums[right] == nums[right - 1]) right--; while (right > left && nums[left] == nums[left + 1]) left++; // 找到答案時,雙指針同時收縮 right--; left++; } } } return result; } };
131.分糖果

class Solution { public: vector<int> distributeCandies(int candies, int num_people) { vector<int> v(num_people,0); int i=1,index = 0; while(candies){ if((candies-i)>=0) v[index]+=i; else {v[index]+=candies; break;} candies-=i; index++; i++; if(index==num_people) index=0; } return v; } };
分糖果就是123456……n的分 循環
hard版本的分糖果
老師想給孩子們分發糖果,有 N 個孩子站成了一條直線,老師會根據每個孩子的表現,預先給他們評分。
你需要按照以下要求,幫助老師給這些孩子分發糖果:
每個孩子至少分配到 1 個糖果。
相鄰的孩子中,評分高的孩子必須獲得更多的糖果。
那么這樣下來,老師至少需要准備多少顆糖果呢?
示例 1:
輸入: [1,0,2]
輸出: 5
解釋: 你可以分別給這三個孩子分發 2、1、2 顆糖果。

class Solution { public: int candy(vector<int>& ratings) { int res=ratings.size(); int ans=0; vector<int> tmp(res,1); for(int i=1;i<res;i++) if(ratings[i]>ratings[i-1]) tmp[i]=tmp[i-1]+1; //上面的把 123456 這種遞增的考慮了 但是 654321這種遞減的沒有考慮 for(int i=res-1;i>0;i--) if(ratings[i-1]>ratings[i] && tmp[i-1]<tmp[i]+1) //比正序多了一個判斷 tmp[i-1]=tmp[i]+1; //求和 for(int i=0;i<res;i++) ans+=tmp[i]; return ans; } };
132.不用加號計算加法

class Solution { public: int add(int a, int b) { int sum = 0, carry = 0; while(b != 0) { sum = a ^ b; // 異或計算未進位的部分 carry = (unsigned int)(a & b) << 1; // 進位部分 a = sum; // 保存未進位部分,再次計算 b = carry; // 保存進位部分,再次計算 } return a; // 最后無進位,異或的結果即加法結果 } };
133.鏈表求和

class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode *head = new ListNode(-1), *p1 = l1, *p2 = l2, *p = head;//用帶頭節點的可以少一點初始的特判 int sum = 0, carr = 0; while (p1 || p2 || carr) //如果改用&&則while結束還要多一些特判 { sum = 0;//當前兩位數字和 if(p1) { sum += (p1->val); p1 = p1->next; } if(p2) { sum += (p2->val); p2 = p2->next; } sum += carr; //加上上一位的進位 ListNode *t = new ListNode(sum % 10); //得到當前位數字 carr = sum / 10; //得到當前位對下一位的進位 p->next = t;//當前位連接上去 p = p->next;//游標指針更新 } return head->next; } };
完整版本的 需要輸出的 計算求和

#include<iostream> #include<vector> using namespace std; struct ListNode { int val; ListNode* next; ListNode(int a) { val = a; next = NULL; } }; ListNode* help(ListNode* head1, ListNode* head2); int main() { vector<int> v1{ 7,1,6 }; vector<int> v2{ 5,9,6 }; ListNode* head1 = new ListNode(0); ListNode* head11 = head1; ListNode* head2 = new ListNode(0); ListNode* head22 = head2; for (int i = 0; i < v1.size(); i++) { head1->next = new ListNode(v1[i]); head1 = head1->next; } for (int i = 0; i < v2.size(); i++) { head2->next = new ListNode(v2[i]); head2 = head2->next; } //函數的使用 ListNode* ans = help(head11->next,head22->next); while (ans) { cout << ans->val; ans = ans->next; } return 0; } ListNode* help(ListNode* head1, ListNode* head2) { ListNode* ans = new ListNode(0); ListNode* ans1 = ans; int flag = 0; int sum; while (head1 || head2 || flag) { sum = 0; if (head1) { sum += head1->val; head1 = head1->next; } if (head2) { sum += head2->val; head2 = head2->next; } ans->next = new ListNode((sum+flag) % 10);//結點連接 flag = sum / 10; ans = ans->next; } return ans1->next; }
134.計算數組中出現次數最多的那個數

class Solution { public: int majorityElement(vector<int>& nums) { unordered_map<int,int> m; for(auto s:nums){ m[s]++; } int ans=0; map<int,int> m1; for(auto s:m){ m1[s.second]=s.first; } for(auto s:m1) ans=s.second; return ans; } };
135.1234組成沒有重復數字的三位數

int main() { for (int i = 1; i < 5; i++) { for (int j = 1; j < 5; j++) { for (int k = 1; k < 5; k++) { if (i != j && j != k && i != k) { printf("%d%d%d", i, j, k); cout << endl; } } } } return 0; }
136.合並兩個有序鏈表

#include<iostream> #include<vector> using namespace std; struct ListNode { int val; ListNode* next; ListNode(int a) { val = a; next = NULL; } }; ListNode* plus_node(ListNode* head1, ListNode* head2); ListNode* hebing(ListNode* head1, ListNode* head2); int main() { vector<int> v1{ 2,7,9 }; vector<int> v2{ 5,6,10,12}; ListNode* head1 = new ListNode(0); ListNode* head11 = head1; ListNode* head2 = new ListNode(0); ListNode* head22 = head2; for (int i = 0; i < v1.size(); i++) { head1->next = new ListNode(v1[i]); head1 = head1->next; } for (int i = 0; i < v2.size(); i++) { head2->next = new ListNode(v2[i]); head2 = head2->next; } //函數的使用 //ListNode* ans = plus_node(head11->next,head22->next); ListNode* ans = hebing(head11->next, head22->next); while (ans) { cout << ans->val; ans = ans->next; } return 0; } ListNode* hebing(ListNode* head1, ListNode* head2) { ListNode* ans = new ListNode(0); ListNode* ans1 = ans; while (head1 && head2) { if (head2->val > head1->val) { ans1->next = head1; head1 = head1->next; ans1 = ans1->next; } else { ans1->next = head2; head2 = head2->next; ans1 = ans1->next; } } if (!head1) ans1->next = head2; if (!head2) ans1->next = head1;//這兩行很重要 return ans->next; } ListNode* plus_node(ListNode* head1, ListNode* head2) { ListNode* ans = new ListNode(0); ListNode* ans1 = ans; int flag = 0; int sum; while (head1 || head2 || flag) { sum = 0; if (head1) { sum += head1->val; head1 = head1->next; } if (head2) { sum += head2->val; head2 = head2->next; } ans->next = new ListNode((sum+flag) % 10);//結點連接 flag = sum / 10; ans = ans->next; } return ans1->next; }
137.字典找單詞

class Solution { public: bool exist(vector<vector<char>>& board, string word) { for(int i = 0; i < board.size(); i++){ for(int j = 0; j < board[0].size(); j++){ if(backtrack(board, word, 0, i , j)){ // 從二維表格的每一個格子出發 return true; } } } return false; } private: bool backtrack(vector<vector<char>>& board, string& word, int wordIndex, int x, int y){ if( board[x][y] != word[wordIndex]){ // 當前位的字母不相等,此路不通 return false; } if(word.size() - 1 == wordIndex){ // 最后一個字母也相等, 返回成功 return true; } char tmp = board[x][y]; board[x][y] = 0; // 避免該位重復使用 wordIndex++; if((x > 0 && backtrack(board, word, wordIndex, x - 1, y)) // 往左走 || (y > 0 && backtrack(board, word, wordIndex, x, - 1)) // 往上走 || (x < board.size() - 1 && backtrack(board, word, wordIndex, x + 1, y)) // 往右走 || (y < board[0].size() - 1 && backtrack(board, word, wordIndex, x, y + 1))){ // 往下走 return true; // 其中一條能走通,就算成功 } board[x][y] = tmp; // 如果都不通,則回溯上一狀態 return false; } };
138.單詞的壓縮編碼

class Solution { public: int minimumLengthEncoding(vector<string>& words) { set<string> set_1(words.begin(),words.end()); for(auto s:set_1){ for(int i=1;i<s.size();i++) set_1.erase(s.substr(i,s.size()-i));//這個是為了刪除一個string的尾部開始 最后一個 最后兩個 最后三個 //換一種寫法 set_1.erase(s.substr(i) 只有一個參數代表是從后面開始選取string的一部分 } int ans=0; for(auto s:set_1){ ans+=(s.size()+1); } return ans; } };
把string 存入set 然后對每一個元素的子元素字符串(后面開始) 刪除 最后的結果就是set的每一個元素的長度+1 的和
string。substr(i) 從i開始到最后的字符串就是答案。
139.丑數

class Solution { public: int nthUglyNumber(int n) { vector<int> v(1,1); int temp; int ans=1; int i=1; int p2=0,p3=0,p5=0; while(i++<n){ temp = min(min(v[p2]*2,v[p3]*3),v[p5]*5); v.push_back(temp); if(temp==v[p2]*2) p2++; if(temp==v[p3]*3) p3++; if(temp==v[p5]*5) p5++; } return v[v.size()-1]; } };
這個是簡單版本 容易理解的版本 這是把所有的數據全部寫入進去 然后取其中的一個值 定義abc時 需要longlong因為雖然是int但是乘積的時候會超出 所以需要用longlong

class Solution { public: int nthUglyNumber(int n) { vector<int> v; for (long long a=1;a<=INT_MAX;a=a*2) for (long long b=a;b<=INT_MAX;b=b*3) for (long long c=b;c<=INT_MAX;c=c*5) v.push_back(c); sort(v.begin(),v.end()); return v[n-1]; } };
140.刪除鏈表的倒數第k個結點 在這個基礎上還可以輸出倒數某一個節點的值。

#include<iostream> #include<vector> #include<set> #include<limits.h> using namespace std; struct ListNode { int val; ListNode* next; ListNode(int a) { val = a; next = NULL; } }; ListNode* creat(vector<int> v); ListNode* delete_K(ListNode* node, int k); int main() { vector<int> v{ 0,1,2,3,4,5,6 }; ListNode* ans =creat(v); ListNode* temp = delete_K(ans, 3); while (temp) { cout << temp->val; temp = temp->next; } return 0; } ListNode* creat(vector<int> v) { ListNode* ans = new ListNode(-1); ListNode* ans1 = ans; for (int i = 0; i < v.size(); i++) { ans->next = new ListNode(v[i]); ans = ans->next; } return ans1->next; } ListNode* delete_K(ListNode* node, int k) { ListNode* head1 = node; ListNode* head2 = node; ListNode* temp = head2; for (int i = 0; i < k; i++) { head1 = head1->next; } while (head1->next) { head1 = head1->next; head2 = head2->next; } head2->next = head2->next->next;//略過去一個節點 return temp; }
141.旋轉鏈表
首先是k%len得到一個數,因為len就是一個循環,然后對於余數,可以在鏈表的len-余數 的位置使得 node->next = nullptr;
142.兩個字符串的最長公共子串——————動態規划
這是個動態規划的題 動態規划的關鍵在於構建一個二維數組。

#include<iostream> #include<vector> #include<set> #include<limits.h> #include<algorithm> using namespace std; int help(string s1, string s2); int main() { string s1="abcdg"; string s2="defg"; int ans = help(s1, s2); cout << ans; return 0; } int help(string s1, string s2) { int l1 = s1.size(); int l2 = s2.size(); vector<vector<int>> v(l1 + 1, vector<int>(l2 + 1, 0)); for (int i = 1; i < l1 + 1; i++) { for (int j = 1; j < l2 + 1; j++) { if (s2[j-1] == s1[i-1]) v[i][j] = v[i - 1][j - 1] + 1; else v[i][j] = max(v[i - 1][j], v[i][j - 1]); } } return v[l1][l2]; }
背包問題

行成 m+1 n+1 矩陣 外圍為 0 然后對於每一個i 比較第i行的數據 然后 j變化 第一個出現的數 可以認為是 在i個物體的情況下,權重w為j的情況下,即前i個物體中,最大值放在了第一個,然后依次類推,放兩個 放3個 放i個物體。
143.順時針打印矩陣

class Solution { public: vector<int> spiralOrder(vector<vector<int>>& v) { vector<int> v1; if(v.empty()) return v1;//這個很重要 int left=0,up=0,right=v[0].size()-1,low=v.size()-1; while(left<=right&&low>=up){ for(int i=left;i<=right;i++) v1.push_back(v[up][i]); up++; if(up>low) break; for(int i=up;i<=low;i++) v1.push_back(v[i][right]); right--; if(left>right) break; for(int i=right;i>=left;i--) v1.push_back(v[low][i]); low--; if(up>low) break; for(int i=low;i>=up;i--) v1.push_back(v[i][left]); left++; if(left>right) break; } return v1; } };
144.最長回文子串
外部循環每個數然后再選取一定的字符子串判斷是否符合要求,但是問題是 字串的的確定判斷很費事;
而且回文串有個問題 就是 中間一個 或者中間就是一對 aba abba 這種,因此,可以在外部寫循環,但是這個循環要作為字符串的中心點,即i i 或者i i+1
然后一旦num=max(num,len1,len2) len1 len2是兩種方法的結果。 if(num>end-start+1) (left right 數值就刷新) 否則就不刷新
return s.substr(start,num) 從start開始選取num長度的字符子串。

class Solution { public: string longestPalindrome(string s) { int len=s.size(); if(len==0||len==1) return s; int start=0;//記錄回文子串起始位置 int end=0;//記錄回文子串終止位置 int mlen=0;//記錄最大回文子串的長度 for(int i=0;i<len;i++) { int len1=help(s,i,i);//一個元素為中心 int len2=help(s,i,i+1);//兩個元素為中心 mlen=max(max(len1,len2),mlen); if(mlen>end-start+1) //上一步的長度 每計算出 一個 mlen就要計算在該mlen的情況下數據的起始和結束 { start=i-(mlen-1)/2; end=i+mlen/2; } } return s.substr(start,mlen); //substr 從start開始 長度為 mlen //該函數的意思是獲取從start開始長度為mlen長度的字符串 } int help(string s,int left,int right) //計算以left和right為中心的回文串長度 { int L=left; //一個元素的話 必然會執行循環 i<len-1 i=0 的話 len =-1 int R=right; //兩個元素為中心的話 如果不相等 結果就是 0 R-L-1=0 while(L>=0 && R<s.length() && s[R]==s[L]) { L--; R++; } return R-L-1; } };
145.行成3的最大倍數 1363

/* 貪心,先降序sort,然后統計出%3結果為0,1,2的數 把所有數都放進一個mulitiset里面維護 0的無腦放 %3為1和為2的,可能兩兩組合,也可能3個為1或者3個為2的放在一起 當出現1個1,3個2時,或者3個1,1個2時,不進行配對。因為不配對得到的結果更長 */ class Solution { public: string largestMultipleOfThree(vector<int>& digits) { if (digits.empty()) return ""; sort(digits.rbegin(),digits.rend()); vector<int>zero,one,two; for (const auto &digit:digits){ int val = digit % 3; if (val==0){ zero.push_back(digit); }else if (val==1){ one.push_back(digit); }else if (val==2){ two.push_back(digit); } } // printf(" %d %d %d\n",zero.size(),one.size(),two.size()); multiset<int,std::greater<int>>all(zero.begin(),zero.end()); int head = 0; while (head<one.size()&&head<two.size()){ int r1 = one.size()-head; int r2 = two.size()-head; if (r1%3==0&&r2%3==1) break; if (r2%3==0&&r1%3==1) break; all.insert(one[head]); all.insert(two[head]); head++; } while (head+2<one.size()||head+2<two.size()){ if (head+2<one.size()){ all.insert(one[head]); all.insert(one[head+1]); all.insert(one[head+2]); } if (head+2<two.size()){ all.insert(two[head]); all.insert(two[head+1]); all.insert(two[head+2]); } head+=3; } // 只有全是0時,才會出現前綴0,不然總可以把0放后面 // printf("all.size():%d\n",all.size()); if (all.size()>0&&*all.begin()==0) return "0"; string res; // transform(all.begin(),all.end(),back_inserter(res),[](int x){return x+'0';}); for (const auto & x: all){ // cout<<"miao"<<endl; res = res + char(x+'0'); } return res; } };
146.全排列

class Solution { public: vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> ans; if(nums.size()<=1) {ans.push_back(nums);return ans;} help(ans,nums,0,nums.size()-1); return ans; } void help(vector<vector<int>>&ans,vector<int>&nums,int left,int right){ if(left==right) {ans.push_back(nums);return;} for(int i=left;i<=right;i++){ swap(nums[i],nums[left]); help(ans,nums,left+1,right); swap(nums[i],nums[left]); } } };
147.鏈表的排序

class Solution { public: ListNode* insertionSortList(ListNode* head) { //先判斷 是否是單節點或者空 if(!head||!head->next) return head; ListNode* ans=new ListNode(-1); ans->next=head; ListNode* cur = head; ListNode* node= head->next; while(node){ ListNode* temp=ans; if(cur->val>node->val){ while(temp->next->val<node->val) temp=temp->next; cur->next=node->next; node->next=temp->next; temp->next=node; //////////////////////////////////////////////要動起來 node=cur->next; //這里主要是用來移動的 } else{ cur=cur->next; node=node->next; } } return ans->next; } };
148.動態規划 最長的路徑

class Solution { public: int maxValue(vector<vector<int>>& grid) { vector<vector<int>> ans(grid.size(),vector<int>(grid[0].size(),0)); ans[0][0]=grid[0][0]; for(int i=1;i<grid.size();i++) ans[i][0]=grid[i][0]+ans[i-1][0]; for(int i=1;i<grid[0].size();i++) ans[0][i]=grid[0][i]+ans[0][i-1]; for(int i=1;i<grid.size();i++){ for(int j=1;j<grid[0].size();j++){ ans[i][j]=max(ans[i-1][j],ans[i][j-1])+grid[i][j]; } } return ans[grid.size()-1][grid[0].size()-1]; } };
從矩陣的一個點到另一個點,構建一個矩陣和原先的大小一致,關鍵在於 ans[i][j] = max(ans[i-1][j],ans[i][j-1]) + grid[i][j]
string str2=str1.substr(4,3); // 利用substr函數將第一個字符串從第str[4]的位置拷3個,大概是 "are"這個樣子
s.erase(0,2)刪除前兩個元素。
set<int> set(v.begin(), v.end()); 把vector中的數據放入set
string.substr(N) 截取x[N]到結尾