劍指offer--算法(7、14、16、21、25、28、59、37、43、51、46、58)

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

我們首先對XY兩段分別進行翻轉操作,這樣就能得到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,求從1nn個整數的十進制表示中1出現的次數。

編程之美上也有類似的,還沒弄明白



offer28

輸入一個字符串,打印出該字符串中字符的所有排列。例如輸入字符串abc,則輸出由字符abc所能排列出來的所有字符串abcacbbacbcacabcba

可以考慮用遞歸實現,首先選出一個值,和首字母交換,然後輸出剩下值的全排列,直到每一個字母都和首字母交換。

這裏還需要一個檢查函數,用來排除重複字符的情況,加入從首字符開始到當前要交換的字符,已經有和當前要交換字符相同的,那麼這個字符就不需要交換了,因爲已經交換過了。

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,它的組合有abcabacbcabc

上一題求排列,這是求組合。假設字符串長度爲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)。例如68都是醜數,但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),還需要更新T2T3、T5、的值,以便下一次找到(m+2),T2左邊的值x2以後小於(m)那麼也肯定小於(m+1),我們只需要從 T2 開始,向右找到第一個 x2 後比(m+1)大的值,作爲新的 T2,T3和T5同理。

現在我們已知(m+1)和T2T3、T5尋找(m+2)的情況和已知(m)和T2T3、T5尋找(m+1)的情況一樣了。

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


還沒搞懂



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章