leetcode刷题记录

剑指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,也依然应该使用先ifswap的方案.


最后的思考&总结:
输入里的每个数字就像火箭,每个火箭都有自己对应的发射器(索引),当某个发射器上有两个火箭时,就说明重复了.
初始化,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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值