offer7
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
可以采用一个栈实现,单词和空格分开处理,但是太麻烦了。
下面是博主的思路
由于本题需要翻转句子,我们先颠倒句子中的所有字符。这时,不但翻转了句子中单词的顺序,而且单词内字符也被翻转了。我们再颠倒每个单词内的字符。由于单词内的字符被翻转两次,因此顺序仍然和输入时的顺序保持一致。
非常实用。而且不需要额外的空间。
void Reverse(char* pBegin, char* pEnd)
{
if (pBegin==NULL || pEnd==NULL || pBegin>pEnd)
return;
char temp;
//交换pBegin和pEnd之间的字符
while (pBegin<=pEnd)
{
temp=*pBegin;
*pBegin=*pEnd;
*pEnd=temp;
pBegin++;
pEnd--;
}
}
void ReverseSentense(char* pS)
{
int len=strlen(pS);
char *pBegin,*pEnd;
pBegin=pS;
pEnd=pBegin+len-1;
//先反转句子
Reverse(pBegin, pEnd);
pBegin=pEnd=pS;
while (*pBegin!='\0')
{
//空格不反转,直接跳过
if (*pBegin==' ')
{
pBegin++;
pEnd++;
continue;
}
//到单词结束时,反转单词,并更新pBegin到下一个单词
else if (*pEnd==' '||*pEnd=='\0')
{
pEnd--;
Reverse(pBegin, pEnd);
pBegin=++pEnd;
}
//pEnd还在单词内部,继续寻找单词结束
else
pEnd++;
}
}
offer14
n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。当一个数字删除后,从被删除数字的下一个继续删除第m个数字。求出在这个圆圈中剩下的最后一个数字。
经典的约瑟夫问题,链表或者数组实现就不再说了,下面是一种非常牛逼的解法,真是佩服的五体投地。
先定义一个函数 f(n,m)实现从(0,1,…,n-1)中计算约瑟夫剩下的最后一个值,
然后假设(0,1,…,n-1)第一轮删除的值是k,继续定义一个函数 g(n-1,m) 实现从k+1开始的n-1个数组成的环中实现约瑟夫,返回的是剩下的编号。
那么有 f(n,m)=g(n-1,m)。
其实 f 和 g 的区别就是对编号的计数方式不同而已,现在考虑g(n-1,m)和f(n-1,m)的不同。
我们已经知道第一轮删除的值是k,假设g(n-1,m)返回的值是x,f(n-1,m)的返回值是y,虽然x!=y,但是他们表示的都是同一个数,从 k+1 开始的第y个数(需超过n后要回环)就是x,x和y的对应关系如下
0, 1, 2,……,k-1,k,k+1,……, n-1 ---->x---->g(n-1,m)
n-k-1,n-k,n-k+1,……,n-2, , 0 ,……,n-k-2 ---->y---->f(n-1,m)
那么x和y的关系就是 x=(k+1+y)%n,同时我们还值k是第一次删除的值,那么 k=(m-1)%n
那么就有x=(k+1+y)%n=((m-1)%n+1+y)%n=(m+y)%n。
即f(n,m)=g(n-1,m)=( f(n-1,m)+m )%n就是一个递归公式了。
offer16
O(logn)求Fibonacci数列
还没有搞懂
offer21
定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。
要求空间复杂度为O(1),一般是要求在原字符串上操作,
博客里的算法很赞,
我们还是把字符串看成有两段组成的,记位XY。左旋转相当于要把字符串XY变成YX。我们先在字符串上定义一种翻转的操作,就是翻转字符串中字符的先后顺序。把X翻转后记为XT。显然有(XT)T=X。
我们首先对X和Y两段分别进行翻转操作,这样就能得到XTYT。接着再对XTYT进行翻转操作,得到(XTYT)T=(YT)T(XT)T=YX。正好是我们期待的结
void TurnString(char *s,int len)
{
if (s!=NULL)
{
int i=0;
char t;
len--;
while (i<len)
{
t=*(s+i);
*(s+i)=*(s+len);
*(s+len)=t;
i++;
len--;
}
}
}
void LeftRotateString(char *s,int m)
{
if (s!=NULL)
{
int len=strlen(s);
if (m<=len)
{
TurnString(s,m);
TurnString(s+m,len-m);
TurnString(s,len);
}
}
}
offer25
输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
编程之美上也有类似的,还没弄明白
offer28
输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
可以考虑用递归实现,首先选出一个值,和首字母交换,然后输出剩下值的全排列,直到每一个字母都和首字母交换。
这里还需要一个检查函数,用来排除重复字符的情况,加入从首字符开始到当前要交换的字符,已经有和当前要交换字符相同的,那么这个字符就不需要交换了,因为已经交换过了。
bool check(char *pBegin,char *pEnd)
{
char *p=pBegin;
while (p!=pEnd)
{
if (*p==*pEnd)
return false;
p++;
}
return true;
}
void pailie(char *a,char *pBegin)
{
//前面已经全排列玩完毕,输出字符串
if (*pBegin=='\0')
{
printf("%s \n",a);
}
else
{
for (char *p=pBegin;*p!='\0';p++)
{ //从pBegin指向的字符开始,之后的每一个字符都和pBegin字符交换位置
if (check(pBegin,p))
{
swap(pBegin,p);
//从pBegin+1开始全排列
pailie(a,pBegin+1);
swap(p,pBegin);
}
}
}
}
offer59
输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。
上一题求排列,这是求组合。假设字符串长度为len,那么我们需要写出 1、2、……、len 不同长度的组合。
在写出长度为M的组合时,我们可以选择首字母然后从剩下的里面选出 m-1 个字符,也可以不选首字符,然后从剩下的字符里面选出M个字符。可以用递归实现。
//该函数实现pStart开始的字符串选择n个字符
//temp是一个全局变量,用来存储已经选择的字符
void Combination(char *pStart,int n)
{
if (pStart==NULL||n>strlen(pStart))
return;
//n为0表示前面已经选择完成,
if (n==0)
{
temp[tempSign]='\0';
puts(temp);
// printf("\n");
return;
}
//选择首字符,从剩下的选择n-1个
temp[tempSign++]=*pStart;
Combination(pStart+1,n-1);
//不选首字符,从剩下的选择n个
tempSign--;
Combination(pStart+1,n);
}
void PrintCom(char *pStart,int n)
{
int i;
for (i=1;i<=n;i++)
{
Combination(pStart,i);
}
}
offer37
我们把只包含因子2、3和 5 的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第1500个丑数。
我们可以创建一个数组,里面存放着已经找到的丑数。加入现在已经找到第 m 个丑数,那么在寻找 m+1 个丑数时,(m+1)肯定是从 m 之前的丑数中某个数 x2或者x3或者x5得出来的。
假设我们已经知道丑数 T2是最小的 x2收比(m)大的数,T3是最小的 x3收比(m)大的数,T5是最小的 x5收比(m)大的数。那么(m+1)一定是 T2x2、 T3x3、 T5x5 中的最小值。
找出(m+1),还需要更新T2、T3、T5、的值,以便下一次找到(m+2),T2左边的值x2以后小于(m)那么也肯定小于(m+1),我们只需要从 T2 开始,向右找到第一个 x2 后比(m+1)大的值,作为新的 T2,T3和T5同理。
现在我们已知(m+1)和T2、T3、T5寻找(m+2)的情况和已知(m)和T2、T3、T5寻找(m+1)的情况一样了。
循环很容易,初始状态是 (m)=1,T2、T3、T5=1。
int min3(int num1, int num2, int num3)
{
int min;
min=num1<num2? num1: num2;
min=min<num3? min: num3;
return min;
}
int FindUgly(int Index)
{
int *Ugly=(int*)malloc(sizeof(int)*Index);
Ugly[0]=1;
int nextUglyIndex=1;
int* pUgly2=Ugly;
int* pUgly3=Ugly;
int* pUgly5=Ugly;
while (nextUglyIndex<Index)
{
Ugly[nextUglyIndex]=min3(*pUgly2*2, *pUgly3*3, *pUgly5*5);
while (*pUgly2*2 <= Ugly[nextUglyIndex])
pUgly2++;
while (*pUgly3*3 <= Ugly[nextUglyIndex])
pUgly3++;
while (*pUgly5*5 <= Ugly[nextUglyIndex])
pUgly5++;
nextUglyIndex++;
}
int UglyNum=Ugly[Index-1];
free(Ugly);
return UglyNum;
}
offer43
把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率
offer51
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
例如:如果输入如下矩阵:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
则依次打印出数字1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10。
#define WIDTH 4
#define HEIGHT 5
int a[]=
{ 1, 2, 3, 4,
6, 7, 8, 9,
11,12,13,14,
16,17,18,19,
21,22,23,24};
//这个函数打印从int *p 开始的宽width高height矩阵的最外围
void PrintCircle(int *p,int width,int height)
{
int i,j;
for (i=0;i<width;i++)
{
printf("%d, ",*(p+i));
}
for (j=1;j<height;j++)
{
printf("%d, ",*(p+width-1+j*WIDTH));
}
for (i=1;i<width;i++)
{
printf("%d, ",*((p+width-1+(height-1)*WIDTH)-i));
}
for (j=1;j<height-1;j++)
{
printf("%d, ",*((p+(height-1)*WIDTH)-j*WIDTH));
}
}
void PrintAll(int *p,int width,int height)
{
while (width&&height)
{
//先打印矩阵最外围,然后宽度和高度都减去2,编程小矩阵,继续打印,直到宽度或者高度有一个为0
PrintCircle(p,width,height);
width-=2;
height-=2;
p+=WIDTH+1;
}
}
offer46
输入一个字符串,输出该字符串中对称的子字符串的最大长度。比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。
曼彻斯特算法的时间复杂度是O(n),搞懂了贴上来。
offer58
在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
还没搞懂