高质量的代码

double类型的比较不能用==

计算机表示的小数(包括float & double)都有误差,如果两个小数的差的绝对值很小,小雨0.0000001(中间为6个0)就可以认为他们相等

完整的代码:功能测试,边界测试,负面测试

考虑题目是否会出现大数问题,超出int的可以用long long
负面测试是指输入的数据是非法的错误的
 
3种错误处理方法:


1.不同的返回值表示不同的错误

2.
3.抛出异常时,程序的执行会打乱正常的顺序,对程序的性能有很大的影响。
 


 

面试题11:数值的整数次方

 注意各种输入,base为0.0,exponent为负数时是错误输入

 exponent为负数,取绝对值,求幂后倒数,

 注意浮点数的比较相等用差值和0.0000001

 求幂用递归,a的n次方分奇偶考虑,偶数的时候就用a的n/2的平方

 除以2的时候用位移,体现细节..

面试题12:打印1到最大n位数

n位数可能会超出long long的范围,long long 为8字节64位

-->用字符串模拟数字的加法 

复制代码
  1  
  2  /*n位数可能会超出long long的范围,long long 为8字节64位
  3      -->用字符串模拟数字的加法*/
  4  #include <memory>
  5  #include <string.h>
  6  //C++ 中要使用strlen一般包含<CString>
  7  
  8  //一直进位直到第一次结束才是一个+1过程的结束
  9  bool Increment(char* number)
 10  {
 11      int length = strlen(number);
 12      int takeOver = 0;
 13      bool isOverStack = false;
 14      
 15      for(int i = length - 1; i >= 0; ++i)
 16      {
 17          int sum = number[i] - '0' + takeOver;
 18          if(i == length - 1)
 19              ++sum;
 20  
 21          if(sum >= 10)
 22          {
 23              if(i == 0)
 24                  isOverStack = true;
 25              else
 26              {
 27                  sum -= 10;
 28                  number[i] = sum + '0';
 29                  takeOver = 1;
 30              }
 31          }
 32          else
 33          {
 34              number[i] = sum + '0';
 35              break;
 36          }
 37  
 38      }
 39      return isOverStack;
 40  }
 41  
 42  //不要打印出最前面的0
 43  void PrintNumber(char* number)
 44  {
 45      int length = strlen(number);
 46      bool posFlag = false;
 47  
 48      for(int i = 0; i < length; ++i)
 49      {
 50          if(!posFlag && number[i] != '0')
 51              posFlag = true;
 52          if(posFlag)
 53              printf("%c", number[i]);
 54      }
 55      return;
 56  }
 57  
 58  void PrintToMaxOfNDigits(int n)
 59  {
 60      if(n <= 0)
 61          return;
 62      char *number = new char[n + 1];
 63      memset(number, '0', n);
 64      number[n] = '';
 65  
 66      while(!Increment(number))
 67          PrintNumber(number);
 68      delete []number;
 69  }
 70  
 71  //把问题转换为数字排列的方法,运用递归是程序更加简单
 72  //全排列方法
 73  
 74  void PrintToMaxOfNDigits_1(int n)
 75  {
 76      char* number = new char[n + 1];
 77      memset(number, '0', n);
 78      number[n] = '';
 79      for(int i = 0; i < 10; ++i)
 80      {
 81          number[0] = '0' + i;
 82          PrintRecursively(number, n, 0)
 83      } 
 84      delete []number;
 85  }
 86  
 87  void PrintRecursively(char* number, int length, int index)
 88  {
 89      if(index == length - 1)
 90      {
 91          PrintNumber(number);
 92          return;
 93      }
 94  
 95      for(int i = 0; i < 10; ++i)
 96      {
 97          number[index + 1] = '0' + i;
 98          PrintRecursively(number, length, index + 1);
 99      }
100  }
复制代码

 面试题13:在O(1)时间内删除链表节点

把下一个节点的内容复制到需要删除的节点上覆盖原有的内容,再把下一个节点删除即可。

若删除的节点位于列表尾部则顺序遍历得到该节点的前序节点,并完成删除操作。

