Solutions to Chapter 1 | Arrays and Strings

来源:互联网 发布:淘宝开店收入 编辑:程序博客网 时间:2024/04/28 14:32

1.1 

Implement an algorithm to determine if a string has all unique characters. What if you can not use additional data structures?

译文:

实现一个算法来判断一个字符串中的字符是否唯一(即没有重复).不能使用额外的数据结构。 (即只使用基本的数据结构)

解答

         面试的时候遇到这种题,第一反应就是要去除模糊性,可以通过提问的方式:

                     1 请问,构成字符串的字符集多大?ASCII还是只有字母和数字组成?

我们假设是ASCII码,那么ASCII码除去128 - 255 扩展的特殊字符,我们假设只有0-127


第一种方法:标记数组法


用  bool a[128]表示字符出现情况,false 为 没有出现过,true表示出现过。初始化为false。遍历字符串的字符,以ascii码为下标进行判断。

时间复杂度 O(n)   时间复杂度已达最低,能优化的就是空间复杂度了,往下看


第二种方法:位操作        


128位 我们用4个int就可以表示(32 bit os),我们用 一个长度为4的int数组即可。
关键是要把字符对应的数字,映射到正确的位上去,      字符的ascii码值 整除 32 得到数组下标   
int val = str.at(i);int index = val/32;//数组下标int offset = val%32//相应位


整体代码如下:vs 2008 已测试通过
#include <iostream>#include <string>using namespace std;bool boolArrays(string str,bool *a){int len = str.length();for (int i = 0; i < len; i++){int val = str.at(i);if (a[val]){return false;}else{a[val] = true;}}return true;}bool bitOperation(string str,int* a)//int 为32位 128个 128/32 = 4{int len = str.length();for (int i = 0; i < len; i++){int val = str.at(i);int index = val/32;int offset = val%32;int j = 1<<(32 - offset);           //关键是这里  别糊涂 我看见有的人的文章这里不对if (a[index]&j){return false;}else{a[index] |= (j);}}return true;}int main(){bool a[128];int  b[4];string str = "";memset(a,0,sizeof(a));memset(b,0,sizeof(b));    while(cin>>str){if (boolArrays(str,a)){cout<<"boolArrays true"<<endl;}else{cout<<"boolArrays false"<<endl;}memset(a,0,sizeof(a));if (bitOperation(str,b)){cout<<"bitOperation true"<<endl;}else{cout<<"bitOperation false"<<endl;}memset(b,0,sizeof(b));}return 0;}


1.2 

Write code to reverse a C-Style String. (C-String means that “abcd” is represented as five characters, including the null character.)

译文:

写代码翻转一个C风格的字符串。(C风格的意思是"abcd"需要用5个字符来表示,包含末尾的 结束字符)

解答

这道题如果就是要考察你有没有注意到C风格字符串最后的那个结束符,那我觉得还是像书 上写的那样,在代码中有所体现。代码如下:

#include <iostream>using namespace std;void Reverse(char *a){char c;for (int i = 0,j = strlen(a) - 1; i < j; i++,j-- ){c    = a[i];a[i] = a[j];a[j] = c;}}int main(){char str[] = "123456789";Reverse(str);cout<<str<<endl;return 0;}



1.3 

Design an algorithm and write code to remove the duplicate characters in a string without using any additional buffer. NOTE: One or two additional variables are fine. An extra copy of the array is not.

FOLLOW UP
Write the test cases for this method.

译文:

设计算法并写出代码移除字符串中重复的字符,不能使用额外的缓存空间。注意: 可以使用额外的一个或两个变量,但不允许额外再开一个数组拷贝。

进一步地,

为你的程序写测试用例。

解答

First, ask yourself, what does the interviewer mean by an additional buffer? Can we use an additional array of constant size?
Algorithm—No (Large) Additional Memory:

1. For each character, check if it is a duplicate of already found characters.
2. Skip duplicate characters and update the non duplicate characters.

Time complexity is O(N2).

首先,自己默默问一下自己,面试官口中所谓的额外的缓存空间是什么?可以使用具有固定大小的数组吗?

下面是一个不使用额外内存(几个变量而已)版本的算法:

1 对每一个字符,检查是否与前面字符重复

2 跳过(覆盖)重复的字符并且更新不重复的字符

算法复杂度是O(n)

#include <iostream>#include <cstring>using namespace std;void removeDuplicate(char* s){if (s == NULL){return ;}if (strlen(s) < 2){return ;}int tail = 1;                 //跟踪不重复的元素,也可以理解为当前不重复字符的个数,这样方便覆盖,不用挪动元素int len = strlen(s);for (int i = 1; i < len; ++i) //直接从第二个字符开始检查{int j;for (j = 0;j < tail; ++j){if (s[j] == s[i]){break;}}if ( j == tail)           //j等于tail 说明没有被break,当前检测字符与之前的字符没有重复{s[tail] = s[i];++tail;}}s[tail] = '\0';return ;}int main(){ char s[]  = "ababababa";        cout<<s<<endl;removeDuplicate(s);   cout<<s<<endl;return 0;}

