4、Median of Two Sorted Arrays(*)
题目
题目要求找到两个排序数组的中位数。
中位数的定义:当n为奇数时,median = array[n/2];当n为偶数时,median = (array[n/2]+array[n/2+1])/2.
暴力算法,两个数组归并排序,对合并的数组求中位数。代码如下:
1 class Solution { 2 public: 3 double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { 4 vector<int> res; 5 6 int i=0,j=0; 7 int length1 = nums1.size(); 8 int length2 = nums2.size(); 9 10 while (i<length1 && j<length2) 11 { 12 if (nums1[i] < nums2[j]) 13 { 14 res.push_back(nums1[i]); 15 i++; 16 } 17 else 18 { 19 20 res.push_back(nums2[j]); 21 j++; 22 } 23 } 24 while (i<length1) 25 { 26 res.push_back(nums1[i]); 27 i++; 28 } 29 while (j<length2) 30 { 31 res.push_back(nums1[j]); 32 j++; 33 } 34 35 if((length2 + length1) % 2 == 1) 36 return res[(length2 + length1)/2]; 37 else 38 { 39 return (res[(length2 + length1)/2] + res[(length2 + length1)/2 + 1])/2; 40 } 41 42 } 43 };
但是,题目要求时间复杂度为O(log(m+n)),因此上面的算法是不符合要求的,因为其时间复杂度为O(m+n)。
如果时间复杂度为O(log(m+n)),肯定会用到二分查找算法。问题是现在有两个数组,怎样灵活的使用二分查找呢?这就需要开动脑筋了
5、Longest Palindromic Substring(*)
题目要求寻找字符串中最长的回文子串。
国际惯例,第一想法:暴力求解,固定一个自创s[i,j]判断其是否为回文串,代码如下:
1 class Solution { 2 public: 3 string longestPalindrome(string s) { 4 if("" == s) 5 return s; 6 7 int i,j; 8 int length = s.size(); 9 string sub; 10 int max 11 string res = ""; 12 for(i=0;i<length;i++) 13 { 14 for (j=i;j<length;j++) 15 { 16 sub = s.substr(i,j-i+1); 17 if(isPalindromic(sub)) 18 { 19 if(sub.length() > max) 20 res = sub; 21 } 22 23 } 24 } 25 return res; 26 } 27 bool isPalindromic(string sub) 28 { 29 int length = sub.size(); 30 int i,j=length-1; 31 while(i<j) 32 { 33 if(sub[i] != sub[j]) 34 return false; 35 i++; 36 j--; 37 } 38 return true; 39 } 40 };
该算法时间复杂度是O(n*n),当n比较大的时候,算法的性能比较低,不能很快的解决问题。导致算法性能低下的原因是产生了回溯。如果能避免回溯,算法性能则能显著提高。
看到这道题目的时候,我们会联想到一道经典的算法题目“最长公共子串”,即求两个字符串最长公共子串(LCS),解决LCS的算法是利用动态规划,重点是求出一个二维数组,其实现代码参考如下:
1 string LCS(string s1,string s2) 2 { 3 4 int len1 = s1.length(); 5 int len2 = s2.length(); 6 7 char*c=malloc(len2),*p; 8 9 int start,end,len,i,j; 10 end=len=0; 11 12 for(i=0; i<len1; i++) //串1从前向后比较 13 { 14 //串2从后向前比较,为什么要从后向前呢?是把一维数组c[ ]当二维数组来用, 15 16 // for(j=0;j<lenRight;j++)//当c申明为一个二维数组时 17 for(j=len2-1; j>=0; j--) 18 { 19 if(s1[i] == s2[j])//元素相等时 20 { 21 if(i==0||j==0) 22 c[j]=1;//c[i][j]=1; 23 else 24 { 25 c[j]=c[j-1]+1;//c[i][j]=c[i-1][j-1]+1; 26 } 27 } 28 else 29 c[j] = 0; //c[i][j]=0; 30 if(c[j] > len) //if (c[i][j]>len) 31 { 32 len=c[j]; //len=c[i][j]; 33 end=j; 34 } 35 } 36 } 37 start=end-len+1; 38 39 //数组p纪录最长公共子串 40 p =(char*)malloc(len+1); 41 for(i=start; i<=end; i++) 42 { 43 p[i-start] = right[i]; 44 } 45 p[len]='\0'; 46 string res(p); 47 return res; 48 }
LCS算法中,是利用空间来换取时间。时间复杂度为O(len1+len2);
现在回到最长回文子串中,通过观察可以得知,最长回文子串其实就是将原始的字符串与其逆序之后的字符串的最长公共子串,因此,解决这个问题可以采用如下方法,先将原始字符串s逆序变为s‘,然后求其最长公共子串,代码如下:
1 class Solution { 2 public: 3 string longestPalindrome(string s) 4 { 5 if("" == s) 6 return s; 7 8 string temp = s; 9 reverse(temp.begin(),temp.end()); 10 11 return LCS(s,temp); 12 } 13 string LCS(string s1,string s2) 14 { 15 16 int len1 = s1.length(); 17 int len2 = s2.length(); 18 19 char *c=new char[len2],*p; 20 21 int start,end,len,i,j; 22 end=len=0; 23 24 for(i=0; i<len1; i++) //串1从前向后比较 25 { 26 //串2从后向前比较,为什么要从后向前呢?是把一维数组c[ ]当二维数组来用, 27 28 // for(j=0;j<lenRight;j++)//当c申明为一个二维数组时 29 for(j=len2-1; j>=0; j--) 30 { 31 if(s1[i] == s2[j])//元素相等时 32 { 33 if(i==0||j==0) 34 c[j]=1;//c[i][j]=1; 35 else 36 { 37 c[j]=c[j-1]+1;//c[i][j]=c[i-1][j-1]+1; 38 } 39 } 40 else 41 c[j] = 0; //c[i][j]=0; 42 if(c[j] > len) //if (c[i][j]>len) 43 { 44 len=c[j]; //len=c[i][j]; 45 end=j; 46 } 47 } 48 } 49 start=end-len+1; 50 51 //数组p纪录最长公共子串 52 p =(char*)malloc(len+1); 53 for(i=start; i<=end; i++) 54 { 55 p[i-start] = s2[i]; 56 } 57 p[len]='\0'; 58 string res(p); 59 return res; 60 } 61 };
另外一种方法就是借鉴LCS的思想,直接构造解决该问题的实现算法。代码如下(网上摘抄):
class Solution { public: string longestPalindrome(string s) { int len = s.length(), max = 1, ss = 0, tt = 0; bool flag[len][len]; for (int i = 0; i < len; i++) for (int j = 0; j < len; j++) if (i >= j) flag[i][j] = true; else flag[i][j] = false; for (int j = 1; j < len; j++) for (int i = 0; i < j; i++) { if (s[i] == s[j]) { flag[i][j] = flag[i+1][j-1]; if (flag[i][j] == true && j - i + 1 > max) { max = j - i + 1; ss = i; tt = j; } } else flag[i][j] = false; } return s.substr(ss, max); } };
------------------------------------------------------------------------------------------------------分割线--------------------------------------------------------------------------
6、ZigZag Conversion
这道题目没有什么可说的,直接代码;
1 class Solution { 2 public: 3 string convert(string s, int nRows) { 4 if (nRows <= 1 || s.length() == 0) 5 return s; 6 int index=0,temp; 7 string res=""; 8 int len = s.length(); 9 const int val = 2*nRows-2; 10 //注意第一行和最后一行的规律 11 for (index = 0;index<nRows&&index<len;index++)//注意当nrows>len时 12 { 13 temp = index; 14 res+=s[temp]; 15 while (temp<len) 16 { 17 temp = temp + val; 18 if(index== 0||index == nRows-1) 19 { 20 if (temp<len) 21 { 22 res+=s[temp]; 23 continue; 24 } 25 break; 26 27 28 } 29 else 30 { 31 temp = temp-2*index; 32 if (temp<len) 33 { 34 res+=s[temp]; 35 } 36 else 37 break; 38 temp = temp+2*index; 39 if (temp<len) 40 { 41 res+=s[temp]; 42 } 43 else 44 break; 45 46 } 47 } 48 49 } 50 return res; 51 52 } 53 };
----------------------------------------------------------------------------------------------------------分割线------------------------------------------------------------------------
7、Reverse Integer
题目
题目要求是反转一个整数,整数包括正数和负数,需要注意的地方是如果一个整数最后几个数字是0,反转后的整数将去掉前面的几个0,比如100--->1;代码如下:
1 class Solution { 2 public: 3 int reverse(int x) { 4 int res=0,temp=x,i,j; 5 int flag=1; 6 if (x<0&&x!=-2147483648) 7 { 8 temp = 0-temp; 9 flag=-1; 10 } 11 if (x==-2147483648)//特例 12 { 13 return 0; 14 } 15 16 while(temp)//1534236469 17 { 18 i=temp%10; 19 if(res>214748364)return 0; 20 res = res * 10 + i; 21 temp/=10; 22 } 23 if (res>(int)pow(2.0,32)-1) 24 { 25 return 0; 26 } 27 res = flag * res; 28 29 return res; 30 31 } 32 };
这道题目的难点是有几个比较特殊的整数需要单独处理。