首页 > IT职业 > IT职场 > 面试->代码 剑指offer(一)
代码鲁棒性 (输入参数检查等) 贯穿所有代码当中,不会特别强调.
注意:
1. 返回值类型为该类型引用(链式赋值),返回*this
2. 传入参数常量引用
3. 释放自身已有空间
4. 判断是不是自身赋值
例:
[cpp] view plain copy
MyString& MyString::operator=( const MyString& str ){
if ( this == &str )
return *this;
delete[] m_pData;
m_pData = NULL:
m_pData = new char[strlen(str.m_pData) + 1];
strcpy( m_pData, str.m_pData );
return *this;
}
不足:若内存不足,new 申请内存时抛出异常,则 m_pData 已经被 delete,此时其为一个空指针,非常容易导致程序奔溃。在赋值运算符函数内部抛异常,CMyString 实例不再保持有效的状态,违背异常安全性原则.
改进:1. 先 new 再 delete 2. 创建临时实例,交换临时实例和原来的实例:
[cpp] view plain copy
MyString& MyString::operator=( const MyString& str ){
if ( &str != this ){
MyString strTemp( str );
char* pTemp = strTemp.m_pData;
strTemp.m_pData = m_pData;
m_pData = pTemp;
}
return *this;
}
3. 二维数组中的查找:二维数组,每一行,从左到右递增。每一列,从上到下递增。输入一个二维数组和一个整数,判断数组中是否含有该整数
思路:选取数组右上角数字,等于->结束。大于->剔除这一列。小于->剔除这一行。这样每比较一次剔除一列或一行,缩小范围,直到找到要查找的数字 or 查找范围为空
例:
[cpp] view plain copy
bool Find( int* matrix, int rows, int cols, int number ){
bool found = false;
if ( NULL != matrix && rows > 0 && cols > 0 ){
int row = 0;
int col = cols - 1;
while ( row = 0 ){
if ( number == matrix[row * cols + col] ){
found = true;
return found;
}
else if ( matrix[row * cols + col] > number )
--col;
else
++row;
}
}
return found;
}
二维数组在内存中占据连续的空间。在内存中从上到下存储各行元素,每行从左到右顺序存储
4. 替换空格
1. 在原串上操作 --> 保证原串后面有足够的空间 --> 从后往前替换
两种思路都需遍历原串一次获取空格个数 --> O(N) 相关题目:两个排序数组 A1,A2。A1 后有足够空间容纳 A2,将 A2 中数字插入 A1 中使所有数字依旧有序 思路:遍历两个数组各一次得到两数组长度,三个指针,一个指针指向 lengthA1+lengthA2-1,其余两个分别指向 A1 与 A2 末尾。依次比较,将合适数字放到第一个指针所指位置 合并两个数组 (字符串) 时,如果从前往后赋值每个数字 or 字符需要重复移动数字 or 字符多次 --> 从后往前复制,减少移动次数,提高效率 5. 从尾到头打印链表 1. 栈 2. 递归 7. 用两个栈实现队列 思路:栈 S1 和 S2,入「队列」入栈 S1。出「队列」,若 S2 有元素 S2.pop( ),否则,将 S1 中元素压入栈 S2 相关题目:两个队列实现栈 8. 旋转数组中最小的数字 --> 把数组最开始的若干个元素搬到数组的末尾(旋转数组),输入一个递增数组的旋转,输出最小数字 思路:数组旋转后可将其视为两个排序的子数组,并且前面子数组元素都大于等于后面子数组元素,最小的元素是第二个子数组的第一个元素 (我们要找最小的元素) --> 二分 (O(logn))(每移动一次指针,查找范围缩小为原来的一半) --> p1 指向数组第一个元素,p2 指向数组最后一个元素。如果中间元素大于 p1 指向数据,即中间元素在第一个子数组中,将 p1 指向这。否则如果 中间元素小于 p2 指向元素,将 p2 指向中间元素。两个指针慢慢靠近 直到 第一个指针指向第一个子数组最后一个元素,第二个指针指向第二个子数组第一个元素,两指针相邻,此时第二个指针指向数组中最小的元素。循环结束 特殊情况: 1. 如果移动元素个数为 0,此时第一个元素就是最小的元素,代码中我们将 indexMid 初始化为 index1,如果第一个数字小于最后一个数字,直接返回。 2. {1, 0, 1, 1, 1} 与 {1, 1, 1, 0, 1} 这种 两个指针指向的数字及它们中间数字三者相同的时候,无法判断中间数字位于哪个子数组中,也就无法移动指针来缩小查找的范围,此时 --> 顺序查找: 9. 斐波那契数列: n f(n) 0 0 1 1 >1 f(n-1)+fr(n-2) 1. 递归 2. 循环: 相关题目: 1. 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法? n = 1, 1 种方法 (一次 1 级台阶) n = 2, 2 种方法 ( 两次 1 级,或者一次 2 级 ) 前两种为基准情况 n = 3, 3( 三次 1 级,或一次 1 级一次 2 级 (顺序不同,两个方法) ) 我们通过 台阶数为 3 来分析。青蛙一次只能跳一级或者两级 也就是说: 青蛙跳上三级台阶最后一跳只有两种情况,跳一级或者跳两级,所以青蛙跳三级台阶总的方法数位: 青蛙跳至只剩一级台阶和只剩两级台阶的方法数之和 即 F(n) = F(n-1) + F(n-2) 或者,我们多写几种情况,也可以发现规律,方法数为 前一次方法数 + 后一次方法数: 3. 我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,总共有多少种方法? 思路: 对于 n>=3 的情况,不管前面矩形是怎样覆盖的。我们只考虑最后一次怎么覆盖。 最后一次只有两种覆盖方式:1. 用一个小矩形竖着覆盖。2. 用两个小矩形横着覆盖。 所以总的方法数无外乎 --> 你用各种方法覆盖到只剩 1 个再竖着覆盖或者你用各种方法覆盖到只剩两个再横着覆盖 即:总的方法数 F(n) = n-1 次的方法数 F(n-1)(接着用一个小矩形竖着覆盖) + n-2 次的方法数 F(n-2)(接着用两个小矩形横着覆盖) : 10. 二进制中 1 的个数 输入一个整数,输出该整数二进制表示中 1 的个数 思路:一个整数减去 1 再和原整数做与行算,会把该整数二进制形式最右边一个 1 变为 0. 那么二进制表示有多少个 1 就可以进行多少次这样的操作。 : 常规思路:将 1 依次左移判断数字每一位是否是 1(注意不能将数字右移 与 1 进行&运算,因为负数右移补符号位,数字最终变为 0XFFFFFFFF,死循环) : 相关题目: 1. 用一条语句判断一个整数是不是 2 的整数次方 思路:如果一个整数是 2 的整数次方,则它二进制表示中只有一位为 1,把这个整数减去 1 和它自己做与运算,这个整数中唯一的 1 就会变为 0 2.输入两个整数 m,n。计算需要改变 m 的二进制表示中多少位才能得到 n 思路:第一步 将 m 与 n 异或,第二部求异或结果中 1 的位数 13. 在 O(1) 时间删除链表结点 思路:1. 如果链表只有一个结点 (头节点),直接删除 2. 如果要删除结点为尾结点,则用常规方法 (O(N)) 3. 删除多个节点的非尾结点 (将下一个节点 value 值赋给被删结点,删除下个节点) 14. 调整数组顺序使奇数位于偶数前面 两个指针 p1,p2,一个从前往后遍历,一个从后往前遍历 : 15. 链表倒数第 k 个结点 想象有一把长度为 k 的尺子放在链表上,两个指针 p1(尺子头),p2(尺子尾)。当 p2 到达链表尾部时 p1 正好指向倒数第 k 个结点 注意这个题重点考虑鲁棒性 相关题目: 1. 求链表中间结点 思路:两个指针,p1,p2。p1 一次走一步,p2 一次走两步。当 p2 指向链表最后一个结点时,p1 正好指向链表中间节点 2. 判断链表是否带环 思路:快慢指针,相遇->带环。快指针到链表尾->不带环 当我们用一个指针遍历链表不能解决问题时,可以尝试两个指针来遍历链表。可以让其中一个指针走的快些 (如一次走两步),或者让它先在链表上走若干步 16. 反转链表 : 17. 合并两个排序的链表 : 21. 包含 min 函数的栈 定义一个可以使 min, push, pop 都为 O(1) 的栈 思路:借用辅助栈,每次数据栈元素入栈时,将所有入栈元素中最小值入辅助栈 (保证辅助栈和数据栈->同入同出) : 23. 从上往下打印二叉树 (层序遍历二叉树) 借助队列 : 29. 数组中出现次数超过一半的数字 思路:如果有一个数字出现次数超过数组元素总个数的一半,则它出现次数比其他数字出现的次数总和还要多。 : 37. 两个链表的第一个公共结点 思路: 1. 借助栈,两个链表都从后往前遍历,直到找到最后一个相同结点 2. 遍历两个链表一次,得到链表长度,len1,len2。假设 len1 为 5,len2 为 4。则在链表 1 上先走 (len1 - len2) 步,接着同时开始走,第一个相同节点 38. 数字在排序数组中出现的次数 思路: 因为是排序数组,找到第一个 k 和最后一个 k 即可知道 k 出现的次数 O(log n) 二分 : 39. 二叉树的深度 : 相关题目:判断一棵树是不是平衡二叉树。左右子树的深度相差不超过 1. 1. 需要重复遍历多次的解法 : 后序遍历,每个节点只遍历一次 : 42. 翻转单词顺序 vs 左旋转字符串 输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。标点符号和普通字母一样处理。--> "I am a student." 变为 "student. a am I" 思路: 1. 翻转句子中所有的字符。此时不但翻转了句子中单词的顺序,连单词内的字符的顺序也翻转了 2. 翻转每个单词中字符的顺序 :[cpp] view plain copy
int Min( int* arr, int len ){
if (NULL == arr || len throw new std::exception( "Invalid parameters" );
int index1 = 0;
int index2 = len - 1;
int indexMid = index1;
while (arr[index1] >= arr[index2]){
if (1 == index2 - index1)
return arr[index2];
indexMid = (index1 + index2) / 2;
// 顺序查找
if (arr[index1] == arr[index2] && arr[index1] == arr[indexMid])
return MinInOrder( arr, index1, index2 );
if (arr[indexMid] >= arr[index1])
index1 = indexMid;
else if (arr[indexMid] index2 = indexMid;
}
}
int MinInOrder( int* arr, int index1, int index2 ){
int result = arr[index1];
for (int i = index1 + 1; i if (arr[i] result = arr[i];
return result;
}[cpp] view plain copy
long long Fibonacci( unsigned n ){
int result[2] = {0, 1};
if (n return result[n];
long long fibOne = 0;
long long fibTwo = 1;
long long fibN = 0;
// 注意这里是 i for (unsigned int i = 2; i fibN = fibOne + fibTwo;
fibOne = fibTwo;
fibTwo = fibN;
}
return fibN;
}[cpp] view plain copy
int jumpFloor( int number ) {
if (number return 0;
else if (1 == number)
return 1;
else if (2 == number)
return 2;
return jumpFloor( number-2 ) + jumpFloor( number-1 );
}[cpp] view plain copy
int rectCover( int number ) {
if ( number return 0;
if ( 1 == number )
return 1;
if ( 2 == number )
return 2;
return rectCover( number-1 ) + rectCover( number-2 );
}[cpp] view plain copy
int NumberOf1( int n ){
int count = 0;
while (0 != n){
++count;
n = (n - 1) & n;
}
return count;
}[cpp] view plain copy
int NumberOf1( int n ){
int count = 0;
unsigned int flag = 1;
while (0 != flag){
if (0 != (flag & n))
++count;
flag = flag }
return count;
}[cpp] view plain copy
void Reorder( int* arr, unsigned int len, bool (*func)(int) ){
if (NULL == arr || len == 0)
return;
int* pBegin = arr;
int* pEnd = arr + len - 1;
while (pBegin while (pBegin ++pBegin;
while (pBegin --pEnd;
if (pBegin int temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
}
}
}
bool IsEven( int n ){
return 0 == ( n & 1 );
}
void ReorderOddEven( int* arr, unsigned int len ){
Reorder( arr, len, IsEven );
}[cpp] view plain copy
ListNode* ReverseList( ListNode* pHead )
{
// 链表为空和链表只有一个结点在这个语句进行处理
if (NULL == pHead || NULL == pHead->next)
return pHead;
ListNode* pPrev = NULL;
ListNode* pCur = pHead;
ListNode* pReverseHead = NULL;
while (NULL != pCur){
ListNode* pNext = pCur->next;
if (NULL == pNext)
pReverseHead = pCur;
pCur->next = pPrev;
pPrev = pCur;
pCur = pNext;
}
return pReverseHead;
}[cpp] view plain copy
ListNode* Merge( ListNode* pHead1, ListNoe* pHead2 ) {
if (NULL == pHead1)
return pHead2;
else if (NULL == pHead2)
return pHead1;
ListNode* pMergedHead = NULL;
if (pHead1->val val){
pMergeHead = pHead1;
pMergeHead->next = Merge( pHead1->next, pHead2 );
}
else{
pMergeHead = pHead2;
pMergeHead->next = Merge( pHead1, pHead2->next );
}
return pMergedHead;
}[cpp] view plain copy
void Push( const int& data ){
dataS.push( data );
if (0 == minS.size( ) || data minS.push( data );
else
minS.push( minS.top( ) );
}
void Pop( ){
assert( dataS.size( ) > 0 && minS.size( ) > 0 );
dataS.pop( );
minS.pop( );
}
const T& Min( ) const {
assert( dataS.size( ) > 0 && minS.size( ) > 0 );
return minS.top( );
}[cpp] view plain copy
void LevelPrint( Node* pRoot ){
if (NULL == pRtoot)
return;
queue
queueNode.push( pRoot );
while (0 != queueNode.size( )) {
Node* pNode = queueNode.front( );
queueNode.pop( );
Visit( pNode );
if (NULL != pNode->pLeft)
queueNode.push(pNode->pLeft);
if (NULL != pNode->pRight)
queueNode.push(pNode->pRight);
}
}[cpp] view plain copy
bool g_inputInvalid = false;
bool CheckInvalidArr( int* arr, int len ){
g_inputInvalid = false;
if (NULL == arr || len g_inputInvalid = true;
return g_inputInvalid;
}
bool CheckMoreThanHalf( int* arr, int len, int number ){
int times = 0;
for (int i = 0; i if (number == arr[i])
++times;
bool isMoreThanHalf = true;
if (times * 2 g_inputInvalid = true;
isMoreThanHalf = false;
}
return isMoreThanHalf;
}
int MoreThanHalfNum( int* arr, int len ){
// 如果只返回0存在二义性,是无效参数返回0还是0出现次数超过一半呢?
// 定义一个函数 和 一个全局变量,即可区分这两种情况
if (CheckInvalidArray( arr, len ))
return 0;
int result = arr[0];
int times = 1;
for (int i = 1; i if (0 == times){
result = arr[i];
times = 1;
}
else if (result == arr[i])
++times;
else
--times;
}
// 此时result中保存的数字并不一定是数组中出现次数超过数组长度一半的数字,需要验证
// 11234
if (!CheckMoreThanHalf( arr, len, result ))
result = 0;
return result;
}[cpp] view plain copy
int GetFirstK( int* arr, int len, int k, int start, int end ){
// 这段区间内没找到k, 返回-1
if (start > end)
return -1;
int midIndex = (start + end) / 2;
int midData = arr[midIndex];
if (k == midData){
if ((midIndex > 0 && k != arr[midIndex - 1])
|| 0 == midIndex)
return midIndex;
else
end = midIndex - 1;
}
else if (midData > k)
end = midIndex - 1;
else
start = midIndex + 1;
return GetFirstK( arr, len, k, start, end );
}
int GetLastK( int* arr, int len, int k, int start, int end ){
if (start > end)
return -1;
int midIndex = (start + end) / 2;
int midData = arr[midIndex];
if (k == midData){
if ((midIndex || len - 1 == midIndex)
return midIndex;
else
start = midIndex + 1;
}
else if (midData > k)
end = midIndex - 1;
else
start = midIndex + 1;
return GetLastK( arr, len, k, start, end );
}
int GetTimesOfK( int* arr, int len, int k ){
int times = 0;
if (NULL != arr && len > 0 ){
int first = GetFirstK( arr, len, k, 0, len - 1 );
int last = GetLastK( arr, len, k, 0, len - 1 );
if (first > -1 && last > -1)
times = last - first + 1;
}
return times;
}[cpp] view plain copy
int TreeDepth( BinaryTreeNode* pRoot ){
if (NULL == pRoot)
return 0;
int depthLeft = TreeDepth( pRoot->left );
int depthRight = TreeDepth( pRoot->right );
return (depthleft > depthRight) ? (depthLeft + 1) : (depthRight + 1);
}[cpp] view plain copy
bool IsBalanced( BinaryTreeNode* pRoot ){
if (NULL == pRoot)
return true;
int depthLeft = TreeDepth( pRoot->left );
int depthRight = TreeDepth( pRoot->right );
int diff = left - right;
if (diff > 1 || diff return false;
return IsBalanced( pRoot->left ) && IsBalanced( pRoot->right );
}[cpp] view plain copy
bool IsBalanced( BinaryTreeNode* pRoot, int* pDepth ){
if (NULL == pRoot){
*pDepth = 0;
return true;
}
int left;
int right;
if (IsBalanced( pRoot->left, &left ) && IsBalanced( pRoot->tight, &right )){
int diff = left - right;
if (diff >= -1 && diff *pDepth = left > right ? (left + 1) : (right + 1);
return true;
}
}
return false;
}
bool IsBlanced( BinaryTreeNode* pRoot ){
int depth = 0;
return IsBlanced( pRoot, &depth );
}[cpp] view plain copy
void Reverse( char* pBegin, char* pEnd ){
if (NULL == pBegin || NULL == pEnd)
return;
while (pBegin char temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
++pBegin;
--pEnd;
}
}
char* ReverseSentence( char* pData ){
if (NULL == pData)
return NULL;
char* pBegin = pData;
char* pEnd = pData;
while ('
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/3127/viewspace-2800538/ ,如需转载,请注明出处,否则将追究法律责任。