声明:很多是我当时刷题过程中,看到许多优秀的思路和解法,特意记录下来,也有自己的理解。
一是自用,方便回顾:二是瞻仰,既然自己菜,那就多学习大佬们优秀的想法。
前言
第一次做不出来不要紧,菜不要紧,关键你得
7.整数反转
题目:给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
解题思路:
最大的值与最小的值为:[−2^31, 2^31 − 1]即:[-2147483648, 2147483647]
通过循环将数字x的每一位拆开,在计算新值时每一步都判断是否溢出。
溢出条件有两个,一个是大于整数最大值INT_MAX,另一个是小于整数最小值INT_MIN
设当前结果为result,下一位为pop;
从result * 10 + pop > INT_MAX 这个溢出条件来看
当出现 result > INT_MAX / 10 且 还有pop需要添加时,则一定溢出
当出现 result == INT_MAX / 10 且 pop > 7 时,则一定溢出,7是2^31 - 1的个位数
从ans * 10 + pop < INT_MIN 这个溢出条件来看
当出现 ans < INT_MIN / 10 且 还有pop需要添加 时,则一定溢出
当出现 ans == INT_MIN / 10 且 pop < -8 时,则一定溢出,8是-2^31的个位数
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Solution { public: int reverse(int x) { int result=0; while(x!=0) { int pop=x%10; if(result>INT_MAX/10||(result==INT_MAX/10&&pop>7)) return 0; if(result<INT_MIN/10||(result==INT_MIN/10&&pop<(-8))) return 0; result=result*10+pop; x/=10; } return result; } };
|
9.回文数
题目:判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
解题思路:
一开始感觉跟第7题有些类似,试着下,然后可以了。
首先负数可以第一时间排除,注意0,0也是个回文数。采用数学的方法(模运算),将每一位数拆分,先乘10再相加。最后对比结果。
注意,int型数的最大数,反正会产生溢出,所以要使用范围更大的数,如long int型
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Solution { public: bool isPalindrome(int x) { if(x<0) return false; int y=x; long int result=0; while(y!=0) { int pop=y%10; result=result*10+pop; y/=10; } if(result==x) return true; else return false; } };
|
11.盛最多水的容器
题目:给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
解题思路:
经典一道双指针题目。
在初始时,左右指针分别指向数组的左右两端。我们开始移动指针,移动哪一个呢?观察发现,容纳的水量是两个指针指向的数字中较小值 × 指针之间的距离。所以移动对应数字较小的那个指针。每一次将容纳的水量记录下来,最大者则为最后结果。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Solution { public: int maxArea(vector<int>& height) { int ans=0; int i=0,j=height.size()-1; while(i<j) { int area=(j-i)*min(height[i],height[j]); if(area>ans) ans=area; if(height[i]<height[j]) i++; else if(height[i]>height[j]) j--; else { i++;j--; } } return ans; } };
|
解题方法来自:https://leetcode-cn.com/u/happyfire/
12.整数转罗马数字
题目:

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
- I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
- X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
- C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
解题思路:
这题官方解答有两种思路,一是贪心,二是硬编码数字。其二者时间复杂度和空间复杂度都一样,但贪心易拓展。
首先,枚举出罗马数字与整数一一对应的表。然后从最大的符号开始
给定的整数减去最大的符合后,如果余数大于符号对应的数,则循环while,每一次取的符号加入到字符串res中,直到余数为0
举例:假如给定的整数是2222
-
第一步:2222-1000=1222,余数大于当前符号1000,再循环while —>M
-
第二步:1222-1000=222,余数小于当前符号1000,不循环while;—>M
-
第三步:222-100=122,余数大于当前符号100,再循环while; —>C
-
第四步:122-100=22,余数小于当前符号100,不循环while; —>C
-
第五步:22-10=12,余数大于当前符号10,再循环while; —>X
-
第六步:12-10=2,余数小于当前符号10,不循环while; —>I
-
……
结果为MMCCXII
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public: string intToRoman(int num) { vector<int> values={1000,900,500,400,100,90,50,40,10,9,5,4,1}; vector<string> strs={"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"}; string res; for(int i = 0; i < values.size(); i++){ while(num >= values[i]) { res += strs[i]; num -= values[i]; } } return res; } };
|
第一眼,哇!真的太厉害。简洁,思路清晰!
代码来自:https://blog.csdn.net/qq_31820761/java/article/details/90646907
19.删除链表的倒数第n个节点
解题思路:
-
求出链表的长度
-
找到删除结点的前驱结点
解释x:x是作所要删除的前驱指针指针移动的次数,倒数第n个就是正数的总长-n+1,所以x=n-1=length-n-1

