一、给定一个字符串,返回它的最长回文子串。如果存在多个答案,返回任意一个即可。字符串长度 ≤≤ 1000。
算法:(暴力枚举)O(n2)O(n2)
因为字符串长度小于1000,因而咱们能够用 O(n2)O(n2) 的算法枚举所有可能的状况。
首先枚举回文串的核心 ii,而后分两种状况向两边扩大边界,直到遇到不同字符为止:
- 回文串长度是奇数,则顺次判断 s[i−k]==s[i+k],k=1,2,3,…s[i−k]==s[i+k],k=1,2,3,…
- 回文串长度是偶数,则顺次判断 s[i−k]==s[i+k−1],k=1,2,3,…s[i−k]==s[i+k−1],k=1,2,3…
如果遇到不同字符,则咱们就找到了以 ii 为核心的回文串边界。
工夫复杂度剖析:一共两重循环,所以工夫复杂度是 O(n2)O(n2)。
C++代码演示:
class Solution {public: string longestPalindrome(string s) { int res = 0; string str; for (int i = 0; i < s.size(); i ++ ) { for (int j = 0; i - j >= 0 && i + j < s.size(); j ++ ) if (s[i - j] == s[i + j]) { if (j * 2 + 1 > res) { res = j * 2 + 1; str = s.substr(i - j, j * 2 + 1); } } else break; for (int j = i, k = i + 1; j >= 0 && k < s.size(); j -- , k ++ ) if (s[j] == s[k]) { if (k - j + 1 > res) { res = k - j + 1; str = s.substr(j, k - j + 1); } } else break; } return str; }};
二、给定一个整型数组,要求返回两个数的下标,使得两数之和等于给定的目标值,要求同一个下标不能应用两次,数据保障有且仅有一组解。
算法:(暴力枚举) O(n2)O(n2)
暴力枚举办法很简略:两重循环枚举下标 i,ji,j,而后判断 nums[i]+nums[j]nums[i]+nums[j] 是否等于 targettarget。
工夫复杂度:因为有两重循环,所以复杂度是 O(n2)O(n2)。
C++代码演示:
class Solution {public: vector<int> twoSum(vector<int>& nums, int target) { vector<int> res; for (int i = 0; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] + nums[j] == target) { res = vector<int>({j, i}); break; } } if (res.size() > 0) break; } return res; }};
三、给你两个非空链表,别离示意两个非负整数。示意办法:链表中的每个节点,别离示意整数中的一位数字,程序从低位至高位。例如:2 -> 4 -> 3 示意342. 请计算两个整数的和,并返回成链表的模式。数据保障两个整数均不蕴含前导0(除了0之外),例如:不会呈现0023。
算法:(模仿人工加法) O(n)O(n)
这是道模拟题,模仿咱们小时候列竖式做加法的过程:
- 从最低位至最高位,逐位相加,如果和大于等于10,则保留个位数字,同时向前一位进1.
- 如果最高位有进位,则需在最后面补1.
做无关链表的题目,有个罕用技巧:增加一个虚构头结点:ListNode *head = new ListNode(-1);,能够简化边界状况的判断。
C++代码演示:
class Solution {public: ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) { ListNode *res = new ListNode(-1); //增加虚构头结点,简化边界状况的判断 ListNode *cur = res; int carry = 0; //示意进位 while (l1 || l2) { int n1 = l1 ? l1->val : 0; int n2 = l2 ? l2->val : 0; int sum = n1 + n2 + carry; carry = sum / 10; cur->next = new ListNode(sum % 10); cur = cur->next; if (l1) l1 = l1->next; if (l2) l2 = l2->next; } if (carry) cur->next = new ListNode(1); //如果最高位有进位,则需在最后面补1. return res->next; //返回真正的头结点 }};
四、给定一个蕴含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c,使得 a + b + c = 0 ?找出所有满足条件且不反复的三元组。留神:答案中不能够蕴含反复的三元组。
算法:(两重枚举,汇合判重) O(n2)O(n2)
应用 C++ 中的汇合 unordered_set。
- 首先对 nums 进行从小到大排序。
- 两重循环枚举 nums 数组,第一重循环仅枚举不反复的数字。
- 第二重循环须要用两个汇合 hash 和 vis 记录某个数字是否存在,在循环体重咱们做两件事:
- 判断 -nums[st] − nums[i] 是否在 hash 汇合中;若存在,则能够记录进答案;并且须要向 vis 汇合增加数字-nums[st] - nums[i]。若不存在,则向 hash 汇合中增加数字 nums[i]。
- 做完上一步后须要判断 vis 汇合中是否存在数字 -nums[st] - nums[i](显然如果1中刚刚增加过肯定是存在的);若存在,则删除 hash 汇合中数字 -nums[st] - nums[i]。
留神:应用 vis 汇合的目标是避免 -nums[st] - nums[i] 和 nums[i] 数对被反复计算入答案。
工夫复杂度:排序工夫复杂度是 O(nlogn)O(nlogn),枚举的工夫简单的是 O(n2)O(n2),每次须要用汇合操作均匀 O(1)O(1),所以总工夫复杂度为 O(n2)O(n2),但常数较大。
C++代码演示:
class Solution {public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> res; unordered_set<int> hash, vis; sort(nums.begin(), nums.end()); for (int st = 0; st < nums.size(); st++) { while (st != 0 && nums[st] == nums[st - 1]) st++; hash.clear(); vis.clear(); for (int i = st + 1; i < nums.size(); i++) { auto got = hash.find(-nums[st] - nums[i]); if (got == hash.end()) hash.insert(nums[i]); else { res.push_back({nums[st], nums[i], -nums[st] - nums[i]}); vis.insert(-nums[st] - nums[i]); } if (vis.find(-nums[st] - nums[i]) != vis.end()) hash.erase(-nums[st] - nums[i]); } } return res; }};
五、实现 atoi 函数,将以字符串(string)模式示意的整数,转换成整型(int)。
aoti 函数须要满足的条件:
- 疏忽所有行首空格,找到第一个非空格字符,能够是 ‘+/−+/−’ 示意是负数或者正数,紧随其后找到最长的一串间断数字,将其解析成一个整数;
- 整数后可能有任意非数字字符,请将其疏忽;
- 从前往后遍历时,如果第一段间断数字为空,则返回0;
- 如果整数大于INT_MAX,请返回int_MAX;如果整数小于INT_MIN,请返回INT_MIN;
算法:(模仿) O(n)O(n)
这道题的难点在于边界状况十分多,须要认真思考。
做这道题目时,举荐先写一个傻瓜版,而后提交,再依据Wrong Case去逐渐增加针对各种状况的解决。
工夫复杂度剖析:假如字符串长度是 nn,每个字符最多遍历一次,所以总工夫复杂度是 O(n)O(n)。
C++代码演示:
class Solution {public: int strToInt(string str) { int res = 0; int neg = 0; int i = 0; while (i < str.size() && str[i] == ' ') i++; if (str[i] == '-') neg = -1; if (str[i] == '+' || str[i] == '-') ++i; for (; i < str.size(); ++i) { if (str[i] < '0' || str[i] > '9') break; else { int x = str[i] - '0'; if (neg == -1) { x = -x; if ((INT_MIN - x) / 10 <= res) { res *= 10; res += x; } else return INT_MIN; } else { if ((INT_MAX - x) / 10 >= res) { res *= 10; res += x; } else return INT_MAX; } } } return res; }};
6、给出两个排好序的单向链表,返回合并排序后新的单向链表。
链表结点的数据结构:
struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {}};
算法:(线性合并) O(n)O(n)
- 新建头部的爱护结点 dummy,设置 cur 指针指向 dummy。
- 若以后 l1l1 指针指向的结点的值 val 比 l2l2 指针指向的结点的值 val 小,则令 cur 的 next 指针指向 l1l1,且 l1l1 后移;否则指向 l2l2,且 l2l2 后移。
- 而后 cur 指针依照上一部设置好的地位后移。
- 循环以上步骤直到 l1l1 或 l2l2 为空。
- 将残余的 l1l1 或 l2l2 接到 cur 指针后边。
工夫复杂度:两个链表各遍历一次,所以工夫复杂度为 O(n)O(n)。
C++代码演示:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode *dummy = new ListNode(0); ListNode *cur = dummy; while (l1 != NULL && l2 != NULL) { if (l1 -> val < l2 -> val) { cur -> next = l1; l1 = l1 -> next; } else { cur -> next = l2; l2 = l2 -> next; } cur = cur -> next; } cur -> next = (l1 != NULL ? l1 : l2); return dummy -> next; }};
材料收费支付
首先祝贺您,可能认真的浏览到这里,如果对局部了解不太明确,倡议先将文章珍藏起来,而后对不分明的知识点进行查阅,而后在进行浏览,相应你会有更深的认知。如果您喜爱这篇文章,就点个赞或者【关注我】吧!!