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的國際象棋上擺放八個皇后,使其不能相互攻擊,即任意兩個皇后不得處在同一行、同一列或者同一對角斜線上。下圖中的每個黑色格子表示一個皇后,這就是一種符合條件的擺放方法。請求出總共有多少種擺法。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
還沒搞懂