代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { int length=1; ListNode* temp=head; while(temp->next) { temp=temp->next; length++; } if(n==length) return head->next; int x=length-n-1; ListNode* p=head; ListNode* q=head->next; while(x>0) { q=q->next; p=p->next; x--; } p->next=q->next; return head; } };
|
61.旋转链表
如果是空链表或者不移动(k=0),直接返回原链表。
求出链表长度,因为k可能是个大于链表长度,所以要进行模运算。
然后将链表首尾连接,再找到length-k%length结点,进行断开,返回的链表从这个结点的下一个结点开始。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Solution { public: ListNode* rotateRight(ListNode* head, int k) { if(k==0||!head) return head; int length=1; ListNode *p=head; while(p->next) { p=p->next; length++; } if(k%length==0) return head; p->next=head; int x=length-k%length; while(x--) { p=p->next; } ListNode *res=p->next; p->next=NULL; return res; } };
|
82.删除排序链表中的重复元素 II
指针是个好东西。快慢指针更是厉害
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Solution { public: ListNode* deleteDuplicates(ListNode* head) { ListNode *dummy=new ListNode(-1); ListNode *fast=head; ListNode *slow=dummy; dummy->next=head; while(fast&&fast->next) { if(fast->val!=fast->next->val) { if(slow->next==fast) slow=fast; else slow->next=fast->next; } fast=fast->next; } if(slow->next!=fast) slow->next=fast->next; return dummy->next; } };
|
98.验证二叉搜索树
- 当前节点的值是其左子树的值的上界(最大值)
- 当前节点的值是其右子树的值的下界(最小值)
OK,有了这个想法,你可以将验证二叉搜索树的过程抽象成下图(-00代表无穷小,+00代表无穷大):

首先来看在这道题中的终止两种终止条件:
- 当当前节点为空时,表示这个节点已经是叶子节点,这个节点没有子节点,可以返回 True
- 当当前节点不在 [ min_value,max_value ] 的区间时,这个节点不能同时符合二叉搜索树的两个特征,返回 False
然后看看递归方程,由于节点有两个子树,所以我们有两个递归方程要执行:
- 对左子树:heaper(root->left,lower,root->val) 解释:当前节点是左子树的上界(你可以粗略地理解为最大值)
- 对右子树:heaper(root->right,root->val,upper)解释同上
这个递归过程是最难理解的地方,如果不理解,你可以看一看上图中绿色剪头,你会很快理解这么递归的原因。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Solution { public: bool heaper(TreeNode* root,long long lower,long long upper) { if(root==NULL) return true; if(root->val<=lower||root->val>=upper) return false; return heaper(root->left,lower,root->val)&&heaper(root->right,root->val,upper); } bool isValidBST(TreeNode* root) { return heaper(root,LONG_MIN, LONG_MAX); } };
|
102.二叉树的层次遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> ans; queue<TreeNode*> q; q.push(root); while(!q.empty()) { vector<int> res; int n=q.size(); for(int i=0;i<n;i++) { TreeNode* temp=q.front(); q.pop(); res.emplace_back(temp->val); if(temp->left) q.push(temp->left); if(temp->right) q.push(temp->right); } ans.emplace_back(res); } return ans; } };
|
617.合并二叉树
其实也不难,一开始脑袋昏昏,一看提交发现,原来那么简单。
递归法,到了树这一块,递归是常见又重要的解题思路了
我们可以对这两棵树同时进行前序遍历,并将对应的节点进行合并。在遍历时,如果两棵树的当前节点均不为空,我们就将它们的值进行相加,并对它们的左孩子和右孩子进行递归合并;如果其中有一棵树为空,那么我们返回另一颗树作为结果;如果两棵树均为空,此时返回任意一棵树均可(因为都是空)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Solution { public: TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { if(t1==NULL) return t2; if(t2==NULL) return t1; t1->val+=t2->val; t1->left=mergeTrees(t1->left,t2->left); t1->right=mergeTrees(t1->right,t2->right); return t1; } };
|
637.二叉树的层平均值
层序遍历,就要用到队列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Solution { public: vector<double> averageOfLevels(TreeNode* root) { vector<double> res; queue<TreeNode*> q; q.push(root); while(!q.empty()) { double sum=0; int n=q.size(); for(int i=0;i<n;i++) { TreeNode* tmp=q.front(); q.pop(); sum+=tmp->val; if(tmp->left) q.push(tmp->left); if(tmp->right) q.push(tmp->right); } res.push_back(sum/n); } return res; } };
|