高质量的代码
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的情况。

浙公网安备 33010602011771号