剑指offer03 数组中重复的数字
正确答案是:
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int i=0;
while (i<nums.size())
{
if (i==nums[i])
{
++i;
continue;
}
if(nums[i]==nums[nums[i]]) return nums[i];
swap(nums[i],nums[nums[i]]);
}
return -1;
}
};
我的是:
swap(nums[i],nums[nums[i]]);
if(nums[i]==nums[nums[i]]) return nums[i];
这里不能先swap(nums[i],nums[nums[i]]);
再判断if(nums[i]==nums[nums[i]]) return nums[i]
,否则只要测试用例中有0,总会在某一时刻执行到nums[0]=0
此时表达式if(nums[0]==nums[nums[0]]) return nums[i]
的条件一定为真,就会返回0
(=nums[0]=nums[nums[0]])
因此只要测试用例中有0就一定会出错.
错误的原因主要是:每次执行++i
后,就应该先判断这个数在之前是否出现过,而不是先交换.
对于测试用例[2, 3, 1, 0, 2, 5, 3]
前者:
[2, 3, 1, 0, 2, 5, 3]->1,2不同->swap:[1, 3, 2, 0, 2, 5, 3]->1,3不同->[3, 1, 2, 0, 2, 5, 3]->3,0不同->[0, 1, 2, 3, 2, 5, 3]->进入continue循环,++i->i=4时,nums[4]==nums[nums[4]]
->return 2
后者:
[2, 3, 1, 0, 2, 5, 3]->swap:[1, 3, 2, 0, 2, 5, 3]->1,3不同->[3, 1, 2, 0, 2, 5, 3]->3,0不同->[0, 1, 2, 3, 2, 5, 3]->swap(nums[0],nums[nums[0]])
(nums[0]==nums[nums[0]]!!!
)->if(nums[0]==nums[nums[0]])为真
->return nums[0] (return 0)
也就是说当测试用例中含有0时,0最终一定在索引为0的位置,并且i一直会等于0,这样下一次的swap
操作,就是自身和自身swap,就像上面写的那样,接着下一条语句就是if(nums[i]==nums[nums[i]])
一定为真,返回0.
if (i==nums[i])
{
++i;
continue;
}
这个代码块的意思就是为了避免这种情况.因此swap之后应该执行++i
操作消除自己等于自己的可能,然后再判断if(nums[i]==nums[nums[i]])
然而使用我的方法时,只要测试用例含有0,紧跟着的是if(nums[i]==nums[nums[i]])
一定为真,所以++i
这个语句块永远都走不进去
但若测试用例不含0,这两种方法并无差异,因为两者的目的都是把数字和放到对应的索引位置.
为什么测试用例中没0就没事呢?:
if (i==nums[i])
{
++i;
continue;
}
因为当测试用例没有0时,上面的语句块其实没有存在的必要.
因为nums[0]这个位置也永远是非0的其他数字,i==nums[i]
一定是假,此时
swap(nums[i],nums[nums[i]]);
if(nums[i]==nums[nums[i]]) return nums[i];
和
if(nums[i]==nums[nums[i]]) return nums[i];
swap(nums[i],nums[nums[i]]);
从结果来讲确实没有什么区别.但依然不严谨
考虑输入为[3, 3, 1, 2]
显然我的方法会走捷径.
因此即使题目限制输入没有0,也依然应该使用先if
再swap
的方案.
最后的思考&总结:
输入里的每个数字就像火箭,每个火箭都有自己对应的发射器(索引),当某个发射器上有两个火箭时,就说明重复了.
初始化,i=0这个位置作为第一个发射器,把自己位置上的这些数字发射到他们所属的索引位置.同时那个索引位置处的火箭会被替换到i=0这个发射器.
而swap
就是发射&自动替换这对操作,需要注意的是发射器(索引)发射的火箭(数字)数字相同是不允许的.因为这时的if(nums[i]==nums[nums[i]]) return nums[i];
一定成立,失去了判断意义.
对于测试样例无0的情况:
i=0这个发射器没有属于自己的发射物(即只有0这个索引,但输入没有0这个数字),因此永远都不会出现发射器和火箭一样的情况(i != nums[i] forever!!! 也就是说++i
语句块从始至终不会执行)
若测试用例中存在重复的数字,那么索引为这个重复数字的接收器就会接收到两个数字相同的火箭,并且一定是i=0这个发射器发射来的.
对于测试样例有0的情况:
i=0处的发射器有自己的火箭,一旦经过某个swap后,i=0的发射器和数字为0的火箭匹配上,就需要立即移到i=1处的发射器,否则i=0发射的火箭就会回到自己的发射器(if(nums[i]==nums[nums[i]]) return nums[i];
)一定成立,导致误判.
换句话讲,发射器只能接收到其他发射器发射来的火箭
而++i
就是为了防止发射器发射出去的火箭又回到自己这种情况的.因此每次发射(swap)后,就得看看自动替换来的新火箭是不是和该发射器数字一样,如果是(if (i==nums[i])
)就得紧跟着执行++i
语句.
出错的原因:
如果用我的方法++i -> swap -> if
,swap后没有判断发射器发射的火箭时候会回到自身,那么只要输入中有数字0,return 索引为0处的火箭(数字),即0.因此正确方法应该是swap -> ++i -> if
但即使输入没0,我的方法也不太好,如上面提到的[3, 3, 1, 2],不再赘述.
剑指offer04 二维数组的查找
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i=matrix.size()-1;
int j=0;
while(i>=0 && j<=matrix[0].size()-1){
if(target<matrix[i][j])
--i;
else if(target>matrix[i][j])
++j;
else
return true;
}
return false;
}
};
错1:给i
赋值的时候忘了减1
错2:while条件判断的后半部分应该是j<matrix[0].size()
第二次做:又掉坑里了…
参考csapp53页,当执行一个运算时,如果他的一个运算数是有符号的一个是无符号的,那么c语言会隐式的将会有符号参数强制类型转换为无符号数
#include <typeinfo>
int main()
{
// 构造一个元素为空容器的容器
vector<int> empty_vector;
vector<vector<int>> empty_matrix;
empty_matrix.push_back(empty_vector);
// 外部容器的size为1(唯一一个元素是一个空容器),里面容器的size为0(不包含任何元素)
cout<<"empty_matrix.size()="<<empty_matrix.size()<<" empty_matrix[0].size()="<<empty_matrix[0].size()<<endl; //empty_matrix.size()=1 empty_matrix[0].size()=0
// 注意:size()函数返回的是一个64位无符号数,范围为0~18446744073709551615 (2^64-1),因此
// 给empty_matrix[0].size()-1相当于对一个数值为0的无符号数减1,0会变为2^64-1而不是-1
// 原因:二进制表示下,0-1实际上是0x0000000000000000减一,变为0xFFFFFFFFFFFFFFFF
// 对于无符号数,位模式0xFFFFFFFFFFFFFFFF代表2^64-1,对有符号数,代表-1
cout<<"empty_matrix[0].size()-1="<<empty_matrix[0].size()-1<<endl; //18446744073709551615
// 在第一次循环while(i>=0 && j<=(matrix[0].size()-1))中,
// i=0,j=0,empty_matrix[0].size()-1=18446744073709551615满足条件,然而matrix[0][0]是不能取值的
// 报错
// 解决办法一:使用<而不是<=,这样无法进入第一次while(j=0,matrix[0].size()=0),直接跳到后边的return false
// while(i >= 0 && j < matrix[0].size())
//而不是
// while(i>=0 && j<=(matrix[0].size()-1))
// 解决办法二:
// 需要注意的是,以上是针对输入用例为 [[]] 的情况,对于输入为 [] 的情况,对应的是当empty_matrix.size()-1==-1也返回false,并且这个应该先判断
// 第一: 先将empty_matrix.size()-1表示为int类型,如果empty_matrix.size()-1==-1返回false
// 第二: 先将empty_matrix[0].size()-1表示为int类型,如果empty_matrix[0].size()-1==-1,也返回false
// 第三: 使用j<=empty_matrix[0].size()-1作为while循环的条件
}
我的答案:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int j=0;
int i=matrix.size()-1;
if(i==-1) return false;
int test = matrix[0].size()-1;
// 使用int类型的matrix[0].size()-1判断,可以不判断
//int test = matrix[0].size()-1; if(test==-1) return false;
while(i>=0 && j<=test){
if(target<matrix[i][j])
--i;
else if(target>matrix[i][j])
++j;
else
return true;
}
return false;
}
};
或者
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int j=0;
int i=matrix.size()-1;
if(i==-1) return false;
int test = matrix[0].size()-1;
if(test==-1) return false;
// 使用无符号数类型的matrix[0].size()-1,应实现排除matrix[0].size()-1==-1的情况
while(i>=0 && j<=matrix[0].size()-1){
if(target<matrix[i][j])
--i;
else if(target>matrix[i][j])
++j;
else
return true;
}
return false;
}
};
标准答案:
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i = matrix.size() - 1, j = 0;
while(i >= 0 && j < matrix[0].size())
{
if(matrix[i][j] > target) i--;
else if(matrix[i][j] < target) j++;
else return true;
}
return false;
}
};
作者:jyd
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/mian-shi-ti-04-er-wei-shu-zu-zhong-de-cha-zhao-zuo/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指offer05 替换空格
这题没啥好说的,需要注意的思想:
小字符替换大字符的题->
可以先对原字符串进行扩展,然后从后往前处理
class Solution {
public:
string replaceSpace(string s) {
int count = 0;
int j = s.size()-1;
if (j==-1) return s;
for(char &c:s){
if (c==' ')
++count;
}
s.resize(s.size()+count*2);
for (int i=s.size()-1;j<i;--j){
if(s[j]!=' '){
s[i]=s[j];
--i;
}
else if(s[j]==' '){
s[i]='0';
s[i-1]='2';
s[i-2]='%';
i-=3;
}
}
return s;
}
};
剑指 Offer 06. 从尾到头打印链表
ez
就是使用reverse函数的时候写错了 arr.reverse(arr.begin(),arr.end());
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
ListNode *cur = head,*pre = nullptr;
while(cur!=nullptr){
ListNode *tmp = cur->next;
cur->next=pre;
pre = cur;
cur = tmp;
}
vector<int> reverse;
while(pre!=nullptr){
reverse.push_back(pre->val);
pre = pre->next;
}
return reverse;
}
};
或者
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int> arr;
while(head){
arr.push_back(head->val);
head = head->next;
}
reverse(arr.begin(),arr.end()); //reverse函数不需要arr.reverse()
return arr;
}
};