1 Two Sum - input array is stored
题目描述:
在一个”增序“的整数数组里找到两个数,使它们的和为定值。已知有且只有一对解。
输入输出样例:
输入是一个数组numbers和一个定值target,输出是两个数的位置下标,从0开始计数。
Input: numbers = [2,7,11,15], target = 9;
Output: [0,1]
Input: nums = [3, 2, 4], target = 6
Output: [1,2]
题解:
首先输入为乱序数组;输出为数组序列号从0开始。
解题方法:
1 一遍哈希法
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> num_map; //建立哈希表
for (int i = 0; i < nums.size(); i++){
auto it = num_map.find(target - nums[i]); //寻找配对元素
if (it != num_map.end()){ //假设数组中不存在重复元素
return vector<int>{ it->second, i }; //返回配对数的下标和原数的下标
}
num_map[nums[i]] = i; //哈希表中的存储情况
}
return vector<int>();
}
};
此处用到了map语法,由于笔者对老本行已经忘记的差不多了,下面对map知识进行补充。
1 map是STL的一个关联容器,提供一对一的数据处理能力。其内部自建一棵红黑树,这个树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的。
2 map的构造函数
map<int, string> mapStudent;
在构造map容器后,插入数据 之 insert--Pair数据
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1,"student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
map<int, string>::iterator iter;
for(iter=mapStudent.begin(); iter!=mapStudent.end(); iter++){
//iter->first会得到关键字,iter->second会得到值。
cout<<iter->first<<" "<<iter->second<<endl;
}
2 两遍哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> num_map; //定义无序哈希表
for (int i = 0; i < nums.size(); i++){
num_map[nums[i]] = i;
//将数组存入哈希表中,其中num_map关键字里面的值为数组中元素值,其值为数组下标
}
for (int i = 0; i < nums.size(); i++){
auto it = num_map.find(target - nums[i]); //为数组中元素寻找配对
// 此处注意不要遗漏去重判断,两者不能为同一个数
if (it != num_map.end() && it->second != i){
return vector<int>{ i, it->second }; //返回下标
}
}
return vector<int>();
}
};
2 Add Two Numbers
题目
给出两个非空链表用于表示两个非负的整数,其中它们各自的位数是按照逆序的方式存储的,并且它们的每个节点都只能存储一位数字。将两个数相加,会返回一个新的链表表示两数之和。
e.g.
Input: (2->4->3)+(5->6->4)
Output: 7->0->8
342+465=807
解法
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* ans = new ListNode(0);
ListNode* ansPosi = ans;
bool addOne; //也可用整数{0,1}表示,经测试效果相同
while (l1 != NULL || l2 != NULL || addOne) {
//取出该位数值
int num1 = 0, num2 = 0;
if (l1 != NULL) {
num1 = l1 -> val;
l1 = l1 -> next;
}
if (l2 != NULL) {
num2 = l2 -> val;
l2 = l2 -> next;
}
//相加、进位
int sum = num1 + num2;
if (addOne) sum++;
addOne = false;
if (sum > 9) {
addOne = true;
sum = sum - 10;
}
// 写入答案链表
ListNode *nextPosi = new ListNode(sum);
ansPosi -> next = nextPosi;
ansPosi = ansPosi -> next;
}
return ans -> next; //考虑代码简洁性,从答案链表第二位开始写入,因此返回第二位
}
};
3 Longest Substring Without Repeating Characters
对于最长无重复子串,较为不错的方法为 滑动窗口 法,其步骤描述如下:
① i,j分别代表窗口左右边界,最大不重复子串的值就是这个窗口能达到的值;
② 判断j++处的字符是否在窗口中,若无就加入窗口再后移。
③随着j的增加,在窗口中有重复的值,然后开始i++,直至窗口内无重复元素;
④ 重复以上步骤,直至j到头为止,而后返回窗口内元素总数。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
set <char> t; //子集t代表滑动窗口
//left,right分别代表窗口的左右下标,均从0开始
int res=0, left =0, right=0;
while(right<s.size()){
if(t.find(s[right])==t.end()){ //滑动窗口的最后一个元素添加到set t中
t.insert(s[right++]);
res= max(res,(int)t.size()); //res表示滑动窗口大小
}else{ //遇到重复值
t.erase(s[left++]); //重复值除去最先添加的在左边的值
}
}
return res;
}
};
4 Median of Two Sorted Arrays
功能:返回两个有序字符串的中位数。
方法:用一个新的数组存放num1和num2合并后的数组,再取其中位数。
class Solution{
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2){
vector<int> numsAll; //用来存放合并后的两个数组的新数组
m=nums1.size();
n=nums2.size();
for (int i=0,j=0; i< m()||j< n();){
if(i<m){
if(j<n){
if(nums1[i]<nums2[j]){
numsAll.push_back(nums1[i++]); //小的放前面
}else{
numsAll.push_back(nums2[j++]);
}
}else{
numsAll.push_back(nums1[i++]);
}
}else{
numsAll.push_back(nums2[j++])
}
}
int middle = (nums1.size()+nums2.size())>>1; //右移一位
if((nums1.size()+nums2.size())%2 == 1){
return (double)numsAll[middle];
}else{
return ((double)numsAll[middle]+(double)numsAll[middle-1])/2.0;
}
}
};
5 Longest Palindromic Substring最长回文子串
法1 : 中心扩展法
① 回文子串长度为奇数,往两边扩展,假设中心字符下标为i,只要有下标为i-1, i+1的两个字符串相等,那么回文子串的长度加2,继续向两边扩展,直到找到两个相同字符串或者到达原字符串边界时停止。
② 回文子串长度为偶数,对比i及i+1,如果二者不同,继续向外扩展,直到相同或超出字符串边界为止。
这种方法过于麻烦,改了一位bloger的代码发现出现Time Limit Exceeded.超时了!而且总耗时大,请直接跳到法2 manacher.
法2: Manacher法
① 字符串预处理
在开头结尾以及每个字符两边均加上“#”,使得n长的数组变成3n大小。以减少处理偶数子串的麻烦;
② 从左到右依次处理每个字符;
③ 对每个字符,先判断它是否位于某个回文串内,如果是,则以它为中心的串的长度至少为到其对称位置上的长度,如果不是,则继续依次搜索。并在搜索过程中记录当前发现最长串的位置。法1耗时26ms, 法2耗时8ms.
class Solution{
public:
string processString(const string &s){
string str = "^#";
for (int i=0;i<s.size();i++){
str+=s[i];
str+="#";
}
str+="$";
return str;
}
string longestPalindrome(string s){
string str = processString(s);
int ans = 0; //结果串的长度
int ans_pos = 0; //结果串的中心
int id=0; //搜索最远回文串的中心
int mx=0;
vector<int> p(str.size(),0);
for(int i=1;i<str.size()-1;i++){
//如果前面已经遍历过,则直接和对称位置比较
p[i]=mx>i?min(p[2*id-i],mx-i):1;
while(str[i+p[i]]==str[i-p[i]])p[i]++;
if(i+p[i]>mx){
mx=i+p[i];
id=i;
}
if(p[i]>ans){
ans=p[i];
ans_pos=i;
}
}
return s.substr((ans_pos-ans)/2,ans-1);
}
};