代码鲁棒性 (输入参数检查等) 贯穿所有代码当中,不会特别强调.
注意:
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 < rows && col >= 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. 翻转每个单词中字符的顺序 : 相关题目:字符串左旋/右旋是把字符串前面的若干个字符转移到字符串的尾部。「abcdefg」和 数字 2 --> "cdefgab" 思路:将要左旋的字符串和剩余字符串分别翻转,再翻转整个字符串 : 50. 树中两个结点的最低公共祖先 1. 二叉搜索树 : 2. 普通树,含指向父节点指针 : 56. 链表中环的入口结点 思路:定义指针 P1,P2。P1 在链表上走环大小步后 P2 开始以相同速度遍历链表,两个指针相遇点即为链表中环的入口结点 : -未完待续[cpp] view plain copy
int Min( int* arr, int len ){
if (NULL == arr || len <= 0)
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] <= arr[index2])
index2 = indexMid;
}
}
int MinInOrder( int* arr, int index1, int index2 ){
int result = arr[index1];
for (int i = index1 + 1; i <= index2; ++i)
if (arr[i] < result)
result = arr[i];
return result;
}[cpp] view plain copy
long long Fibonacci( unsigned n ){
int result[2] = {0, 1};
if (n < 2)
return result[n];
long long fibOne = 0;
long long fibTwo = 1;
long long fibN = 0;
// 注意这里是 i<=n, 而不是i<n
for (unsigned int i = 2; i <= n; ++i){
fibN = fibOne + fibTwo;
fibOne = fibTwo;
fibTwo = fibN;
}
return fibN;
}[cpp] view plain copy
int jumpFloor( int number ) {
if (number <= 0)
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 <= 0 )
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 << 1;
}
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 < pEnd){
while (pBegin < pEnd && !func(*pBegin))
++pBegin;
while (pBegin < pEnd && func(*pEnd))
--pEnd;
if (pBegin < pEnd){
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 < pHead2->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.top( ))
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<Node*> queueNode;
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 <= 0)
g_inputInvalid = true;
return g_inputInvalid;
}
bool CheckMoreThanHalf( int* arr, int len, int number ){
int times = 0;
for (int i = 0; i < len; ++i)
if (number == arr[i])
++times;
bool isMoreThanHalf = true;
if (times * 2 <= len){
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 < len; ++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 && k != arr[midIndex + 1])
|| 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 < -1)
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 <= 1){
*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 < pEnd){
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 ('\0' != *pEnd)
++pEnd;
--pEnd;
// 翻转整个句子
Reverse( pBegin, pEnd );
// 翻转句子中每个单词
pBegin = pEnd = pData;
while ('\0' != *pBegin){
if (' ' == *pBegin){
++pBegin;
++pEnd;
}
else if (' ' == *pEnd || '\0' == *pEnd){
Reverse( pBegin, --pEnd );
pBegin = ++pEnd;
}
else
++pEnd;
}
return pData;
}[cpp] view plain copy
char* LeftRotateString( char* pStr, int n ){
if (NULL != pStr){
int nLength = strlen( pStr );
if (nLength > 0 && n > 0 && n < nLength){
char* pFirstStart = pStr;
char* pFirstEnd = pStr + n - 1;
char* pSecondStart = pStr + n;
char* pSecondEnd = pStr + nLength - 1;
Reverse( pFirstStart, pFirstEnd );
Reverse( pSecondStart, pSecondEnd );
Reverse( pFirstStart, pSecondEnd );
}
}
}[cpp] view plain copy
BSTNode* GetLastCommonNode( BSTNode* pRoot, BSTNode* pNode1, BSTNode* pNode2 ){
if (NULL == pRoot)
return NULL;
else if (NULL == pNode1)
return pNode2;
else if (NULL == pNode2)
return pNode1;
else if (pNode1 == pNode2)
return pNode1;
int val = pRoot->val;
int val1 = pNode1->val;
int val2 = pNode2->val;
if (val1 < val && val2 < val)
// 注意,递归时,有返回值,调用函数一定要加 return。 无返回值则可以只递归调用,做你想做的事情, 不用加return
return GetLastCommonNode( pRoot->left, pNode1, pNode2 );
else if (val1 > val && val2 > val)
return GetLastCommonNode( pRoot->right, pNode1, pNode2 );
else{
if (val1 == val)
return pNode1;
else if (val2 == va1)
return pNode2;
else
return pRoot;
}
}[cpp] view plain copy
BTreeNode* FindFirstCommonNode( TreeNode* pNode1, TreeNode* pNode2 ){
if (NULL == pNode1)
return pNode2;
else if (NULL == pNode2)
return pNode1;
stack<TreeNode*> s1;
stack<TreeNode*> s2;
// 注意:入栈第一个结点分别是这两个结点,而不是它们的父结点
while (NULL != pNode1){
s1.push( pNode1 );
pNode1 = pNode1->parent;
}
while (NULL != pNode2){
s2.push( pNode2 );
pNode2 = pNode2->parent;
}
TreeNode* firstCommonNode = NULL;
// 如果两个结点没在同一颗二叉树中, 返回NULL
if (s1.top( ) != s2.top( ))
return NULL;
while (0 != s1.size( ) && 0 != s2.size( )){
if (s1.top( ) == s2.top( )){
firstCommonNode = s1.top( );
s1.pop( );
s2.pop( );
}
else
break;
}
return firstCommonNode;
}[cpp] view plain copy
// 首先判断单链表是否带环,若带环(此时快慢指针已经相遇),从此刻起计步,直到下次两个指针再相遇,快指针步数减去慢指针步数,即为环的长度
ListNode* EntryNodeOfLoop( ListNode* pHead )
{
if (NULL == pHead)
return NULL;
// 定义快慢指针判断是否有环
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
// 如果有环,通过这两个变量求得环长度
int fastLength = 0;
int slowLength = 0;
int loopLength = 0;
// 求出环长度后通过这两个结点求出环入口节点
ListNode* p1 = pHead;
ListNode* p2 = pHead;
// 设置个死循环,如果链表不带环,我们在循环里直接退出即可
while ( 1 ){
// 在两个指针走之前必须判断pFast和pFast的next是否为空,两个条件都必须判断而且判断顺序不能出错
// 因为快指针走得快,我们只需要判断快指针而不需要判断慢指针。如果为空,即链表不带环,返回NULL
// 检测是否带环同时也避免指针越界访问
if (NULL == pFast || NULL == pFast->next)
return NULL;
pFast = pFast->next->next;
pSlow = pSlow->next;
// 此时我们再判断两个指针是否相遇,不能在指针还没走的时候判断,因为最开始,两个指针就是相遇的
if (pFast == pSlow)
break;
}
// 程序运行到此处,两个指针已经相遇,此时我们求环的长度
while ( 1 ){
pFast = pFast->next->next;
fastLength += 2;
pSlow = pSlow->next;
++slowLength;
if (pFast == pSlow)
break;
}
loopLength = fastLength - slowLength;
// 环长度已知,此时我们来找环的入口结点
while ( 1 ){
// p1先走环的长度步 loopLength
for ( int i = 0; i < loopLength; ++i )
p1 = p1->next;
// 因为这个带环链表入口结点有可能就是第一个结点,所以如果此时p1走了环的长度步后回到了原点(即回到环的入口处(链表的第一个结点))与p2相遇了,则直接返回p1。即为环入口结点
if ( p1 == p2 )
return p1;
//否则p1 p2以相同速度开始走
while ( 1 ){
p1 = p1->next;
p2 = p2->next;
//同样的,先走后判断
//此时即找到了环的入口结点
if ( p1 == p2 )
return p1;
}
}
}
共同学习,写下你的评论
评论加载中...
作者其他优质文章