1. String does not contain any duplicates, e.g.: abcd
2. String contains all duplicates, e.g.: aaaa
3. Null string
4. String with all continuous duplicates, e.g.: aaabbb
5. String with non-contiguous duplicate, e.g.: abababa

测试用例:

  1. 不包含重复字符的字符串,比如:abcd
  2. 字符串全是重复字符,比如:aaaa
  3. 空字符串
  4. 重复字符连续出现,比如:aaabbb
  5. 重复字符不连续出现,比如:abababa

另外一种方法:用到上第一题中的方法,标记数组,假设可以用固定长度的数组

void removeDuplicate1(char* s){if(s == NULL) return;int len = strlen(s);if(len < 2)   return;bool boolArray[128];memset(boolArray,0,sizeof(boolArray));int tail = 0;for (int i = 0; i < len; i++){int val = s[i];if (!boolArray[val]){boolArray[val] = true;s[tail] = s[i];++tail;}}s[tail] = '\0';return;}int main(){char str[]  = "abcdeffdsfagdfbfdew";cout<<str<<endl;removeDuplicate1(str);cout<<str<<endl;return 0;}

1.4 

Write a method to decide if two strings are anagrams or not.

译文:

写一个函数判断两个字符串是否由相同的字符构成(回文)。

解答

有两种方法解决这个问题:

O(nlogn)的解法

先分别对两个字符串进行排序,排序后的字符串若相等,则是回文,否则不是

bool isAnagram1(string s1, string s2){if(s1 == "" || s2 == "") return false;if(s1.length() != s2.length()) return false;sort(&s1[0], &s1[0]+s1.length());sort(&s2[0], &s2[0]+s2.length());if(s1 == s2) return true;else return false;}

O(n)的解法

检查两个字符串中的每一个字符是否有相同的出现次数,一个++,一个--。若是回文,则最终抵消,否则不是

bool anagrams(string &s1,string &s2){if (s1 == ""||s2 == "") return false;if (s1.length() != s2.length()){return false;}int ascii[128];memset(ascii,0,sizeof(ascii));int len = s1.length();    int val1 = 0,val2 = 0,i;for ( i = 0; i < len; i++){val1 = s1.at(i);val2 = s2.at(i);++ascii[val1];--ascii[val2];}for (i = 0; i < 128; i++){if (ascii[i] != 0){return false;}}return true;}


1.5 

Write a method to replace all spaces in a string with ‘%20’.

译文:

写一个函数,把字符串中所有的空格替换为%20 。

解答

简单题。

剑指offer中也提到了此题,在网络编程(服务器)中,URL中含有特殊字符 如 空格 ‘#’ 服务器端无法获取正确的参数。所以要替换为相应ascii码的十六进制

最笨的方法是逐个判断,若为空格,则向后移动。但这样的时间复杂度很高O(n2)



The algorithm is as follows:
1. Count the number of spaces during the first scan of the string.
2. Parse the string again from the end and for each character:
»»If a space is encountered, store “%20”.
»»Else, store the character as it is in the newly shifted location.

另外一种算法如下:
1 先遍历一遍字符串,记录其中空格的个数
2 从后向前判断并塑造取代空格后的字符串

代码:
#include <iostream>using namespace std;void replaceBlank(char* str){if (str == NULL) return;int count = 0,len = strlen(str);for (int i = 0; i < len; i++){if (str[i] == ' '){count++;}}int size = len + 2*count;str[size] = '\0';int start = 0,end = size - 1;for (int j = len - 1; j >= 0; j--){if (str[j] == ' '){str[end]     = '0';str[end - 1] = '2';str[end - 2] = '%';end -= 3;}else{str[end--] = str[j];}}}int main(){   char a[20] = "a b c d";   replaceBlank(a);   cout<<a<<endl;   return 0;}

1.6 

Given an image represented by an NxN matrix, where each pixel in the image is 4 bytes, write a method to rotate the image by 90 degrees. Can you do this in place?

译文:

一张图像表示成NxN的矩阵,图像中每个像素是4个字节,写一个函数把图像旋转90度。 你能原地进行操作吗?(即不开辟额外的存储空间)

解答

先问是顺时针还是逆时针,假设顺时针。
The rotation can be performed in layers, where you perform a cyclic swap on the edges on each layer. In the first for loop, we rotate the first layer (outermost edges). We rotate the edges by doing a four-way swap first on the corners, then on the element clockwise from the edges, then on the element three steps away.

Once the exterior elements are rotated, we then rotate the interior region’s edges.

图片的旋转可以将像素划分成一圈一圈,然后从最外层一圈一圈上来旋转。旋转某一圈的某个元素的时候,相当于对应的上下左右依次交换。