如果链表中只有一个节点,则在删除后还需要把链表的头结点设置为NULL。

//题目已经假设删除的节点一定在链表中

//节点delete之后,要将本身设为NULL,position本身就是一个节点指针,是有指向的

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 
 4 typedef struct ListNode* head;
 5 typedef struct ListNode* position;
 6 struct ListNode
 7 {
 8     int m_nValue;
 9     ListNode* m_pNext;
10 };
11 
12 void DeleteNode(head pListHead, position pToBeDeleted)
13 {
14     if(!pListHead || !pToBeDeleted)
15         return;
16     position pTemp;
17     if(pToBeDeleted->m_pNext == NULL)
18     {
19         pTemp = pListHead;
20         while(pTemp->m_pNext != pToBeDeleted)
21             pTemp = pTemp->m_pNext;
22         pTemp->m_pNext = pToBeDeleted->m_pNext;
23         delete pToBeDeleted;
24         //注意下面一行
25         pToBeDeleted = NULL;
26         return;
27     }
28     pTemp = pToBeDeleted->m_pNext;
29     pToBeDeleted->m_nValue = pTemp->m_nValue;
30     pToBeDeleted->m_pNext = pTemp->m_pNext;
31     delete pTemp;
32     //注意下面一句
33     pTemp = NULL;
34     return;
35 }
复制代码

面试题14:调整数组顺序使奇数位位于偶数之前

题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。要求时间复杂度为O(n)。

用和快排比较相似的算法,维护两个指针

通常情况下位运算符比%要快一些,所以判断奇偶可以用data & 0X1 比较好

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 void ReorderOddEven(int *number, unsigned int length)
 4 {
 5     //函数初始时基本的判断参数输入有没有错误的不要忘了!!!
 6     if(number == NULL || length == 0)
 7         return;
 8 
 9     int *p1 = number, *p2 = number + (length -1);
10     while(p1 < p2)
11     {
12         while(p1 < p2 && (*p1 & 0x1) == 1)
13             ++p1;
14         while(p1 < p2 && (*p2 & 0x1) == 0)
15             --p2;
16         if(p1 < p2)
17         {
18             int temp = *p1;
19             *p1 = *p2;
20             *p2 = temp;
21         }
22         
23     }
24     return;
25 }
复制代码

可以利用函数指针把这个算法扩展

复制代码
 1 void Reorder(int* number, unsigned int length, bool (*func)(int))
 2 {
 3     //函数初始时基本的判断参数输入有没有错误的不要忘了!!!
 4     if(number == NULL || length == 0)
 5         return;
 6 
 7     int *p1 = number, *p2 = number + (length -1);
 8     while(p1 < p2)
 9     {
10         while(p1 < p2 && !func(*p1))
11             ++p1;
12         while(p1 < p2 && func(*p2))
13             --p2;
14         if(p1 < p2)
15         {
16             int temp = *p1;
17             *p1 = *p2;
18             *p2 = temp;
19         }
20 
21     }
22     return;
23 }
24 
25 bool isEven(int n)
26 {
27     return (n & 0x1) == 0;
28 }
29 void ReorderOddEven2(int* number, unsigned int length)
30 {
31     Reorder(number, length, isEven);
32 }
复制代码

代码的鲁棒性:

容错性是代码鲁棒性的一个重要体现

1.输入错误的用户名

2.试图打开不存在的文件

3.网络不能连接

4......

-->预防性编程

面试题15:链表中倒数第k个结点

题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。链表结点定义如下:

记尾节点是倒数第一个节点,

用unsigned int 来表示length比较合理,此时0 - 1得到一个巨大的数

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 typedef struct ListNode* phead;
 4 typedef struct ListNode* position;
 5 struct ListNode
 6 {
 7     int m_nValue;
 8     ListNode* m_pNext;
 9 };
