春招刷题笔记-剑指offer-代码的完整性
代码的完整性
数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
class Solution {
public:
double Power(double base, int exponent) {
double result = 0;
// 指数为0,返回1
if (exponent == 0)
result = 1;
result = 1;
int temp = exponent < 0 ? -exponent : exponent;
for (int i = 0; i < temp; i++)
result = result * base;
if (exponent < 0) {
result = 1.0 / result;
}
return result;
}
};
/* 更严谨的解法并且使用了幂函数的性质,可以在O(logn)的时间内求解出来:
a^n = a^(n/2) * a^(n/2) n为偶数时;
a^n = a^(n/2) * a^(n/2) +1 n为奇数时。
*/
class Solution {
public:
double Power(double base, int exponent) {
double result = 0;
// 底数为0,并且这时候的指数小于0,会造成0在分母上的情况
if ((base >= -__DBL_EPSILON__ && base <= __DBL_EPSILON__) && exponent < 0)
return 0;
unsigned int exponent_ = exponent > 0 ? (unsigned int)(exponent) : (unsigned int)(-exponent);
result = PowerWithUnsignedExponent(base, exponent_);
if (exponent < 0)
result = 1.0 / result;
return result;
}
double PowerWithUnsignedExponent(double base, unsigned int exponent) {
if (exponent == 0)
return 1;
if (exponent == 1)
return base;
double result = PowerWithUnsignedExponent(base, exponent >> 1);
result *= result;
// 最好采用位运算来判断最后一位是不是1,
// 计算机中位运算的速度比算数运算的快
//if (exponent % 2 == 1)
if (exponent & 0x1 == 1)
result *= base;
return result;
}
};
打印1到最大的n位数
题目:输入数字n,按顺序打印出从1最大的n位十进制数。比如输入3,则
打印出1、2、3一直到最大的3位数即999。
思路分析: 本道题目需要注意的陷阱是大数的问题,如果给出的n比较大,很有可能会超出计算机能表示的最大的整数的范围,所以此题需要借助一个字符数组来模拟整数,字符数组的大小为n+1,最后一位number[n]用来保存数组的结束标志’\0’,采用全排列的方式来输出。
void Print1ToMaxOfNDigits_2(int n) {
if (n <= 0) return;
char* number = new char[n + 1];
number[n] = '\0';
for (int i = 0; i < 10; i++) {
// 第一个数字分别是从0 ~ 9
number[0] = i + '0';
Print1ToMaxOfNDigitsRecursively(number, n, 0);
}
}
void Print1ToMaxOfNDigitsRecursively(char* number, int length, int index) {
// 递归的终止条件是处理到了字符数组的最后一个元素
if (index == length - 1) {
PrintNumber(number);
return;
}
for (int i = 0; i < 10; i++) {
number[index + 1] = i + '0';
Print1ToMaxOfNDigitsRecursively(number, length, index + 1);
}
}
// 字符串number表示一个数字,数字有若干个0开头
// 打印出这个数字,并忽略开头的0
void PrintNumber(char* number) {
bool isBegining = true;
int length = strlen(number);
for (int i = 0; i < length; i++) {
if (isBegining && number[i] != '0')
isBegining = false;
if (!isBegining) {
printf("%c", number[i]);
}
}
printf("\t");
}
在O(1)时间删除链表结点
题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该
结点。
思路分析: 本题需要注意的是题目给出的用O(1)的时间删除一个节点,另外需要注意的是如果链表只有一个节点的话,需要把头结点设置为空;如果要删除的节点是尾节点需要从头开始遍历到尾节点的前一个节点;如果要删除的节点在中间位置,那么交换要删除的节点和下一个节点的值,再删除下一个节点就OK了。
struct ListNode {
int data;
struct ListNode* next;
};
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) {
// 如果头节点为空或者指定的节点为空,那么直接返回
if (pListHead == nullptr || pToBeDeleted == nullptr)
return;
// 如果指定的节点就是头结点,并且链表只有一个节点
if (pToBeDeleted == *pListHead && pToBeDeleted -> next == nullptr) {
delete pToBeDeleted;
pToBeDeleted = nullptr;
*pListHead = nullptr;
} else if (pToBeDeleted -> next == nullptr) {
// 如果指定的节点是尾节点,那么从头开始遍历,删除尾节点
ListNode* temp = nullptr;
temp = *pListHead;
while (temp && temp -> next != pToBeDeleted)
temp = temp -> next;
if (temp -> next == pToBeDeleted) {
// 确保一定能在链表中找到指定的节点
temp -> next = nullptr;
delete pToBeDeleted;
}
} else {
// 如果指定的节点是链表中间的某一个节点
ListNode* temp = pToBeDeleted -> next;
pToBeDeleted -> data = temp -> data;
pToBeDeleted -> next = temp -> next;
delete temp;
temp = nullptr;
}
}
删除链表中的重复节点
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5.
思路分析:
借助三个指针nodepre,node,和nodenext。nodepre指向的是没有重复的那部分链表的尾部,node和nodenext两个指针的偏移来标记值相同的节点,其中node指向的是有重复数字的最后一个节点,nodenext指向的是node的下一个节点,在删除节点的时候,将nodepre的next指针指向nodenext,node的next指针置为空,就可以删除从nodepre的下一个节点开始到node指向的节点之间的所有重复的节点,删除完成之后,将nodepre指针不变,node指针置为nodenext,nodenext为node的下一个节点。以此类推。注意nodepre的偏移只发生在没有重复节点的时候,如果有重复节点,并且要删除重复节点那么就不会有nodepre指针的偏移。
注意:
(1)本题需要注意的是指针的溢出,因为引入了nodepre,node,nodenext三个指针来判断处理,所以在nodenext这个地方很容易就出现指针的溢出;
(2)引入一个节点phead作为头结点,使得头结点的操作和其余所有节点的操作是一样的;
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead) {
// 链表为空或者链表只有一个节点
if (pHead == nullptr || pHead -> next == nullptr)
return pHead;
// 引入一个节点作为头节点,使得pHead指向的节点的
// 操作和其余的节点的操作是一样的
ListNode* phead = new ListNode(0);
phead -> next = pHead;
ListNode* nodepre = phead;
ListNode* node = pHead;
ListNode* nodenext = nullptr;
if (node != nullptr)
nodenext = node -> next;
while (node) {
if (nodenext && node -> val == nodenext -> val) {
while (nodenext && node && nodenext -> val == node -> val) {
//寻找重复的节点
node = node -> next;
nodenext = nodenext -> next;
}
// 从nodepre下一个位置 开始到node位置的节点都是重复的节点
ListNode* temp = nodepre -> next;
nodepre -> next = nodenext;
node -> next = nullptr;
while (temp != nullptr) {
node = temp ;
temp = temp -> next;
delete node;
}
temp = nullptr;
node = nodepre;
}
nodepre = node;
node = nodenext;
if (node != nullptr)
nodenext = node -> next;
}
pHead = phead -> next;
delete phead;
return pHead;
}
};
正则表达式的匹配
题目描述: 请实现一个函数用来匹配包含’.‘和’‘的正则表达式。模式中的字符’.’
表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题
中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"
和"abaca"匹配,但与"aa.a"及"ab*a"均不匹配。
bool match(const char* str, const char* pattern)
{
if(str == nullptr || pattern == nullptr)
return false;
return matchCore(str, pattern);
}
bool matchCore(const char* str, const char* pattern) {
// 两个都是空串的时候是匹配的
if (*str == '\0' && *pattern == '\0')
return true;
// 要匹配的串不是空的,模式是空的
if (*str != '\0' && *pattern == '\0')
return false;
if (*(pattern + 1) == '*') {
/* 模式的第二个字符是*的时候,有两种选择
第一种:匹配串和模式串的第一个字符是匹配的:这种情况下,有三种往下走的方式
![这里写图片描述](https://img-blog.csdn.net/20171007084510213?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWluZ3lm/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
*/
if (*str == *pattern
|| (*str != '\0' && *pattern == '.')) {
return matchCore(str + 1, pattern + 2)
|| matchCore(str + 1, pattern) || matchCore(str, pattern + 2);
} else {
matchCore(str, pattern + 2);
}
}
if ((*str == *pattern)) || (*str != '\0' && *pattern == '.')) {
return matchCore(str + 1, pattern + 1);
}
return false;
}