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]; }
背包问题
View Code
行成 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]到结尾

