高质量的代码
double类型的比较不能用==
计算机表示的小数(包括float & double)都有误差,如果两个小数的差的绝对值很小,小雨0.0000001(中间为6个0)就可以认为他们相等
完整的代码:功能测试,边界测试,负面测试
1.不同的返回值表示不同的错误
面试题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] = '\0'; 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] = '\0'; 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的情况。