前言
- 题目前标号即为leetcode中对应的题号
- 此文是刷题后的个人总结
9.回文数
题目
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
class Solution {
public:
bool isPalindrome(int x) {
//此处填写解答
}
};
思路
- 用字符串操作就很简单, 先筛掉负数, 因为负号不会在数的末尾出现, 再转成字符串然后翻转,比较一下翻转前后的字符串是否相等即可
- 不用字符串也好做, 循环除以10取余数并存起来, 然后从首尾开始向中间遍历比较即可
class Solution {
public:
bool isPalindrome(int x) {
// 负数有负号所以都不满足回文
if(x<0) return false;
vector<int> tmp;
do{
tmp.push_back(x%10);
x/=10;
}while(x);
int len=tmp.size();
for(int i=0,j=len-1;i<=j;i++,j--){
if(tmp[i]!=tmp[j]){
return false;
}
}
return true;
}
};
13.罗马数字转整数
题目
罗马数字包含以下七种字符: I
, V
, X
, L
,C
,D
和 M
。
字符 | 数值 |
---|---|
I | 1 |
V | 5 |
X | 10 |
L | 50 |
C | 100 |
D | 500 |
M | 1000 |
例如, 罗马数字 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 的范围内。
示例 1:
输入: “III”
输出: 3
示例 2:
输入: “IV”
输出: 4
示例 3:
输入: “IX”
输出: 9
示例 4:
输入: “LVIII”
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: “MCMXCIV”
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
输入的字符串不包含双引号" "
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IC 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
class Solution {
public:
int romanToInt(string s) {
//此处填写解答
}
};
思路
罗马数字和阿拉伯数字阅读顺序一样, 比较简单, 使用switch逐位操作即可
class Solution {
public:
int romanToInt(string s) {
// 罗马数字和十进制顺序一样, 直接遍历s, 做一些特判即可
int idx=0;// 标记当前处理位置
int ans=0;
int len=s.length();
// 逐位处理
while(idx<len){
char c=s[idx++];
switch(c){
case 'I':
if(s[idx]=='V'){
ans+=4;
idx++;
}else if(s[idx]=='X'){
ans+=9;
idx++;
}else{
ans+=1;
}
break;
case 'V':
ans+=5;
break;
case 'X':
if(s[idx]=='L'){
ans+=40;
idx++;
}else if(s[idx]=='C'){
ans+=90;
idx++;
}else{
ans+=10;
}
break;
case 'L':
ans+=50;
break;
case 'C':
if(s[idx]=='D'){
ans+=400;
idx++;
}else if(s[idx]=='M'){
ans+=900;
idx++;
}else{
ans+=100;
}
break;
case 'D':
ans+=500;
break;
case 'M':
ans+=1000;
break;
default:
break;
}
}
return ans;
}
};
14.最长公共前缀
题目
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:
输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z
。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
//此处填写解答
}
};
思路
感觉自己的思路不好, 参考了大佬的想法, 非常简洁
- 选择第一个串作为初始, 逐个和字符串数组里面的元素比较, 如果不是前缀就把它缩短一位, 直到找到满足所有字符串的前缀, 因为字串的长度是从大到小变化的, 所以第一个满足要求的字串就一定是最大前缀
- 涉及两个string类函数的使用:
str.find()
和str.substr()
size_t find (const string& str, size_t pos = 0) const noexcept;
str: 带查找字串
pos: 从pos位置开始查找, 包括pos
返回str第一次出现的位置
string substr (size_t pos = 0, size_t len = npos) const;
从pos位置开始, 包括pos, 获得长度为len的字符串
- 空字符串要用
""
, 用''
会报错
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string ans=strs.empty()?"":strs[0];
for(string i:strs){
while(i.find(ans)!=0){
ans=ans.substr(0,ans.length()-1);
}
}
return ans;
}
};
20.有效的括号
题目
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
class Solution {
public:
bool isValid(string s) {
//此处填写解答
}
};
思路
- 考虑到括号匹配的特性, 可以用
stack
模拟, 另外再用map
辅助查找 - 把右括号作为key, 左括号作为value, 建立map
- 遇到左括号就压栈, 遇到右括号就弹栈
- 如果弹栈时栈为空, 或者栈顶元素和右括号不匹配, 则return false, 遍历完了之后如果栈内还有元素, 则return false; 剩余情况return true
class Solution {
public:
bool isValid(string s) {
stack<char> tmp;
map<char,char> m;
m[')']='(';m[']']='[';m['}']='{';
int idx=0;
int len=s.length();
while(idx<len){
char c=s[idx];
idx++;
switch(c){
case '(':case '[':case '{':
tmp.push(c);
break;
case ')':case ']':case '}':
if(tmp.empty()) {
return false;
}
if(tmp.top()!=m[c]){
return false;
}else{
tmp.pop();
}
break;
default:break;
}
}
if(!tmp.empty()){
return false;
}
return true;
}
};
21.合并两个有序链表
题目
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
//此处填写解答
}
};
思路
- 想遍历两个链表逐个比较然后链接, 所以想到用一个头节点和一个标志临时位置指针进行操作, 返回时直接返回头节点->next, 这样可以应对空链表的情况, 也可以很好地把操作过程封装进while里, 不需要做预处理
- 注意创造一个头节点要用
new ListNode(x)
而不是& ListNode(x)
, 因为不能对临时变量取地址
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// 注意new而不能&, 后者会报错:
//taking the address of a temporary object of type 'ListNode'
ListNode* head=new ListNode(0);
ListNode* p=head;
while(l1&&l2){
if(l1->val<l2->val){
p->next=l1;
l1=l1->next;
}else{
p->next=l2;
l2=l2->next;
}
p=p->next;
}
//最后不要忘了对剩余链表进行处理, 此时必有一个链表已经为空,
//把另一个链表剩余的部分接上去即可
p->next=l1?l1:l2;
return head->next;
}
};
- 还可以用递归方法, 更加简洁
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(!l1){
return l2;
}
if(!l2){
return l1;
}
if(l1->val<l2->val){
l1->next=mergeTwoLists(l1->next,l2);
return l1;
}else{
l2->next=mergeTwoLists(l1,l2->next);
return l2;
}
}
};