10 
11 position FindKthNode(phead listHead, unsigned int k)
12 {
13     if(listHead == NULL || k == 0)
14         return NULL;
15     position temp1 = listHead, temp2 = listHead;
16 
17     for(unsigned int i = 0; i < k - 1; ++i)
18     {
19         if(temp1->m_pNext != NULL)
20             temp1 = temp1->m_pNext;
21         else
22             return NULL;
23     }
24     while(temp1->m_pNext != NULL)
25     {
26         temp1 = temp1->m_pNext;
27         temp2 = temp2->m_pNext;
28     }
29     return temp2;
30 }
复制代码

要注意K=0,listHead指向空,list长度小于K

1.求链表的中间节点:

  若链表总数为奇数,返回中间节点,否则返回中间节点2个中的任意一个

  定义两个指针都从链表头结点出发,一个一次走一步,一个一次走两步,走的快的到末尾时,走的啊,慢的刚好在链表的中间

2.判断一个链表是否构成环形;

  同上,定义两个指针都从链表头结点出发,一个一次走一步,一个一次走两步,走的快如果能追上走的慢的那么就是环形链表,如果到链表末尾还没有追上走的慢的则不是环形链表

 

当我们使用一个指针遍历链表不能解决问题的时候,可以尝试用两个指针来遍历,两个指针遍历的速度或者先后不同。

 

面试题16:反转链表

输入的链表指针为NULL或者整个链表只有一个节点

反转后头结点是不是链表的原先的尾节点

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 typedef struct ListNode* pHead;
 4 typedef struct ListNode* position;
 5 
 6 struct ListNode
 7 {
 8     int m_nValue;
 9     ListNode* m_pNext;
10 };
11 
12 pHead ReverseList(pHead listHead)
13 {
14     pHead pReversedHead = NULL;
15     position pNode = listHead;
16     position pPrev = NULL;
17 
18     //这种形式中头结点不是哑元,反转后头结点为的下一个为NULL
19     //所以初始的pPrev设为NULL
20     
21     while(pNode != NULL)
22     {
23         position pNext = pNode->m_pNext;
24         if(pNext == NULL)
25             pReversedHead = pNode;
26 
27         pNode->m_pNext = pPrev;
28         pPrev = pNode;
29         pNode = pNext;
30     }
31     return pReversedHead;
32 }
复制代码

递归实现注意头结点的6,7,8行是最后实现的

复制代码
1 Node * resverselinkRecursion(Node *head) 
2 {
3  if(head==NULL || head->next==NULL)
4   return head;
5  Node *n = resverselinkRecursion(head->next);
6  head->next->next = head;
7  head->next=NULL;
8  return n;
9 }
复制代码

 面试题18:树的子结构

判断二叉树B是不是A的子结构

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 
 4 typedef struct BinaryTreeNode* position;
 5 typedef struct BinaryTreeNode* root;
 6 struct BinaryTreeNode
 7 {
 8     int            m_nValue;
 9     position    m_pLeft;
10     position    m_pRight;
11 };
12 
13 bool HasSubTree(root pRoot1, root pRoot2)
14 {
15     bool result = false;
16     if(pRoot1 != NULL && pRoot2 != NULL)
17     {
18         if(pRoot1->m_nValue == pRoot2->m_nValue)
19             result = DoesTree1HaveTree2(pRoot1, pRoot2);
20         if(!result)
21             result = HasSubTree(pRoot1->m_pLeft, pRoot2);
22         if(!result)
23             result = HasSubTree(pRoot1->m_pRight, pRoot2);
24     }
25     return result;
26 }
27 
28 bool DoesTree1HaveTree2(root pRoot1, root pRoot2)
29 {
30     if(pRoot2 == NULL)
31         return true;
32     if(pRoot1 == NULL)
33         return false;
34     if(pRoot1->m_nValue != pRoot2->m_nValue)
35         return false;
36     return DoesTree1HaveTree2(pRoot1->m_pLeft, pRoot2->m_pLeft) &&
37         DoesTree1HaveTree2(pRoot1->m_pRight, pRoot2->m_pRight);
38 }
复制代码

树的操作都伴随着大量的指针,尤其要考虑清楚是否为NULL的情况。

原文地址:https://www.cnblogs.com/zsq1993/p/5985181.html