面试题11:数值的整数次方
实现函数double Power(double base,int exponent),求base的exponent次方。不适用库函数,同时无需考虑大数问题。
思路:
题目简单,但要考虑周全,并且如何实现周全的方法也是关键。
考虑的问题:(1)base为0 (2)exponent<=0
因此这样写比较完备:
计算次方时,不考虑exponent的正负情况,而是直接计算base的|exponent|次方,再根据exponent的正负看是否需要取倒数。
还有一点,就是double情况下,判断是否为0,不能用==。而是应该计算与0.0的差,看是否在一个很小的范围内(例如:差的绝对值小于0.0000001,即该小数为0)
最后,需要注意强制类型转换。(将exponent的类型由int转为unsigned int)
1 #include <iostream> 2 using namespace std; 3 double Power(double base,int exponent); 4 double Power_Unsigned(double base,unsigned int exponent); 5 bool Equal(double a,double b); 6 int main() 7 { 8 cout<<Power(1.1,2)<<endl; 9 return 0; 10 } 11 double Power(double base,int exponent) 12 { 13 if(Equal(base,0.0)&&(exponent<0)) 14 { 15 cout<<"Error!"<<endl; 16 return 0; 17 } 18 unsigned int absExponent; 19 if(exponent<0) 20 absExponent=(unsigned int)(-exponent); 21 else absExponent=(unsigned int)exponent; 22 double result=Power_Unsigned(base,absExponent); 23 if(exponent<0) 24 result=1.0/result; 25 return result; 26 } 27 double Power_Unsigned(double base,unsigned int exponent) 28 { 29 double result=1.0; 30 for(int i=0;i<exponent;i++) 31 { 32 result=result*base; 33 } 34 return result; 35 } 36 bool Equal(double a,double b) 37 { 38 if((a-b<0.0000001)||(a-b>-0.0000001)) 39 return true; 40 else return false; 41 }
面试题12:打印1到最大的n位数(大数陷阱)
输入数字n,按顺序打印出从1到n最大的n位十进制数。比如输入3,则打印出1,2,3....999。
思路:
通常会想到计算出最大的n位数,可以直接计算10的n次方=max,按顺序输出1到max-1即可
然而,考虑到大数问题,就没这么简单了。
具体实现:
使用char数组模拟加法,每次加1,实现递增打印。
首先,将字符数组初始化为全'0',然后每次为字符串表示的数字模拟加1,打印出来,同时应判断是否达到最高位。
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 bool If_Max(char* arr); 5 void Print_to_Max(int n); 6 void Print(char* arr); 7 int main() 8 { 9 int n; 10 cin>>n; 11 Print_to_Max(n); 12 return 0; 13 } 14 void Print_to_Max(int n) 15 { 16 if(n<=0) return; 17 char* arr=new char[n+1]; 18 memset(arr,'0',n); 19 arr[n]='\0'; 20 while(!If_Max(arr)) 21 { 22 Print(arr); 23 } 24 } 25 bool If_Max(char* arr) 26 { 27 bool flag=false; 28 int jw=0; 29 int len=strlen(arr); 30 for(int i=len-1;i>=0;i--)//对所有位进行处理,但如果最低位没有进位的话,应该及时跳出循环。 31 { 32 int temp=arr[i]-'0'+jw;//第i位上值的大小(整数),用于判断该位是否有进位 33 if(i==len-1)//只有最后一位才+1 34 temp++; 35 if(temp>=10) 36 { 37 if(i==0) flag=true;//最高位产生进位则溢出! 38 else 39 { 40 temp-=10; 41 jw=1; 42 arr[i]='0'; 43 } 44 45 } 46 else 47 { 48 arr[i]='0'+temp; 49 break;//如果最低位没有进位的话,则直接跳出循环,无需为高位进行处理! 50 } 51 } 52 return flag; 53 } 54 void Print(char* arr) 55 { 56 int len=strlen(arr); 57 bool start=false; 58 for(int i=0;i<len;i++) 59 { 60 if(arr[i]!='0') 61 start=true; 62 if(start) 63 cout<<arr[i]; 64 } 65 cout<<'\t'; 66 }
面试题13:在O(1)时间删除链表结点
在给定单项链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。链表结点定义如下:
- struct ListNode
- {
- int m_nValue;
- ListNode* m_pNext;
- };
函数定义如下:
思路:
通常情况,一般先遍历到被删除结点的前一个结点,在进行删除,但这样的时间复杂度是O(n),因此不能使用。
可以这样解决:
将被删除结点pNode的后一个结点pNext的值复制给pNode,改为删除pNext即可。
同时应考虑几个特殊情况:pNode为头结点,pNode为尾结点,该链表只有一个结点;
1 #include <iostream> 2 using namespace std; 3 struct ListNode 4 { 5 int m_nValue; 6 ListNode* m_pNext; 7 }; 8 ListNode* CreateList(){ListNode* head=NULL; return head;} 9 void Insert(ListNode** head,int val); 10 void Print(ListNode* head); 11 void Delete(ListNode** head,ListNode* del); 12 int main() 13 { 14 ListNode* head=CreateList(); 15 int val; 16 while(cin>>val) 17 Insert(&head,val); 18 Print(head); 19 Delete(&head,head); 20 Print(head); 21 return 0; 22 } 23 void Insert(ListNode** head,int val) 24 { 25 ListNode* temp=new ListNode(); 26 temp->m_nValue=val; 27 temp->m_pNext=NULL; 28 if(*head==NULL) 29 *head=temp; 30 else 31 { 32 ListNode* p=*head; 33 while(p->m_pNext) 34 p=p->m_pNext; 35 p->m_pNext=temp; 36 } 37 } 38 void Print(ListNode* head) 39 { 40 if(head) 41 { 42 ListNode* p=head; 43 while(p) 44 { 45 cout<<p->m_nValue<<" "; 46 p=p->m_pNext; 47 } 48 } 49 cout<<endl; 50 } 51 void Delete(ListNode** head,ListNode* del) 52 { 53 if(!*head||!del) 54 return; 55 if(del->m_pNext) 56 { 57 ListNode* temp=del->m_pNext; 58 del->m_nValue=temp->m_nValue; 59 del->m_pNext=temp->m_pNext; 60 delete temp; 61 temp=NULL; 62 } 63 else if(*head==del) 64 { 65 delete del; 66 del=NULL; 67 *head=NULL; 68 } 69 else 70 { 71 ListNode *p=*head; 72 while(p->m_pNext!=del) 73 { 74 p=p->m_pNext; 75 } 76 p->m_pNext=del->m_pNext; 77 delete del; 78 del=NULL; 79 } 80 }
面试题14:调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整数组中数字的顺序,使得所有奇数位于数组的前半部分,偶数位于数组的后半部分。
思路1:
使用辅助数组temp,遍历数组arr,将遍历到所有奇数逐个放入temp中,再遍历一遍,将所有偶数放入temp剩余部分中;
最后同时遍历temp和arr,将arr中的元素用temp中的元素替换
思路2:
使用两个指针,分别指向第一个和最后一个元素,设为i,j(i<j),向后移动i,直到i指向的为偶数,开始向前移动j,直到指向的位奇数。若满足(i<j)则互换数据。直到i==j;
思路3:(一个扩展性的解法)在思路2的基础上进行修改
将思路2中,循环语句中的判断条件使用一个函数表示即可
1 #include <iostream> 2 using namespace std; 3 void Change1(int* arr,int len); 4 void Change2(int* arr,int len); 5 void Change3(int* arr,int len); 6 bool func(int i); 7 int main() 8 { 9 int arr[]={1,2,3,4,5}; 10 int len=sizeof(arr)/sizeof(arr[0]); 11 // Change1(arr,len); 12 Change3(arr,len); 13 for(int i=0;i<len;i++) 14 cout<<arr[i]<<" "; 15 return 0; 16 } 17 void Change1(int* arr,int len) 18 { 19 if(!arr||len<=0) 20 return; 21 22 int temp[len]; 23 int j=0; 24 for(int i=0;i<len;i++) 25 { 26 if(arr[i]%2==1) 27 temp[j++]=arr[i]; 28 } 29 for(int i=0;i<len;i++) 30 { 31 if(arr[i]%2==0) 32 temp[j++]=arr[i]; 33 } 34 for(int i=0;i<len;i++) 35 arr[i]=temp[i]; 36 } 37 38 void Change2(int* arr,int len) 39 { 40 if(!arr||len<=0) 41 return; 42 int i=0,j=len-1; 43 while(i<j) 44 { 45 while(i<j&&arr[i]%2!=0) 46 i++; 47 while(i<j&&arr[j]%2!=1) 48 j--; 49 if(i<j) 50 { 51 int temp; 52 temp=arr[i]; 53 arr[i]=arr[j]; 54 arr[j]=temp; 55 } 56 } 57 } 58 59 void Change3(int* arr,int len) 60 { 61 if(!arr||len<=0) 62 return; 63 int i=0,j=len-1; 64 while(i<j) 65 { 66 while(i<j&&!func(arr[i])) 67 i++; 68 while(i<j&&func(arr[j])) 69 j--; 70 if(i<j) 71 { 72 int temp; 73 temp=arr[i]; 74 arr[i]=arr[j]; 75 arr[j]=temp; 76 } 77 } 78 } 79 bool func(int i) 80 { 81 if(i%2==0) 82 return true; 83 else return false; 84 }
面试题15:链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。为了符合大多数人的习惯,从1开始数,即链表最后一个结点为倒数第一个结点。例如:一个链表有6个结点,从头开始的值为:1、2、3、4、5、6。这个链表的倒数第3个结点的值是4。
思路1:
首先,遍历一遍链表,得到链表的长度len;
然后,从头遍历链表,第len-i+1即为倒数第i个结点。
思路2:(这种思路在链表中很常见!)--两个指针,一快一慢或者一个先走一个后走
思路1的效率还不够高;
可以设置两个指针,均指向head结点。然后第一个指针后移k个结点后,两个指针同时后移,直到第一个指针指向尾结点,这样第一个指针和第二个指针之间的距离为k。因此第二个指针恰好指向倒数第k个结点。
需要注意鲁棒性!
根据思路二,可以进行延伸:
延伸1:求链表的中间结点;
延伸2:判断链表是否是一个环形结构-两个指针,一个一次一步,一个一次两部,能相遇即为环形
面试题16:反转链表
定义一个函数,输入链表的头结点,反转该链表并输出反转链表的头结点。
- struct ListNode
- {
- int m_nKey;
- ListNode* m_pNext;
- };
思路:
这题没什么多讲的,注意反转时候的细节:
反转时,保存pNext;
p->next=pPre;
pPre=p;
p=pNext;
1 #include <iostream> 2 using namespace std; 3 struct ListNode 4 { 5 int m_nValue; 6 ListNode* m_pNext; 7 }; 8 ListNode* CreateList(){ListNode* head=NULL; return head;} 9 void Insert(ListNode** head,int val); 10 ListNode* Revert(ListNode* head); 11 void Print(ListNode* head); 12 int main() 13 { 14 ListNode* head=CreateList(); 15 int val; 16 while(cin>>val) 17 Insert(&head,val); 18 Print(head); 19 ListNode* newHead=Revert(head); 20 Print(newHead); 21 return 0; 22 } 23 void Insert(ListNode** head,int val) 24 { 25 ListNode* temp=new ListNode(); 26 temp->m_nValue=val; 27 temp->m_pNext=NULL; 28 if(!*head) 29 *head=temp; 30 else 31 { 32 ListNode* p=*head; 33 while(p->m_pNext) 34 p=p->m_pNext; 35 p->m_pNext=temp; 36 } 37 } 38 void Print(ListNode* head) 39 { 40 if(head) 41 { 42 ListNode* p=head; 43 while(p) 44 { 45 cout<<p->m_nValue<<" "; 46 p=p->m_pNext; 47 } 48 } 49 cout<<endl; 50 } 51 ListNode* Revert(ListNode* head) 52 { 53 if(!head) return NULL; 54 if(!head->m_pNext) return head; 55 ListNode* p=head; 56 ListNode* pre=NULL;//初始化NULL,即为倒转的尾结点NULL; 57 ListNode* RevertHead=NULL; 58 while(p) 59 { 60 ListNode* next=p->m_pNext;//保存next,防止链表断裂导致无法遍历; 61 if(next==NULL) 62 RevertHead=p; 63 p->m_pNext=pre; 64 pre=p; 65 p=next; 66 } 67 return RevertHead; 68 }