数组面试题-大力出奇迹?
文章目錄
- 數(shù)組中重復(fù)的數(shù)字
- 二維數(shù)組中的查找
- 旋轉(zhuǎn)數(shù)組的最小數(shù)字
- 調(diào)整數(shù)字順序使奇數(shù)位于偶數(shù)前面
- 數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字
- 最小的k個(gè)數(shù)
- 連續(xù)子數(shù)組的最大和
- 數(shù)字序列中某一位的數(shù)字
- 把數(shù)組排成最小的數(shù)
- 和為s的數(shù)字
- 數(shù)組中數(shù)字出現(xiàn)的次數(shù)
相關(guān)推薦(面試專欄查看更多)
- 鏈表面試題(動(dòng)圖詳解)-明明做出來了卻為什么沒有Offer?
- 二叉樹面試題-你已經(jīng)是棵成熟的二叉樹了,要學(xué)會(huì)自己解題
- 數(shù)組面試題-大力出奇跡?
數(shù)組中重復(fù)的數(shù)字
題目:在一個(gè)長度為n的數(shù)組里的所有數(shù)字都在0~n-1的范圍內(nèi)。數(shù)組中某些數(shù)字時(shí)重復(fù)的,但不知道有幾個(gè)數(shù)字重復(fù)了,也不知道每個(gè)數(shù)字重復(fù)了幾次。請(qǐng)找出數(shù)組中任意一個(gè)重復(fù)的數(shù)字。
一般做法可能是吧數(shù)組排序,然后只需從頭到尾掃描排序后的數(shù)組就可以了,復(fù)雜度是O(nlogn)O(nlogn)O(nlogn)。還可以借助哈希表,判斷是否存在重復(fù)數(shù)字,時(shí)間復(fù)雜度是O(n)O(n)O(n)但是也需要O(n)O(n)O(n)大小的空間。
我們來看一種時(shí)間復(fù)雜度是O(n)O(n)O(n)且空間復(fù)雜度是O(1)O(1)O(1)的做法。因?yàn)閿?shù)字范圍是0~n-1,當(dāng)沒有重復(fù)數(shù)字時(shí),數(shù)字i將出現(xiàn)在下標(biāo)為i的位置,當(dāng)有重復(fù)數(shù)字時(shí)有些位置就可能存在多個(gè)數(shù)字。從頭到尾掃描這個(gè)數(shù)字中的每個(gè)數(shù)字,當(dāng)掃描到下標(biāo)為i的數(shù)字是,比較這個(gè)數(shù)字(設(shè)為m)是否和i相同,若相同則繼續(xù)掃描下一個(gè)數(shù)字;否則拿它和下標(biāo)為m的數(shù)字比較,如果相同就找到了一個(gè)重復(fù)的數(shù)字,否則交換這兩個(gè)數(shù)字。
二維數(shù)組中的查找
題目:在一個(gè)二維數(shù)組中,每一行都是按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請(qǐng)完成一個(gè)函數(shù),輸入這樣一個(gè)二維數(shù)組和一個(gè)整數(shù),判斷數(shù)組中是否含有該整數(shù)
對(duì)于排序數(shù)組中的查找,我們第一反應(yīng)是用二分查找,但是在這個(gè)二維數(shù)組中,二分會(huì)存在兩個(gè)區(qū)域(藍(lán)、黃),而且兩個(gè)區(qū)域間還會(huì)重疊(綠),處理起來較復(fù)雜。
我們考慮選取右上角(15)作為起點(diǎn),設(shè)查找的數(shù)字是10,首先15大于10,那15這一列后面的數(shù)是比15還大的,所以15這一列排除;然后分析剩下的列,仍取右上角(9),9小于10,那9這一行前面的數(shù)也是比9小的,排除9這一行;然后分析11,11大于10同樣排除這一列,接下來的右上角就是我們需要找的數(shù)10,退出循環(huán)。
旋轉(zhuǎn)數(shù)組的最小數(shù)字
題目:把一個(gè)數(shù)組最開始的若干元素搬到數(shù)組的末尾,我們稱之為數(shù)組的旋轉(zhuǎn)。輸入一個(gè)遞增排序的數(shù)組的一個(gè)旋轉(zhuǎn),輸出旋轉(zhuǎn)數(shù)組的最小元素。
直觀做法可能就是遍歷數(shù)組找到最小數(shù)字即可,復(fù)雜度是O(n)O(n)O(n),但就完全沒用到給定條件,事情不會(huì)這么簡單。
其實(shí)旋轉(zhuǎn)數(shù)組可以看成由兩個(gè)有序子數(shù)組組成的,最小元素剛好就是這兩個(gè)子數(shù)組的分界線,而對(duì)于有序來說,可以嘗試二分。分別用兩個(gè)指針指向第一個(gè)元素和最后一個(gè)元素,然后求出中間元素,若中間元素位于前面子數(shù)組(大于等于第一個(gè)指針的值),說明最小元素應(yīng)位于中間元素后面,把第一個(gè)指針移到中間;否則位于后面子數(shù)組(小于等于第二個(gè)指針)則移動(dòng)第二個(gè)指針,當(dāng)兩個(gè)指針相鄰時(shí),第二個(gè)指針值就是答案。復(fù)雜度是O(logn)O(logn)O(logn)
但這樣還存在一個(gè)BUG,就是判斷中間指針既大于等于第一個(gè)指針,又小于等于第二個(gè)指針的情況,此時(shí)就無法判斷屬于哪一個(gè)子數(shù)組,移動(dòng)哪一個(gè)指針,這種情況下只能采用順序查找暴力的方法了。
調(diào)整數(shù)字順序使奇數(shù)位于偶數(shù)前面
題目:輸入一個(gè)整數(shù)數(shù)組,實(shí)現(xiàn)一個(gè)函數(shù)來調(diào)整該數(shù)組中數(shù)組的順序,使得所有奇數(shù)位于數(shù)組前半部分,所有偶數(shù)位于數(shù)組后半部分
最笨的方法無非就是遍歷數(shù)組,每當(dāng)遇到一個(gè)偶數(shù),就把他后面的數(shù)往前挪,時(shí)間復(fù)雜度時(shí)O(n2)O(n^2)O(n2)。
我們可以定義維護(hù)指針,一個(gè)從前向后維護(hù)奇數(shù),一個(gè)從后向前維護(hù)偶數(shù),當(dāng)?shù)谝粋€(gè)指針遇到偶數(shù)時(shí),就移動(dòng)第二個(gè)指針尋找一個(gè)奇數(shù),然后交換這兩個(gè)數(shù)字,當(dāng)兩指針相遇則退出。復(fù)雜度是O(n)O(n)O(n)
數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字
題目:數(shù)組中有一個(gè)數(shù)字出現(xiàn)的次數(shù)超過數(shù)組長度的一半,請(qǐng)找出這個(gè)數(shù)字。
一般做法是把數(shù)組排序,然后中位數(shù)就是答案,時(shí)間復(fù)雜度是O(nlogn)O(nlogn)O(nlogn)。
解法二:
分析發(fā)現(xiàn),數(shù)組中一個(gè)數(shù)字出現(xiàn)次數(shù)超過數(shù)組一半,也就是說其他所有數(shù)字出現(xiàn)次數(shù)加起來也沒有它多。因此當(dāng)我們遍歷下一個(gè)數(shù)字的時(shí)候,若和上一個(gè)數(shù)字相同則次數(shù)加一;若不同則次數(shù)減一,當(dāng)次數(shù)為0的時(shí)候,需要更新保存的數(shù)字并設(shè)次數(shù)為1。復(fù)雜度是O(n)O(n)O(n)
(插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/
最小的k個(gè)數(shù)
題目:輸入n個(gè)整數(shù),找出其中最小的k個(gè)數(shù)。
一般做法還是排序,然后輸出前k的數(shù)即可,復(fù)雜度是O(nlogn)O(nlogn)O(nlogn)。
我們可以創(chuàng)建一個(gè)大小為k的數(shù)據(jù)容器來存儲(chǔ)最小的k個(gè)數(shù)字,每次讀入一個(gè)數(shù)的時(shí)候,判斷容器中是否已有k個(gè)數(shù)據(jù),沒有的話則放入,有的話則比較容器中的最大值,若大于當(dāng)前數(shù)則替換之,保持容器中是目前最小的k個(gè)數(shù),復(fù)雜度是O(nlogk)O(nlogk)O(nlogk),適合海量數(shù)據(jù)。
#include<bits/stdc++.h> using namespace std; void getLeastK(int* a,int len, int k) {if (a == nullptr || k <= 0 || k > len)return;multiset<int,greater<int>>s;//可重復(fù)大根堆multiset<int, greater<int>>::iterator it;for (int i = 0; i < len; i++) {if (s.size() < k)s.insert(a[i]);else {it = s.begin();if (*it > a[i]) {s.erase(it);s.insert(a[i]);}}}for (it = s.begin(); it != s.end(); it++)printf("%d ", *it); } int main() {int a[6] = { 1,4,5,2,2 };getLeastK(a, 5, 3);return 0; } //運(yùn)行結(jié)果:2 2 1連續(xù)子數(shù)組的最大和
題目:輸入一個(gè)整型數(shù)組,數(shù)組里有正數(shù)也有負(fù)數(shù)。數(shù)組中一個(gè)或連續(xù)多個(gè)整數(shù)組成一個(gè)子數(shù)組。求所有數(shù)組的和的最大值,要求時(shí)間復(fù)雜度是O(n)O(n)O(n)。
當(dāng)前面累加的和小于0時(shí),則拋棄前面的,從當(dāng)前數(shù)開始累加,否則加上前面的累加和,動(dòng)態(tài)的維護(hù)一個(gè)最大值。此外也可以用動(dòng)態(tài)規(guī)劃求解,設(shè)dp[i]dp[i]dp[i]表示第iii個(gè)數(shù)字結(jié)尾的子數(shù)組的最大和,dp[i]=max(a[i],dp[i?1]+a[i])dp[i]=max(a[i],dp[i-1]+a[i])dp[i]=max(a[i],dp[i?1]+a[i])。
#include<bits/stdc++.h> using namespace std; int getMaxSub(int* a,int len) {int* dp = new int[len];dp[0] = a[0];for (int i = 1; i < len; i++) {dp[i] = max(a[i], dp[i - 1] + a[i]);}int mx = dp[0];for (int i = 1; i < len; i++)mx = max(mx, dp[i]);return mx; } int main() {int a[7] = { 1,4,-5,2,2 ,-1,3 };printf("%d", getMaxSub(a, 7));return 0; } //運(yùn)行結(jié)果:6數(shù)字序列中某一位的數(shù)字
題目:數(shù)字以0123456789101112131415…的格式序列化到一個(gè)字符序列中。在這個(gè)序列中第5位(從0開始計(jì)數(shù))是5,第13位是1,第19位是4等等。請(qǐng)寫一個(gè)函數(shù),求任意第n位對(duì)應(yīng)的數(shù)字。
笨方法就是直接構(gòu)造序列,然后求出第n位。但是我們有更快的方法,找出某些規(guī)律從而跳過若干數(shù)字。
首先只有0-9這10個(gè)一位數(shù),然后是10-99這90個(gè)兩位數(shù),然后是100-999這900個(gè)三位數(shù),以此類推,不難發(fā)現(xiàn),n≠1時(shí),共有9*10n-1個(gè)n位數(shù),這樣我們就能跳過若干位,直接到第n位所對(duì)應(yīng)的數(shù)字,然后再去確定是這個(gè)數(shù)字的第幾位。
#include<bits/stdc++.h> using namespace std; int digitAtIndex(int index) {if (index < 0)return -1;int n = 1;//n位數(shù)while (1) {//找到位于幾位數(shù)之間int cnt = n == 1 ? 10 : 9 * pow(10, n - 1);//n位數(shù)個(gè)數(shù)if (index < cnt * n)break;index -= n * cnt;n++;}int num = n == 1 ? 0 : pow(10, n - 1);//n位數(shù)的第一個(gè)數(shù)num += index / n;//index位于第幾個(gè)數(shù)int r = n - index % n;//這個(gè)數(shù)的第幾位for (int i = 1; i < r; i++)num /= 10;return num % 10; } int main() {printf("第1位:%d\n", digitAtIndex(1));printf("第5位:%d\n", digitAtIndex(5));printf("第13位:%d\n", digitAtIndex(13));printf("第19位:%d\n", digitAtIndex(19));return 0; } /*運(yùn)行結(jié)果 第1位:1 第5位:5 第13位:1 第19位:4 */把數(shù)組排成最小的數(shù)
題目:輸入一個(gè)正整數(shù)數(shù)組,把數(shù)組里所有數(shù)字拼接起來排成一個(gè)數(shù),打印能拼接處的所有數(shù)字中最小的一個(gè)。例如,輸入數(shù)組{3,32,321},則能排成最小數(shù)字是321323。
直接用全排列的話,肯定超時(shí),而且還是一個(gè)隱形的大數(shù)問題。一個(gè)直觀的解決方法就是先把數(shù)字轉(zhuǎn)成成字符串,把數(shù)字n和m拼接起來的到nm和mn,它們的位數(shù)是相同的,因此比較他們的大小只需要按照字符串大小的比較規(guī)則就可以了,排序之后的順序組成的數(shù)字就是最小的數(shù),證明略。
#include<bits/stdc++.h> using namespace std; bool cmp(string a,string b){string c=a+b;string d=b+a;return c<d; } void printMin(int* a, int len) {if (a == nullptr || len <= 0)return;string* s = new string[len];char* c = new char[len];for (int i = 0; i < len; i++) {sprintf(c, "%d", a[i]);s[i] = c;}sort(s, s + len,cmp);for (int i = 0; i < len; i++)cout << s[i]; } int main() {int a[3] = { 3,32,321 };printMin(a, 3);return 0; } //運(yùn)行結(jié)果:321323和為s的數(shù)字
題目:輸入一個(gè)遞增排序的數(shù)組和一個(gè)數(shù)字s,在數(shù)組中查找兩個(gè)數(shù),使得它們的和正好是s。如果有多對(duì)數(shù)字的和等于s,則輸出任意一對(duì)即可。
由于數(shù)組是遞增的(也可以自己排序下),那我們可以用雙指針類似尺取法的思路來求解。定義兩個(gè)指針,指向第一個(gè)和最后一個(gè)元素,當(dāng)這兩個(gè)元素的和大于s時(shí),則把第二個(gè)指針向前移動(dòng),否則向后移動(dòng)第二個(gè)指針即可。
比如數(shù)組{2,4,5,7,11,15}求和為16的過程:
數(shù)組中數(shù)字出現(xiàn)的次數(shù)
題目:求數(shù)組中只出現(xiàn)一次的兩個(gè)數(shù)字。
一個(gè)整型數(shù)組里除了兩個(gè)數(shù)字之外,其他數(shù)字都出現(xiàn)了兩次。請(qǐng)寫程序找出這兩個(gè)只出現(xiàn)一次的數(shù)字。要求是否復(fù)雜度時(shí)O(n)O(n)O(n),空間復(fù)雜度是O(1)O(1)O(1)。
我們想到異或運(yùn)算的一個(gè)性質(zhì):任何一個(gè)數(shù)字異或他自己都等于0。也就是說,如果我們從頭到尾依次異或數(shù)組中的每個(gè)數(shù)字,那么最終結(jié)果剛好是那個(gè)只出現(xiàn)一次的數(shù)字,那些出現(xiàn)兩次以上的數(shù)字全部在異或中抵消了。
可這道題目是有兩個(gè)只出現(xiàn)一次的數(shù)字。怎么拆成兩個(gè)子數(shù)組呢?我們先遍歷數(shù)組全部異或一遍,得到的結(jié)果就是那兩個(gè)數(shù)字的異或結(jié)果,由于這兩個(gè)數(shù)字不同,所以異或結(jié)果不為0,在二進(jìn)制中至少有一位為1,那么我們就可以根據(jù)這一位是不是為1,把數(shù)字劃分成兩個(gè)子數(shù)組,然后就能求解了。而且相同的數(shù)不可能分別分配到兩個(gè)子數(shù)組中,因?yàn)樗鼈兊亩M(jìn)制也是相同的。
#include<bits/stdc++.h> using namespace std; void findOnce(int* a, int len) {if (a == nullptr || len <= 0)return;int XOR = 0;for (int i = 0; i < len; i++) //求異或值XOR ^= a[i];int idx = 0;while ((XOR & 1) == 0) {//求最右的1XOR >>= 1;idx++;}int n=0, m=0;//兩個(gè)子數(shù)組分別異或for (int i = 0; i < len; i++) {if (((a[i] >> idx) & 1) == 0)n ^= a[i];else m ^= a[i];}printf("%d %d", n, m); } int main() {int a[8] = { 2,4,3,4,6,3,5,2 };findOnce(a, 8);return 0; } //運(yùn)行結(jié)果:6 5原創(chuàng)不易,請(qǐng)勿轉(zhuǎn)載(本不富裕的訪問量雪上加霜 )
博主首頁:https://wzlodq.blog.csdn.net/
微信公眾號(hào):吾仄lo咚鏘
如果文章對(duì)你有幫助,記得一鍵三連?
總結(jié)
以上是生活随笔為你收集整理的数组面试题-大力出奇迹?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多态是什么 父类如何调用子类的方法(美团
- 下一篇: 移动OA办公——Smobiler第一个开