面试算法基础及编程 第四弹 (字符串、数值类、或其他常见相关)
生活随笔
收集整理的這篇文章主要介紹了
面试算法基础及编程 第四弹 (字符串、数值类、或其他常见相关)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
// # -*- coding:utf-8 -*-
// # @Author: Mr.chen(ai-chen2050@qq.com)
// # @Date: 2018-08-18 21:06:30 // 注:此為第四彈,主要講解字符串、數值類、或其他常見相關面試筆試題,要求手寫。/*
1、替換字符串中的空格。題目:請實現一個函數,將一個字符串中的空格替換成“%20”。例如,當字符串為We Are Happy.則經過替換之后的字符串為We%20Are%20Happy 思路:法一:從頭到尾遍歷字符串做替換,時間復雜度為O(n2),效率低法二:從尾到頭遍歷字符串做替換,時間復雜度為O(n),效率高,下面用它實現
*/// 指向字符數組的字符指針str,字符數組長度length
void replaceSpace(char *str,int length) {// 邊界檢查1:判斷字符數組是否為空if(str==NULL)return ;// 遍歷字符串,統計空格個數、替換前字符個數、替換后字符個數int CountOfBlanks=0; // 空格個數int Originallength=0;// 替換前字符個數int len=0; // 替換后字符個數for(int i=0;str[i]!='\0';++i){Originallength++;if(str[i]==' ')++CountOfBlanks;}len =Originallength+2*CountOfBlanks;// 邊界檢查2:判斷字符數組是否越界if(len+1>length)return ;// 替換空格char*pStr1=str+Originallength;// 字符指針指向原始字符串的末尾char*pStr2=str+len; // 字符指針指向替換后字符串的末尾while(pStr1 != pStr2) // 替換結束的條件{if(*pStr1==' '){*pStr2-- ='0';*pStr2-- ='2';*pStr2-- ='%';}else{*pStr2--=*pStr1;}--pStr1;}
}/*
2、位運算:二進制中 1 的個數。輸入一個整數,輸出該二進制中有多少個 1。經過分析(略),我們發現把一個整數減去1,并且與原數進行與運算會把該整數最右邊的一個 1 變成 0,那么一個整數的二進制表示中會有多個 1,就可以進行多少次這樣的操作。基于這種思想,我們可以寫出以下代碼。
*/
int numberOf1(int n)
{int count = 0;while(n){++ count;n = (n -1) & n;}return count;
} // 此題最為重要就是發現規律,在 Coding/*
3、數值的整數次方。實現函數 double Power(double base,int exponent)求 base 的 exponent 次方,不使用庫函數,不考慮大樹問題。思路:注意邊界情況,如果指數是 0或者負數,如果是負數,可以取絕對值,然后求倒數,求倒數時注意,分母不能為 0,此外,我們可以采用如下這個方法(如下圖)求取指數,已經求過的沒有必要再次計算。并且,該公式很容易使用遞歸來實現。
*/
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
double powerWithUnsignedExponent(double base,unsigned int exponent) {if(exponent == 0)return 1;if(exponent ==1)return base;double result = powerWithUnsignedExponent(base,exponent >> 1);result *= result;//判斷是否是奇數if((exponent & 0x1) == 1)result *= base;return result; }// 在上面用 >> 右移運算符代替了除 2,用位與運算符代替了求余 %,判斷是否是奇數/* 4、調整數組順序使奇數在前面,偶數在后面。暴力遍歷循環,時間效率O(N2),一般方法使用兩個指針分別指向頭和尾,如果有偶數在奇數的前面則交換他們。如果第一個指向偶數,第二個指向奇數,則交換他們。如果,面試官還要我們注意擴展性(通用屬性),提高代碼的可重復性,則可以有如下代碼: */ void ReOrder(int *pData,unsigned int length,bool (*func)(int)) {if(pData == NULL || length == 0)return;int* pBegin = pData;int* pEnd = pData + length -1;while(pBegin < pEnd){while(pBegin < pEnd && !func(*pBegin))++ pBegin;while(pBegin < pEnd && func(*pEnd))-- pEnd;if(pBegin < pEnd){int temp = *pBegin;*pBegin = *pEnd;*pEnd = temp;}} }// 解耦和 bool isEven(int n) {return (n & 1) == 0; }// 調用時 void ReOrderOddEven(int* pData,unsigned int length) {ReOrder(pData,length, isEven); }/* 5、字符串的排列: 輸入一個字符串,打印該字符串中的字符的所有的排列,如例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。思路:對于該問題,一般不容易一下想出解決方案。于是,我們應該嘗試把復雜的問題分解成小的問題,如:我們把一個字符串看成是兩部分組成:第一部分是它的第一個字符,第二個部分是后面所有的字符,如下圖,我們使用兩種不同的顏色背景來來區分字符串的兩部分。我們求整個字符串的排列,也可以看成兩部分。首先求所有可能出現在第一個位置的字符,即把第一個字符和后面的所有字符交換,然后固定第一個字符,然后求后面所有字符的排列。這個時候,仍把后面的字符分成兩個部分,后面字符的第一個字符,和這個字符之后所有的字符,讓這個字符和它后面的字符依次交換。如下所示:我們可以看出這是很典型的遞歸的思想。本題很明顯需要使用回溯算法解決的問題,解決一個回溯問題,實際上就是一個決策樹的遍歷過程。(探索與回溯法)是一種選優搜索法,又稱為試探法,按選優條件向前搜索,以達到目標。 但當探索到某一步時,發現原先選擇并不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為"回溯點"。具體代碼如下所示: */? ? ? ? ? ? ? ? ??
void permutation(char* pStr) {if(NULL == pStr)return;permutation(pStr,pStr); }// pStr 指向整個字符串的第一個字符, pBegin 指向當前我們做排列操作的第一個字符。 // 在每一次遞歸的時候,我們從 pBegin 向后掃描一個字符,在交換完 pBegin 和 pChar // 之后,我們對 pBegin 后面的字符串做遞歸的操作。 void permutation(char* pStr,char* pBegin) {if(*pBegin == '\0')printf("%s\n",pStr);else {for(char* pChar = pBegin; *pChar != '\0'; ++pChar){// 交換char pTemp = *pChar;*pChar = *pBegin;*pBegin = pTemp;// 遞歸permutation(pStr,pBegin+1);// 回退pTemp = *pChar;*pChar = *pBegin;*pBegin = pTemp;}} }/* 6、數組中出現次數超過一半的數字。題目:數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如,輸入一個長度為 9 的數組 { 1,2,3,2,2,2,5,4,2 },由于數字 2在數組中出現 5 次,超過數組的一半,因此輸出 2。法一:先排序,再取中間數字,時間復雜度 O(nlogn),不是最好的。法二:可借鑒快排的 partition 的思想,時間復雜度O(n)。法三:下面代碼使用的方法,充分利用數組的特點,進行降維,取最重要的特征,次數最大。思路如下:由于該數字出現的次數超過數組的一半,也就是它出現的次數比起其他所有的數字出現次數還要多。因此,我們可以考慮在遍歷數組的時候,保存兩個值,一個是數組中的一個值,一個是該數字出現的次數。當我們遍歷下一個數字的時候,如果下一個數字與之前保存的數字相同,則次數加 1,如果不同,則次數減去 1。如果次數為 0,我們需要保存下一個數字,并且把次數置為 1。由于要找的數字比起其他數字出現次數總和還多。故它一定是最后一次把次數置為 1,的數字。此外,還應該檢查一些異常輸入,比如,輸入數組是否為NULL (checkInvalidArray),以及輸入數組中,最高頻率的數字出現次數,是否超過一半以上(checkMoreThanHalf)等,代碼如下: */ int moreThanHalfNumber(int* numbers,int length) {if(checkInvalidArray(numbers,length))return 0;int result = numbers[0];int times = 1;for(int i=1; i< length;++i){if(times == 0){result = numbers[i];times = 1;}if(numbers[i] == result)++ times;else -- times;}if (!checkMoreThanHalf(numbers,length,result))return 0;return result; }// 檢查異常輸入 bool g_inputInvalid = false;bool checkInvalidArray(int* numbers,int length) {g_inputInvalid = false;if(numbers == NULL || length <= 0)g_inputInvalid = true;return g_inputInvalid; }bool checkMoreThanHalf(int* numbers,int length,int number) {int times = 0;for(int i=0; i < length; i++){if(numbers[i] == number)++ times;}bool isMoreThanHalf = true;if( times * 2 <= length){g_inputInvalid = true;isMoreThanHalf = false;} return isMoreThanHalf; }/* 7、連續字數組的最大和,題目:輸入一個整形數組,數組里面既有正數也有負數。數組中的一個或連續多個整數組成一個字數組,求所有字數組的和的最大值,要求時間復雜度為O(n)。例如輸入:{1,-2,3,10,-4,7,2,-5}, 的和最大字數組為{3,10,-4,7,2,-5},該字數組的和為 18。如果列舉它所有的字數組,共有 n(n+1) /2 個字數組。計算出所有的字數組的和,也需要O(n2)的時間。法一:我們可以嘗試著分析數組的規律,當開始累加到出現負數,則說明該拋棄前面所累加的和了,但是我們還需要存儲上一次最大的和,如果下一次添加,比累加的大,則需要更新最大和,下圖是分析過程。此外,我們還需要考慮無效輸入,比如輸入的數組參數為空指針,數組長度小于等于 0 等情況。此時,我們讓函數返回什么數字,如果返回0,怎么區分是最大和為0,還是無效輸入,為此我們定義了一個全局變量來標記是否無效。代碼如下: */? ??
bool g_InvalidInput = false;int FindGreatestSumOfSubArray(int* pData,int length) {if(NULL == pData || length <= 0){ // 區分輸出是無效輸入還是最大總和為 0 g_InvalidInput = true;return 0;}g_InvalidInput = false;int nCurSum = 0;int nGreatestSum = 0x80000000;for(int i=0; i<length; i++){if(nCurSum <= 0)nCurSum = pData[i];else nCurSum += pData[i];// 更新最大值if(nCurSum > nGreatestSum)nGreatestSum = nCurSum;}return nGreatestSum; }/* 8、把數組排成最小的數:輸入一個正整數數組,把數組中所有的數字拼成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組 [3,32,321] 能打印出這 3 個數字能拼成的最小的數字為 321323。思路:法一:全排列求出數組中所有數字的全排列,然后把每個全排列拼起來,求出拼出來的數字的最小值。有 n!個組合,接下來看一種時間復雜度為O(nlogn)的算法。法二: 定義新的排序規則,* 如果兩個數字m,n拼接成mn和nm,如果 mn<nm,那么m應該排在n的前面,我們定義此時m小于n,如果mn=nm,我們定義m等于n。* 可以考慮將數字轉成字符串,一來防止數字拼接時的溢出,二來字符串的拼接和比較容易實現。* 由于把數字 m和n 拼接起來得到mn和nm,它們的位數一定是相同的,因此比較它們的大小只需按照字符串大小的比較規則即可。* 代碼如下: */ const int g_maxNumberLength = 10;char* g_strCombine1 = new char[g_maxNumberLength *2 +1]; char* g_strCombine2 = new char[g_maxNumberLength *2 +1];void printMinNumber(int* numbers,int length) {if(NULL == numbers || length <= 0)return;// 先將數組里面的數字變成字符串char** strNumbers = (char**)(new int[length]);for(int i=0; i<length; ++i){strNumbers[i] = new char[g_maxNumberLength +1];sprintf(strNumbers[i],"%d",numbers[i]);}qsort(strNumbers,length,sizeof(char*),compare);// 打印出來for(int i=0; i<length; ++i) printf("%s",strNumbers[i]);printf("\n");// 釋放內存for(int i=0; i<length; ++i)delete strNumbers[i];delete strNumbers; }// 重載 qsort 的 compare 方法 int compare(const void* strNumber1,const void* strNumber2) {strcpy(g_strCombine1,*(const char**)strNumber1);strcat(g_strCombine1,*(const char**)strNumber2);strcpy(g_strCombine2,*(const char**)strNumber2);strcat(g_strCombine2,*(const char**)strNumber1);return strcmp(g_strCombine1,g_strCombine2); } /* 9、數字在排序數組中出現的次數。題目:統計一個數字在排序數組中出現的次數。例如輸入排序數組,{1,2,3,3,3,3,4,5} 和數字 3,由于 3 在這個數組中出現了 4 次,因此輸出 4。方法一:暴力遍歷,時間復雜度為 O(n),法二:由于已經排好序了,故相同的數必定在一起,分別找到該數第一次出現的地方和最后一次出現的地方。分別使用二分查找算法,則總的時間復雜度,任然是 O(logn),我們先來分析如何找到第一個 K,二分查找總是先拿數組的中間數字和 k 做比較,如果中間數字比 K 大,則 k 只能出現在前半段,只需要去前半段查找即可,反之,去后半段查找。如果中間數字與 k 相等呢。我們先來判斷這個數字是不是第一個 k,如果位于中間位置前一個不是K,則中間位置就是第一個k,如果中間位置的前一個位置也是k,則說明第一個k,在數組的前半段,下一輪我們仍然需要在數組的前半段查找,同理,查找最后一個k,最后就可以確定 k 的個數了。代碼如下: */ // 找數組中的第一個 K int GetFirstK(int* data,int length,int k,int start,int end) {if(start > end)return -1;int MiddleIndex = (start + end) / 2;int MiddleData = data[MiddleIndex];if(MiddleData == k){if((MiddleIndex > 0 && data[MiddleIndex - 1] !=k) || MiddleIndex == 0)return MinddleIndex;else end = MiddleIndex - 1;}else if (MiddleData > k)end = MiddleIndex - 1;elsestart = MinddleIndex + 1;return GetFirstK(data,length,k,start,end); }// 找數組中的最后一個 K int GetLastK(int* data,int length,int k,int start,int end) {if(start > end)return -1;int MiddleIndex = (start + end) / 2;int MiddleData = data[MiddleIndex];if(MiddleData == k){if((MiddleIndex < length -1 && data[MiddleIndex + 1] !=k) || MiddleIndex == length -1)return MinddleIndex;else start = MiddleIndex + 1;}else if (MiddleData < k)start = MinddleIndex + 1;else end = MiddleIndex - 1;return GetLastK(data,length,k,start,end); }// 得出 k 出現的次數 int GetNumberOfK(int* data,int length,int k) {int number = 0;if(data != NULL && length >0){int first = GetFirstK(data,length,k,0,length-1);int last = GetLastK(data,length,k,0,length-1);if(first > -1 && last > -1)number = last - first + 1;}return number; }/* 10、數組中值出現一次的數字,題目:一個整數數組中除了兩個數字外,其他數字均出現了兩次,寫出程序找出這兩個只出現一次的數字。要求時間復雜度是O(n),空間復雜度為O(1),如果是出現一次的數字只有一個,則我們可以用異或去解決,如下,所示,如果有兩個,我們可以選擇將原數組分成兩個數組,讓出現一次的兩個數分別在兩個數組之中,然后就可以使用異或分別去解決了。 */ // 次數出現一次的只有一個數 int FindAppearOnce(int arr[], int len) {int i = 0;int ret = 0;for(i = 0; i<len; i++){ret = arr[i]^ret;}return ret; }// 次數出現一次的數字有兩個 void FindAppearOnce(int arr[], int len, int* pn1, int* pn2) {int num = 0;//記錄整組異或的結果,即兩個一次出現的數異或的結果int i = 0;int indexOf1 = 0; // indexOf! 即從右邊起第一個為 1 的 bit 位的位置for(i = 0; i<len; i++) //得出整組異或的結果,即兩個一次出現的數異或的結果{num = num^arr[i];} //找出異或結果中從右邊起第一個為1的bit位while(((num&1)!= 1) &&(indexOf1< 8 * sizeof(int))) {num = num>>1;++ indexOf1;}*pn1 = *pn2 = 0;for(i = 0; i<len ; i++)//將原數組分為兩組,分別求出每組中出現一次的數字{int k_bit = (arr[i]>>indexOf1) & 1; //arr[i]第k位的值if(k_bit == 1){*pn1 ^= arr[i]; // 異或}else{*pn2 ^= arr[i]; // 異或}} }/* 11、不用加減乘除做加法。題目:寫一個函數,求兩個整數之和,要求在函數體中不能使用 加減乘除 四則運算符。首先看十進制是如何做的: 5+7=12,三步走 第一步:相加各位的值,不算進位,得到2。 第二步:計算進位值,得到10. 如果這一步的進位值為0,那么第一步得到的值就是最終結果。 第三步:重復上述兩步,只是相加的值變成上述兩步的得到的結果2和10,得到12。同樣我們可以用三步走的方式計算二進制值相加: 5-101,7-111 第一步:相加各位的值,不算進位,得到010,二進制每位相加就相當于各位做異或操作,101^111。 第二步:計算進位值,得到1010,相當于各位做與操作得到101,再向左移一位得到1010,(101&111)<<1。 第三步重復上述兩步, 各位相加 010^1010=1000,進位值為100=(010&1010)<<1。 繼續重復上述兩步: 1000^100 = 1100,進位值為0,跳出循環,1100為最終結果。 */int Add(int num1, int num2) {while(num2!=0){int temp=num1^num2; // 各位相加的值num2=(num1&num2)<<1; // 進位值num1=temp;}return num1; }/* 12、在字符串中找出第一個只出現一次的字符。如輸入 “abaccdeff", 則輸出 ‘b’.如果采用從頭掃描在和后面的字符對比查看是否有相同的字符,則時間復雜度為 O(n^2)。對于此類問題我們 可以采用借助哈希表的方式先遍歷統計,在查看統計數的方式,時間復雜度為 O(n). 方法如下所示: */char firstNotRepearChar(char *pStr) {if (NULL == pStr)return '\0';const int hashTableLen = 256;unsigned int hashtable[hashTableLen];for (unsigned int i = 0; i < hashTableLen; i++)hashtable[i] = 0;char *pHashKey = pStr;while (*pHashKey != '\0')hashtable[*pHashKey++] ++;pHashKey = pStr;while (*pHashKey != '\0'){if (hashtable[*pHashKey] == 1)return *pHashKey;++ pHashKey;}return '\0'; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的面试算法基础及编程 第四弹 (字符串、数值类、或其他常见相关)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用atp-get安装Python-pi
- 下一篇: 驾校一点通下载|驾校一点通电脑版下载