剑指offer——高质量的代码(第三章)

面试题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 }
View Code

 

面试题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 }  
View Code

 

 

 
面试题13:在O(1)时间删除链表结点
    在给定单项链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。链表结点定义如下:
  1. struct ListNode  
  2. {  
  3.     int m_nValue;  
  4.     ListNode* m_pNext;  
  5. };  
  函数定义如下:
思路:
    通常情况,一般先遍历到被删除结点的前一个结点,在进行删除,但这样的时间复杂度是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 }  
View Code

 

 
 
面试题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 }  
View Code

 

 

面试题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:反转链表
    定义一个函数,输入链表的头结点,反转该链表并输出反转链表的头结点。
  1. struct ListNode  
  2. {  
  3.     int m_nKey;  
  4.     ListNode* m_pNext;  
  5. };  
思路:
    这题没什么多讲的,注意反转时候的细节:
    反转时,保存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  }
View Code

 

 

 

 

 

面试题17:合并两个排序的链表
    输入两个递增排序的链表,合并这两个链表并使得新链表中的结点依旧按照递增排序。如图所示:
思路1:
    将两个链表合并为新的链表:新建一个链表作为结果:
            因此需要定义一个头结点,初始为NULL;
            设两个指针分别遍历两个链表,比较两个指针所指向结点的值;
            在新链表中新建结点,结点值为两个指针所指结点值的较小者,并且将较小者的指针后移;
            重复上述循环,直到某个指针指向空;
            最后,将未走完的链表中的结点加入到新链表的末尾。
 
思路2:
    与思路1基本相同,但使用递归的方式进行,代码更加简洁,当然,运算复杂多了。
  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* Merge1(ListNode* head1,ListNode* head2);
 11 ListNode* Merge2(ListNode* head1,ListNode* head2);
 12 void Print(ListNode* head);
 13 int main()
 14 {
 15     ListNode* head1=CreateList();
 16     ListNode* head2=CreateList();
 17     int val;
 18     while(cin>>val)
 19         Insert(&head1,val);
 20     cin.clear();
 21     cin.sync();
 22     while(cin>>val)
 23         Insert(&head2,val);
 24     ListNode* head=Merge2(head1,head2);
 25     Print(head);
 26     return 0;
 27 }
 28 void Insert(ListNode** head,int val)
 29 {
 30     ListNode* temp=new ListNode();
 31     temp->m_nValue=val;
 32     temp->m_pNext=NULL;
 33     if(!*head)
 34         *head=temp;
 35     else
 36     {
 37         ListNode* p=*head;
 38         while(p->m_pNext)
 39             p=p->m_pNext;
 40         p->m_pNext=temp;
 41     }
 42 }
 43 void Print(ListNode* head)
 44 {
 45     if(head)
 46     {
 47         ListNode* p=head;
 48         while(p)
 49         {
 50             cout<<p->m_nValue<<" ";
 51             p=p->m_pNext;
 52         }
 53     }
 54     cout<<endl;
 55 }
 56 ListNode* Merge1(ListNode* head1,ListNode* head2)
 57 {
 58     if(!head1&&!head2) return NULL;
 59     if(!head1&&head2) return head2;
 60     if(head1&&!head2) return head1;
 61     ListNode* p1=head1,*p2=head2;
 62     ListNode* newHead=NULL;
 63     ListNode* p=newHead;
 64     while(p1&&p2)
 65     {
 66         ListNode* temp=new ListNode();
 67         temp->m_pNext=NULL;
 68         if(p1->m_nValue<=p2->m_nValue)
 69         {
 70             temp->m_nValue=p1->m_nValue;
 71             p1=p1->m_pNext;
 72         }
 73         else
 74         {
 75             temp->m_nValue=p2->m_nValue;
 76             p2=p2->m_pNext;
 77         }
 78         if(newHead==NULL)
 79         {
 80             newHead=temp;
 81             p=newHead;
 82         }
 83         else
 84         {
 85             p->m_pNext=temp;
 86             p=p->m_pNext;
 87         }
 88     }
 89     while(p1)
 90     {
 91         p->m_pNext=p1;
 92         p=p->m_pNext;
 93         p1=p1->m_pNext;
 94     }
 95     while(p2)
 96     {
 97         p->m_pNext=p2;
 98         p=p->m_pNext;
 99         p2=p2->m_pNext;
100     }
101     return newHead;
102 }
103 
104 ListNode* Merge2(ListNode* head1,ListNode* head2)
105 {
106     if(!head1&&!head2) return NULL;
107     if(!head1&&head2) return head2;
108     if(head1&&!head2) return head1;
109     ListNode* newHead=NULL;
110     if(head1->m_nValue<head2->m_nValue)
111     {
112         newHead=head1;
113         newHead->m_pNext=Merge2(head1->m_pNext,head2);
114     }
115     else
116     {
117         newHead=head2;
118         newHead->m_pNext=Merge2(head1,head2->m_pNext);
119     }
120     return newHead;
121 }
View Code

 

 

面试题18:树的子结构
    输入两棵二叉树,判断B是不是A的子结构。二叉树结点的定义如下:

'

 

思路:  
    分两步走:
    第一步,寻找相同的根结点。
    第二步,根结点相同,递归判断左右子树。
    这两部都用到了递归的思想。
 1 #include <iostream>
 2 using namespace std;
 3 struct Node
 4 {
 5     int m_nValue;
 6     Node* m_pLeft;
 7     Node* m_pRight;
 8 };
 9 void CreateTree(Node*& t);
10 void Print(Node* t);
11 bool SameRoot(Node* t1,Node* t2);
12 bool If_Child(Node* t1,Node* t2);
13 int main()
14 {
15     Node* root1=NULL,*root2=NULL;
16     CreateTree(root1);
17     CreateTree(root2);
18     bool f=SameRoot(root1,root2);
19     if(f) cout<<"Y"<<endl;
20     else cout<<"N"<<endl;
21     return 0;
22 }
23 void CreateTree(Node*& t)
24 {
25     int val;
26     cin>>val;
27     if(val==0)
28         t=NULL;
29     else
30     {
31         t=new Node();
32         t->m_nValue=val;
33         t->m_pLeft=NULL;
34         t->m_pRight=NULL;
35         CreateTree(t->m_pLeft);
36         CreateTree(t->m_pRight);
37     }
38 }
39 void Print(Node* t)
40 {
41     if(t==NULL)
42         return;
43     cout<<t->m_nValue<<" ";
44     Print(t->m_pLeft);
45     Print(t->m_pRight);
46 }
47 bool SameRoot(Node* t1,Node* t2)
48 {
49     bool flag=false;
50     if(t1->m_nValue==t2->m_nValue)
51         flag=If_Child(t1,t2);
52 //    else
53 //        flag=SameRoot(t1->m_pLeft,t2)||SameRoot(t1->m_pRight,t2);//这样写是不对的。。。
54     if(!flag)
55         flag=SameRoot(t1->m_pLeft,t2);
56     if(!flag)
57         flag=SameRoot(t1->m_pRight,t2);
58     return flag;
59 }
60 bool If_Child(Node* t1,Node* t2)
61 {
62     if(!t2)
63         return true;
64     if(!t1||t1->m_nValue!=t2->m_nValue)
65         return false;
66     return If_Child(t1->m_pLeft,t2->m_pLeft)&&If_Child(t1->m_pRight,t2->m_pRight);
67 }
View Code

 

 

posted on 2015-06-26 21:36  VisualTracker  阅读(159)  评论(0编辑  收藏  举报