#include <iostream>using namespace std;void rotate90(int a[][4],int n){for (int layer = 0; layer < n / 2; ++layer) { int first = layer; int last = n - 1 - layer; for(int i = first; i < last; ++i) { int offset = i - first; int top = a[first][i]; // save top // left -> top a[first][i] = a[last-offset][first]; // bottom -> left a[last-offset][first] = a[last][last - offset]; // right -> bottom a[last][last - offset] = a[i][last]; // top -> right a[i][last] = top; // right <- saved top } }for(int i=0; i<4; ++i){for(int j=0; j<4; ++j)cout<<a[i][j]<<" ";cout<<endl;}cout<<endl;;}int main(){int a[][4] = {1,2,3,4,          5,6,7,8,  9,10,11,12,  13,14,15,16};rotate90(a,4);return 0;}




1.7 

Write an algorithm such that if an element in an MxN matrix is 0, its entire row and column is set to 0.

译文:

写一个函数处理一个MxN的矩阵,如果矩阵中某个元素为0,那么把它所在的行和列都置为0.

解答

简单题。

At first glance, this problem seems easy: just iterate through the matrix and every time we see a 0, set that row and column to 0. There’s one problem with that solution though: we will “recognize” those 0s later on in our iteration and then set their row and column to zero. Pretty soon, our entire matrix is 0s!
One way around this is to keep a second matrix which flags the 0 locations. We would then do a second pass through the matrix to set the zeros. This would take O(MN) space.
Do we really need O(MN) space? No. Since we’re going to set the entire row and column to zero, do we really need to track which cell in a row is zero? No. We only need to know that row 2, for example, has a zero.
The code below implement this algorithm. We keep track in two arrays all the rows with zeros and all the columns with zeros. We then make a second pass of the matrix and set a cell to zero if its row or column is zero.

乍一看题目,先遍历矩阵,出现0元素,就将所在的行列置零。但是这样的方法执行下来的话整个矩阵都变成了0了。
一个变通的方法,在另外一个MxN的矩阵中记录是否出现零的情况,然后根据记录对原来的矩阵中对相应的行列进行置零。可是这样方法的空间复杂度为O(MxN)。有没有改进的空间呢?
经过观察,其实我们只需要记录哪一列和哪一行需要置零即可。那么我们新的记录方法即为:使用两个数组rows[]和col[]来记录需要置零的行列。更具这样的方法算法代码如下:

#include <iostream>using namespace std;void setZeros(int (*a)[4],int m,int n){  bool* row = new bool[m],*col = new bool[n];  memset(row,0,sizeof(row));  memset(col,0,sizeof(col));  // Store the row and column index with value 0  for (int i = 0; i < m; i++)  {  for (int j = 0; j < n; j++)  {  if (a[i][j] == 0)  {  row[i] = true;  col[j] = true;  }  }  }   // Set arr[i][j] to 0 if either row i or column j has a 0  for (int i = 0; i < m; i++)  {  for (int j = 0; j < n; j++)  {  if (row[i]||col[j])  {  a[i][j] = 0;  }  }  }  delete row;  delete col;}int main(){int a[][4] = {1,2,3,2,              4,5,6,7,              8,9,0,4,              3,5,2,3};for(int i=0; i<4; ++i){for(int j=0; j<4; ++j)cout<<a[i][j]<<" ";cout<<endl;}cout<<endl;    setZeros(a,4,4);for(int i=0; i<4; ++i){for(int j=0; j<4; ++j)cout<<a[i][j]<<" ";cout<<endl;}cout<<endl;return 0;}


1.8 

Assume you have a method isSubstring which checks if one word is a substring of another. Given two strings, s1 and s2, write code to check if s2 is a rotation of s1 using only one call to isSubstring (i.e., “waterbottle” is a rotation of “erbottlewat”).

译文:

假设你有一个isSubstring函数,可以检测一个字符串是否是另一个字符串的子串。 给出字符串s1和s2,只使用一次isSubstring就能判断s2是否是s1的旋转字符串, 请写出代码。旋转字符串:"waterbottle"是"erbottlewat"的旋转字符串。

解答

Just do the following checks
1. Check if length(s1) == length(s2). If not, return false.
2. Else, concatenate s1 with itself and see whether s2 is substring of the result.
input: s1 = apple, s2 = pleap ==> apple is a substring of pleappleap
input: s1 = apple, s2 = ppale ==> apple is not a substring of ppaleppale

很巧妙的算法:

算法描述:
1 如果length(s1)!= length(s2) 返回 false
2 将是s1和本身连接,得到新字符串s1',调用isSubstring(s2,s1')判断s2是否为s1'的字符串。

#include <iostream>#include <string>using namespace std;bool isSubstring(string s1, string s2){if(s1.find(s2) != string::npos) return true;else return false;}bool isRotation(string s1, string s2){if(s1.length() != s2.length() || s1.length()<=0)return false;return isSubstring(s1+s1, s2);}int main(){string s1 = "apple";string s2 = "pleap";cout<<isRotation(s1, s2)<<endl;//cout<<string::npos<<endl;return 0;}



原创粉丝点击