diff --git a/算法问题选编/字符串.md b/算法问题选编/字符串.md index b6c01c6..d83937c 100644 --- a/算法问题选编/字符串.md +++ b/算法问题选编/字符串.md @@ -2,8 +2,8 @@ 字符串常见的问题: -* 单词反转 -* 回文判断 +* 单词反转/移动/回文判断 +* 字符(串)统计 * 字符串的压缩 * 字符串的排列和组合 * 字符串比较 @@ -12,19 +12,18 @@ #### 翻转句子中单词的顺序 -题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。 -为简单起见,标点符号和普通字母一样处理。 +题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。 例如输入“I am a student.”,则输出“student. a am I”。 ``` -char *revert_by_word(const char *source,char *dest); +char *revert_by_word(char *source); ``` -思路一:反向拷贝 -思路二:原地逆序,字符串2边的字符逐个交换 -思路三: 针对不允许临时空间的情况,也就是字符交换不用临时空间,可以使用的方法有: +原地逆序,字符串2边的字符逐个交换 , 再按单词逆序; 也可以先按单词逆序,再对整个句子逆序; -1. 异或操作 +针对不允许临时空间的情况,也就是字符交换不用临时空间,可以使用的方法有: + +1. 异或操作 2. 也就是2个整数相互交换一个道理 ``` @@ -37,17 +36,63 @@ a = a - b; 最终示例代码: ``` -char *revert_by_word(const char *source,char *dest){ - +void _reverse(char *start,char *end){ + if ((start == NULL) || (end == NULL)) return; + while(start < end){ + char tmp = *start; + *start = *end; + *end = tmp; + + start++, + end--; + } +} + +char *revert_by_word(char *source){ + char *end = source; + char *start = source; + if (source == NULL) return NULL; + while (*end != '\0') end++; + end--; + + _reverse(start,end); + + start=end=source; + while(*start != '\0'){ + if (*start == ' '){ + start++; + end++; + }else if(*end == ' ' || *end == '\0'){ + _reverse(start,end-1); + start = end; + }else{ + end++; + } + } + return source; } ``` 类似的题目还有: -不开辟用于交换数据的临时空间,如何完成字符串的逆序(在技术一轮面试中,有些面试官会这样问) +不开辟用于交换数据的临时空间,如何完成字符串的逆序 用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。 +#### 左旋转字符串 + +>字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。 + +如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。 + +``` +char *left_rotate(char *str,int offset){ + +} +``` +我们可以abcdef分成两部分,ab和cdef,内部逆序以后,整体再次逆序,就可以得到想要的结果了,也就是跟上面的问题是同样的问题。 + + #### 判断字符串是否是回文 分析: 2个指针,一头一尾,逐个比较,都相同就是回文 @@ -93,7 +138,6 @@ char char_first_appear_once(const char *source) hash表长度 256,字符直接作为key值。需要注意的是 char 的范围是 -128~127,unsigned char 才是0~255 - 示例代码: ``` @@ -187,29 +231,57 @@ void proc(char *str) 比如:HaJKPnobAACPc,要小写字母前面且不要求保存顺序,可以是:anobcHJKPAACP -小写字母 a~z 的 ASCII码值 是 ,A~Z 的 ASCII码值 是 ; +1. 小写字母 a~z 的 ASCII码值是 97~122,A~Z的 ASCII码值是 65~90;0~9 的ASCII码是 48~57 +2. 两边向中间扫描,左边大写右边小写就交换;如果都小写,头指针向前知道找到大写;如果都是大写,尾指针向后找小写; 示例代码 ``` -void proc(char *str){ - -} -``` +char *proc(char *str){ + char *start = str; + char *end = str; + if (str == NULL) return NULL; + while(*end != '\0') end++; + end--; + while(start < end){ + if (*start >= 'A' && *start <= 'Z'){//大写 + if (*end >= 'a' && *end <= 'z'){ + char tmp = *start; + *start = *end; + *end = tmp; + + start++; + } + end--; + }else{//小写 + if (*end >= 'A' && *end <= 'Z'){ + end--; + } + start++; + } + } + return str; +} +``` #### 实现字符串转整型的函数 -也就是实现函数atoi的功能,这道题目考查的就是对各种情况下异常处理。比如 +也就是实现函数atoi的功能,这道题目考查的就是对各种情况下异常处理。比如: -正/负数,"-432" -浮点数,"43.2344" -溢出,"32111111112222222222222222222222222222222" -非法,"2123des" 0 -NULL/空串,这时候返回值0 +以"213"分析转换成证书的过程。3+1x10+2x100,思路是:每扫描到一个字符,把之前得到的数字*10,再加上当前的数字 + +0开头,"0213" +正/负数,"-432" ,"--422","++23" +浮点数,"43.2344" +非法,"2123des" +存在空格," -32"," +432"," 234","23 432","353 "," + 321" +NULL/空串,这时候返回值0 +溢出,"32111111112222222222222222222222222222222" , 与 `INT_MAX `比较 + +如何区分正常的'0'和异常情况下返回的结果"0"? 可以通过一个全局变量 g_status 来标示,值为 kValid/kInvalid。 -以"213"分析转换成证书的过程。3+1*10+2*100,思路是:每扫描到一个字符,把之前得到的数字*10,再加上当前的数字 ``` int atoi(const char *str){ @@ -217,6 +289,7 @@ int atoi(const char *str){ } ``` +详细过程也可以[参考这里](http://blog.csdn.net/v_july_v/article/details/9024123) #### 匹配兄弟字符串 @@ -243,41 +316,76 @@ int isBrother(const char *first,const char *secd){ ``` -#### 字符串的集合合并 +#### 删除串中指定的字符 -给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。 - +删除指定的字符以后,后面的字符都要向前移动一位。这种复杂度是O(N^2);那么有没有O(N)的方法呢? + +比如 "abcdeccba" 删除字符 "c"。使用2个指针,一前一后,比较前面的指针和删除字符: + +1. 不相等,两个指针一起跑,且前面的指针值拷贝到后面指针指向的空间 +2. 相等时,快指针向前一步 + +``` +char *delete_occurence_character(char *src , char target){ + char *front = src; + char *rear = src; + while(*front != '\0'){ + if (*front != target){ + *rear = *front; + rear++; + } + front++; + } + *rear = '\0'; + return src; +} +``` #### 在字符串中删除特定的字符 题目:输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。 + +这是上个题目的升级版本。 + ``` -char *delete_occurence(char *source,const char *del); +char *delete_occurence_characterset(char *source,const char *del); ``` 1. 蛮力法。 遍历字符串,每个字符去删除字符串集合中查找,有就删除 - - +2. 使用上面的方式,一次遍历 #### 字符串原地压缩 -题目描述:“eeeeeaaaff" 压缩为 "e5a3f2",请编程实现。 -分析,主要问题在于字符串的移动 +题目描述:“abeeeeegaaaffa" 压缩为 "abe5ag3f2a",请编程实现。 + +这是使用2个指针,一前一后,如果不相等,都往前移动一位;如果相等,后一位变为数字2,且移动后面的指针一位,任然相等则数字加1,不相等 + ``` -char *compress(const char *src,char *dest) +char *compress(const char *src,char *dest){ + +} ``` +上面的压缩算法可以看到,压缩算法的效率验证依赖给定字符串的特性,如果'aaaaaaaa....aaa' 这样特征的字符串,使用上面的压缩算法,压缩率接近100%,相反,可能会的0%的压缩率。 + + + +#### 删除字符串中的数字并压缩字符串 + +如字符串”abc123de4fg56”处理后变为”abcdefg”。注意空间和效率。 +(下面的算法只需要一次遍历,不需要开辟新空间,时间复杂度为O(N)) + + + #### 字符串匹配实现 请以俩种方法,回溯与不回溯算法实现。 -实现一个高级的字符匹配算法 - #### 对称子字符串的最大长度 @@ -312,57 +420,6 @@ int continumax(char *outputstr,char *intputstr) ``` -#### 左旋转字符串 - ->字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。 - -如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。 -要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。 - - - - -#### n个字符串联接 - -有n个长为m+1的字符串,如果某个字符串的最后m个字符与某个字符串的前m个字符匹配,则两个字符串可以联接,问这n个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。 - - - - -#### 设计一个系统处理词语搭配问题 - -设计一个系统处理词语搭配问题,比如说 中国 和人民可以搭配, -则中国人民 人民中国都有效。要求: - - *系统每秒的查询数量可能上千次; - *词语的数量级为10W; - *每个词至多可以与1W个词搭配 - -当用户输入中国人民的时候,要求返回与这个搭配词组相关的信息。 - - - - -#### 删除字符串中的数字并压缩字符串 - -如字符串”abc123de4fg56”处理后变为”abcdefg”。注意空间和效率。 -(下面的算法只需要一次遍历,不需要开辟新空间,时间复杂度为O(N)) - - - -#### 删除串中指定的字符 - -``` -char *delete_character(char *src , char target) -``` - - - -#### 给出一个函数来复制两个字符串A和B - -字符串A的后几个字节和字符串B的前几个字节重叠。这种题目往往就是考你对边界的考虑情况。 - - #### 子串匹配的个数 @@ -414,11 +471,33 @@ int longest_common_subsequence(const char *s1,const char *s2, char *common) +#### n个字符串联接 + +有n个长为m+1的字符串,如果某个字符串的最后m个字符与某个字符串的前m个字符匹配,则两个字符串可以联接,问这n个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。 + + + + +#### 字符串的集合合并 + +给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。 + + + + + +#### 给出一个函数来复制两个字符串A和B + +字符串A的后几个字节和字符串B的前几个字节重叠。这种题目往往就是考你对边界的考虑情况。 + + + + #### 请编写能直接实现strstr()函数功能的代码。 ``` -//返回第一次出现子串的地址,否则返回0 string.h +//返回第一次出现子串的地址,否则返回0 char *strstr(const char *haystack, const char * needle); ``` @@ -430,20 +509,6 @@ char *strstr(const char *haystack, const char * needle); char *strcpy(char *strDest, const char *strSrc); ``` 其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数 - - - -#### ? - -给一串很长字符串,要求找到符合要求的字符串,例如目的串:123 ,1******3***2 ,12*****3这些都要找出来 -其实就是类似一些和谐系统。。。。。 - - -#### ? - -函数将字符串中的字符'*'移到串的前部分,前面的非'*'字符后移,但不能改变非'*'字符的先后顺序,函数返回串中字符'*'的数量。 -如原始串为:ab**cd**e*12,处理后为*****abcde12,函数并返回值为5。(要求使用尽量少的时间和辅助空间) - diff --git a/算法问题选编/测试代码/delete_occurence_character.c b/算法问题选编/测试代码/delete_occurence_character.c new file mode 100644 index 0000000..fd5668a --- /dev/null +++ b/算法问题选编/测试代码/delete_occurence_character.c @@ -0,0 +1,22 @@ +#include "stdio.h" + +char *delete_occurence_character(char *src , char target){ + char *front = src; + char *rear = src; + while(*front != '\0'){ + if (*front != target){ + *rear = *front; + rear++; + } + front++; + } + *rear = '\0'; + return src; +} + +int main(int argc, char const *argv[]) +{ + char test[] = "abcdeccba"; + printf("%s\n", delete_occurence_character(test,'c')); + return 0; +} diff --git a/算法问题选编/测试代码/proc.c b/算法问题选编/测试代码/proc.c new file mode 100644 index 0000000..73e63c6 --- /dev/null +++ b/算法问题选编/测试代码/proc.c @@ -0,0 +1,35 @@ +#include "stdio.h" + +char *proc(char *str){ + char *start = str; + char *end = str; + if (str == NULL) return NULL; + while(*end != '\0') end++; + end--; + + while(start < end){ + if (*start >= 'A' && *start <= 'Z'){//大写 + if (*end >= 'a' && *end <= 'z'){ + char tmp = *start; + *start = *end; + *end = tmp; + + start++; + } + end--; + }else{//小写 + if (*end >= 'A' && *end <= 'Z'){ + end--; + } + start++; + } + } + return str; +} + +int main(int argc, char const *argv[]) +{ + char test[] = "HaJKPnobAACPc";//"char *test" always make "28767 bus error" + printf("%s\n", proc(test)); + return 0; +} diff --git a/算法问题选编/测试代码/revert_by_word.c b/算法问题选编/测试代码/revert_by_word.c new file mode 100644 index 0000000..da95e3a --- /dev/null +++ b/算法问题选编/测试代码/revert_by_word.c @@ -0,0 +1,57 @@ +#include "stdio.h" + +void _reverse(char *start,char *end){ + if ((start == NULL) || (end == NULL)) return; + while(start < end){ + char tmp = *start; + *start = *end; + *end = tmp; + + start++, + end--; + } +} + +char *_revert_by_word(char *source){ + char *end = source; + char *start = source; + while(*start != '\0'){ + if (*start == ' '){ + start++; + end++; + }else if(*end == ' ' || *end == '\0'){ + _reverse(start,end-1); + start = end; + }else{ + end++; + } + } + return source; +} + +char *revert_by_word(char *source){ + char *end = source; + char *start = source; + if (source == NULL) return NULL; + while (*end != '\0') end++; + end--; + + /*整个句子逆序*/ + _reverse(start,end); + + /*按单词逆序.tneduts a ma I */ + _revert_by_word(source); + + return source; +} + +//为啥运行时 bus error? +int main(int argc, char const *argv[]) +{ + char *test = "how are you ?"; + printf("%s\n",_revert_by_word(test)); + + //char *test = "I am a student."; + //printf("%s\n",revert_by_word(test)); + return 0; +} diff --git a/算法问题选编/系统设计.md b/算法问题选编/系统设计.md index c97e970..84e8475 100644 --- a/算法问题选编/系统设计.md +++ b/算法问题选编/系统设计.md @@ -3,6 +3,22 @@ #### 要求设计一个DNS的Cache结构,要求能够满足每秒5000以上的查询,满足IP数据的快速插入,查询的速度要快。 + + +#### 设计一个系统处理词语搭配问题 + +设计一个系统处理词语搭配问题,比如说 中国 和人民可以搭配, +则中国人民 人民中国都有效。要求: + +*系统每秒的查询数量可能上千次; +*词语的数量级为10W; +*每个词至多可以与1W个词搭配 + +当用户输入中国人民的时候,要求返回与这个搭配词组相关的信息。 + + + + #### 任务调度 系统有很多任务,任务之间有依赖,比如B依赖于A,则A执行完后B才能执行