目錄
面試編程題總結(持續更新)
從頭到尾打印2020.3.31
輸入一個鏈表,按鏈表從尾到頭的順序返回一個ArrayList。
方法一:棧實現。
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> value;//注意vector的用法
ListNode *p=NULL;//將p指向頭結點
p=head;
stack<int> stk;//棧的使用方式
while(p!=NULL){
stk.push(p->val);//壓棧操作
p=p->next;
}
while(!stk.empty()){//while循環中的條件要放.empty()
value.push_back(stk.top());//這是動態數組vector的存數方式。
stk.pop();
}
return value;
}
};
總結:
1.這種題目逆序的還是要想到棧的先進後出的特性。
2.掌握vector data:實現了動態數組,用於元素數量變化的對象數組。
vector插入元素的形式是:data.push_back(元素);
vector清空容器:data.clear();
vector容器的長度:data.size();
訪問vector元素的方式:與訪問數組的方式相同
要特別注意vector<vector>雙重vetor->二維數組。
3.c++中棧的用法:
push(): 向棧內壓入一個成員;
pop(): 從棧頂彈出一個成員;
empty(): 如果棧爲空返回true,否則返回false;
top(): 返回棧頂,但不刪除成員;
size(): 返回棧內元素的大小;
4.特別注意結構體中value是怎麼聲明的,別一上來就是p->value;
方法二:數組翻轉
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> value;
ListNode *p=NULL;
p = head;
while(p!=NULL){//直接先壓如動態數組之中
value.push_back(p->val);
p = p ->next;
}
int temp=0;
int i = 0;
int j=value.size()-1;
while(i<j){//然後直接進行數組翻轉就可以了
temp= value[i];
value[i]=value[j];
value[j] = temp;
i++;
j--;
}
return value;
}
};
這個跟上面的區別也就只有,這個方法充分利用了vetor的類數組的性質。可以,直接把vector當成一個數組,然後,直接進行代換。
方法三:遞歸思路
class Solution{
public:
vector<int> printListFromTailTohead(ListNode *head){
ListNode *p =NULL;
p = head;
if(p!=NULL){
if(p->next!=NULL){//不斷的往下面遞歸,跑到最末尾。
printListFromTailToHead(p->next);
}
value.push_back(p->val);//跑到最末尾的時候,執行的就是這一句了。
}
return value;
}
};
總結:這個方法就很奇妙了,先是不斷的判斷p->next是否爲NULL,假如真的遞歸到了最末尾,這個時候的p->val,剛好就是鏈表的最後一個元素了。這個時候,就可以將這個元素逐漸的push_back進去vector容器之中,然後返回value()就可以了。
重建二叉樹2020.3.31
題目描述
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
import java.util.*;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length==0){//這是比較小心的操作,當length==0時,直接return null
return null;
}
int rootVal = pre[0];//首先,根據先序遍歷拿到根節點
if(pre.length==1)
return new TreeNode(rootVal);//這是返回一課樹的表現
TreeNode root = new TreeNode(rootVal);//新建一棵樹,開始裝左右樹。
int rootIndex = 0;//
for(int i=0;i<in.length;i++){//從這個for循環中拿到中序遍歷的根節點的地方
if(rootVal==in[i]){
rootIndex = i;
break;
}
}
root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,rootIndex+1),Arrays.copyOfRange(in,0,rootIndex));//這個地方就只能靠遞歸建樹了。給出左子樹中,先序,中序遍歷的序列
root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,rootIndex+1,pre.length),Arrays.copyOfRange(in,rootIndex+1,in.length));//這個地方就只能靠遞歸建樹了。給出右子樹中,先序,中序遍歷的序列
return root;//到最後,你就會因爲給出的序列只有一個節點,跳出這個遞歸
}
}
總結:遞歸,肯定非常重要的是遞歸出來的條件。這時,要注意,判斷說結點數爲0和結點數爲1的情況。然後,找到中序遍歷序列的根節點的位置。
接下來,就可以使用遞歸。把root的左子樹的範圍給進行,遞歸建樹。用到Arrays.copyOfRange數組複製。注意要先import java.util.*,然後再使用這個函數。Arrays.copyOfRange(T[ ] original,int from,int to)將一個原始的數組original,從下標from開始複製,複製到上標to,生成一個新的數組。注意這裏包括下標from,不包括上標to。
Java複製數組的四種方法:arraycopy()方法、clone() 方法、copyOf()和copyOfRange()方法。
Arrays.copyOf(dataType[] srcArray,int length);
Arrays.copyOfRange(dataType[] srcArray,int startIndex,int endIndex)
System.arraycopy(dataType[] srcArray,int srcIndex,int destArray,int destIndex,int length)
array_name.clone()
用兩個棧實現隊列2020.4.2
class Solution
{
public:
void push(int node) {
stack1.push(node);//先入棧1
}
int pop() {
int a;
if(stack2.empty()){
while(!stack1.empty()){
a = stack1.top();//你要先拿到這個值,再push,或pop,不然,一旦不拿着,直接push或pop就沒了
stack2.push(a);
stack1.pop();
}
}
a = stack2.top();
stack2.pop();
return a;
}
private:
stack<int> stack1;
stack<int> stack2;
};
總結:整體的思路其實很簡單,就是在
入隊列:直接壓棧1
出隊列:先判斷棧2是否爲空,若爲空,就得把棧1 的所有元素搞到棧2,讓元素把順序反過來。然後,再開始真正的從棧2出元素。不然,或棧2 不爲空的話,就直接從棧2出元素就好了。你畫個圖,也許就能明白了。
但要注意,我代碼中的那一點。出棧時,一定先用stack.top(),拿到要出棧的元素,不然,你壓棧的時候,就沒辦法壓了。
旋轉數組的最小數字2020.4.2
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
//二分查找使用時,要把0個元素和1個元素的情況都要考慮到。
int length_vector=rotateArray.size();//首先,一定要先考慮特殊情況,length=0的情況
if(length_vector==0)
return 0;
int low = 0;//二分查找的固定套路
int high = length_vector-1;
if(low==high)//只有一個元素時的情況。
return rotateArray[low];
while(low<high){
if(rotateArray[low]<rotateArray[high])//這個if考慮的是旋轉後的數組是有序的時候
return rotateArray[low];
if(high==low+1)//這個考慮的是當只有兩個元素得到時候
return rotateArray[high];
int mid = (high+low)/2;
if(rotateArray[low]>=rotateArray[mid]&&rotateArray[high]>=rotateArray[mid]){//這個if考慮的是,數組中出現相同元素的時候,只能採用順序查找了。
int index = low;
for(int i=low+1;i<=high;i++){
if(rotateArray[index]>rotateArray[i])
index = i;//把最小元素的下標就可以找出來了。
}
return rotateArray[index];
}
if(rotateArray[low]<rotateArray[mid])//二分查找的經典方式。
low = mid+1;
else if(rotateArray[high]>rotateArray[mid]) //最小元素在low~mid之間
high=mid-1;
}
return -1;
}
};
總結:當然,這題也可以用順序查找,不過就是查找一個最小值,但要注意,這裏要考察的是二分,用二分的時間複雜度爲O(nlogn),順序就只是O(n)了。
質因數的統計2020.4.3
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int a[100006];
int main(){
int n;
cin>>n;
int ans = 0;
for(int i=1;i<=n;i++){//把這個範圍內要弄的數放在數組中比較方便
a[i]=i;
}
for(int i=2;i<=n;i++){//確保從a[2]開始,因爲質因數的分解從2開始數。
if(a[i]>1){//這是循環的破出條件。
for(int j=2;j<=sqrt(n);j++){//注意這裏的sqrt很重要,減少了很多數的比較
while(a[i]%j==0){//不斷的除,就能得到想要的那個質數。
a[i] = a[i]/j;
ans++;
// cout<<ans<<endl;
}
}
}
}
for(int i=1;i<=n;i++){//這個地方應該只是確保把本來就是質數的那一個
//也就是上面的循環沒有處理的那個算進來,不然,就會少了本來就是質數的那一份。
if(a[i]>1)
ans++;
}
cout<<ans;
return 0;
}
總結:基本把思路已經寫在代碼中了,這個代碼已經很容易理解了。
跳臺階2020.4.4
題目描述
一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(先後次序不同算不同的結果)。
class Solution {
public:
int jumpFloor(int number) {
if(number<1){
return 0;
}
else if(number==1){
return 1;
}
else if(number==2){
return 2;
}
else{
int result =0;
int pre1 =0;
int pre2 = 1;
for(int i=1;i<=number;i++){
result = pre1+pre2;
pre1 = pre2;
pre2 = result;
}
return result;
}
}
};
總結:仔細研究一下,其實這個題目也是一個斐波拉契數列而已。遞歸也可以,不過遞歸太浪費資源,很多時候,這種題目是一般過不了的。
二進制中的個數2020.4.7
#include<cstdio>
#include<iostream>
using namespace std;
int NumberOf1_low(int n) {
int count = 0;
int flag = 1;
while (flag != 0) {
if ((n & flag) != 0) {
count++;
}
flag = flag << 1;//將flag的1進行移位,逐位與n進行相比。
//注意,若對n進行移位,負數會導致死循環,因爲對n進行右移時,
//是補1的,所以不能那麼做。
}
return count;
}
int main(){
int n;
cin>>n;
cout<<NumberOf1_low(n);
return 0;
}
總結:我可能還是做的太少,一開始想到只有是將這個二進制給表示出來,逐漸的循環,從而得到1的個數。沒想到,在n於1進行與操作的時候,他們之間的進制已經自動轉換成爲二進制了。
數值的整數次方
public class Solution {
public double Power(double base, int exponent) {
int n=exponent;
//首先要區分好情況。
if(exponent<0){
if(base==0)
throw new RuntimeException("分母不能爲0");
exponent = -exponent;
}
else if(exponent==0){
return 1;
}
double res =1,cur =base;
while(exponent!=0){//這個是快速冪的解法,也就是把指數分解成二進制,然後,逐步的乘
if((exponent&1)==1)//如果這個位爲1,那就是要乘了。
res*=cur;
cur*=cur;//不管上面這個If是否有執行,那個指數肯定是要翻倍的,比較是2的多少次方。
exponent=exponent>>1;//右移一位,逐漸看一下指數的二進制形式的各個位。
}
if(n>=0)//判斷當時的指數是正數還是負數。
return res;
else{
return 1/res;
}
}
}
總結:這個題目歸根到底還是用空間換取時間的一道題目,因爲直接乘的話,確實花費的計算的時間太大了。還是比較值得掌握的一道題目的。