leetcode每日一题复盘(数组,字符串)

leetcode 1 两数之和

第一次做题的时候不知道规则,傻乎乎的自己写了个主函数,导致测试用例各种类型不匹配耽误了不少时间,总结是对vector容器不熟悉,vector<int>类型和int的数组类型混淆,以及对vector容器的初始化不了解,对获取数组长度的函数用法不熟

vector容器本质上仍是数组,vector是STL标准库中提供的模板类,里面封装了动态数组(不定长数组),通过参数决定存放的数据类型

vector类的类型定义

template<class T, class Alloc = allocator<T>>

class vector

{

private:

T *start_;

T *finish_;

T *end_;

……

}

模板参数中class T表示存放的数据类型,class Alloc表示分配器的类型,如果省略该模板参数的值,将默认使用allocator<T>,用new和delete分配和释放内存

分配器

在C++中,分配单个对象时,通常将内存分配和对象初始化组合在一起,分配内存后调用类的构造函数初始化对象;

当分配较大内存时,我们可以使用分配器,将内存分配和构造对象分开,分配出来的内存是未构造的,我们可以按需使用其中的某一块内存

而allocator是一个类,allocator对象为数据类型为T的对象分配内存

start_,finish_,end_这三个指针的意义在图中很容易理解,黄色表示已使用的内存大小,黄加蓝表示的是整个分配的内存大小

vector<int>类型和int的数组类型的区别

在于vector<int>是C++标准库中的容器,可以动态分配内存大小,而int数组类型是C++的基本数据类型,其内存大小是预先分配好的,两者的相同点仅在于存放的数据类型是相同的(也是我一开始混淆的点),两种类型之间有很大的差别,不能当作同一种类型

 vector容器有构造,赋值,插入删除,比较等操作,涉及到多个函数,下面举例部分:

vector相关函数

构造函数:

vector();  // 创建一个空的vector容器

vector(initializer_list<T> il); // 使用统一初始化列表进行对象初始化  例:  vector a({1,2,3})或vector a={1,2,3}(不能加explicit)

vector(const vector<T>& v);  // 拷贝构造函数

explicit vector(const size_t n);   // 创建vector容器,元素个数为n(容量和实际大小都是n)

赋值函数:

vector &operator=(const vector<T> &v);    // 把容器v赋值给当前容器

vector &operator=(initializer_list<T> il); // 用统一初始化列表给当前容器赋值

插入删除:

void push_back(const T& value);  // 在容器的尾部追加一个元素

void emplace_back(…);           // 在容器的尾部追加一个元素,…用于构造元素

emplace_back和push_back区别在于可以直接构造对象,不用传已构造好的对象进去

iterator insert(iterator pos, const T& value);  // 在指定位置插入一个元素,返回指向插入元素的迭代器

iterator emplace (iterator pos, …);  // 在指定位置插入一个元素,…用于构造元素,返回指向插入元素的迭代器

void pop_back();                      // 从容器尾部删除一个元素

iterator erase(iterator pos);      // 删除指定位置的元素,返回下一个有效的迭代器

补充获取数组\字符串长度的四种函数:

strlen()函数:

strlen属于C库函数,用于计算字符串str的长度直到空结束字符(不包括\0)

例:a="hello";strlen(a) 此时返回值为5

sizeof()运算符:

sizeof 是一个关键字,它是一个编译时运算符,用于判断变量或数据类型的字节大小,可用于获取基本数据类型、类、结构、共用体和其他用户自定义数据类型的大小。

例:sizeof(int) 此时返回值为4                  a[2]={1,2};sizeof(a) 此时返回值为2*4=8

值得一提的是sizeof计算字符串大小时算上空结束符\0

length()函数:

length函数是string类库函数,用于计算字符串的长度

例:a="hello" a.length() 此时值为5

size()函数:

size函数是vector类库函数,返回vector中存在的元素个数,可能与vector的实际容量不同;size函数也可用于求字符串长度

例:假设有一个大小为5的vector容器v里面存放了3个元素   则v.size()=3

leetcode 4 寻找两个正序数组的中位数

这题试了两种方法都无法通过,也无法满足时间复杂度的要求,只能想到二分查找,但是想不到如何下手,加之身体不舒服,只能选择最简单最暴力的解法...

下面是leetcode的官方题解:

这里将问题转化为查找第k小的数,对数组A,数组B分别进行二分查找,把k看作最终的查找目标,数组0到k之间看作一个小的数组,此时k/2-1是这个小数组的中位数

然后给出查找的规则,两个小数组的中位数的较小值,不可能是第k小的数,因此排除掉一部分数据

 

当A小数组的中位数小于B小数组的时,则把0到k/2-1的数全部排除,等于大于的情况也是这样处理,这样就完成了第一次二分查找,排除掉了k/2个数

之后继续二分查找,直到k==1(此时总共排除掉了k-1个数),比较两数组第k个数其中较小值就是两个数组的中位数

针对一个数组特别短,一个数组较长导致k/2-1大于数组长度时的情况,这时数组短的中位数直接选取其最后一个元素,更新k的值的时候要根据实际情况

 

 完整代码:

class Solution {
public:
    int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
        /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
         * 这里的 "/" 表示整除
         * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
         * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
         * 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
         * 这样 pivot 本身最大也只能是第 k-1 小的元素
         * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
         * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
         * 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
         */

        int m = nums1.size();
        int n = nums2.size();
        int index1 = 0, index2 = 0;

        while (true) {
            // 边界情况
            if (index1 == m) {
                return nums2[index2 + k - 1];
            }
            if (index2 == n) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return min(nums1[index1], nums2[index2]);
            }

            // 正常情况
            int newIndex1 = min(index1 + k / 2 - 1, m - 1);
            int newIndex2 = min(index2 + k / 2 - 1, n - 1);
            int pivot1 = nums1[newIndex1];
            int pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= newIndex1 - index1 + 1;
                index1 = newIndex1 + 1;
            }
            else {
                k -= newIndex2 - index2 + 1;
                index2 = newIndex2 + 1;
            }
        }
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int totalLength = nums1.size() + nums2.size();
        if (totalLength % 2 == 1) {
            return getKthElement(nums1, nums2, (totalLength + 1) / 2);
        }
        else {
            return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
        }
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/median-of-two-sorted-arrays/solutions/258842/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 程序员面试金典 01.01判定字符是否唯一

今天做了两题,这题太简单了不讲

程序员面试金典 01.02 判定是否互为字符重排 

看了题解才知道可以用哈希表或者排序来做,思路倒是和题解一样,属于是知识没运用上,自己写的算法只能处理短字符串,还是对数据结构不熟悉

 下面分析两种解法,首先是排序:

 这里用到了sort()函数(又是一个没见过的知识点)

sort()函数

sort()函数是C++标准模板库STL中的一个模板函数,可以对数据按需求进行排序,默认是从小到大排序

sort()函数有三个参数,begin,end,cmp,其中begin是第一个元素的指针,end是最后一个元素的下一位的指针,cmp是排序准则,可以自己编写cmp函数自定义排序准则

C++ sort()排序详解_晴空๓的博客-CSDN博客 大佬写的sort函数详解,里面写的很详细了

begin()函数和end()函数 

string类下的begin()函数,返回一个迭代器,指向字符串的第一个元素;

而end()函数返回一个迭代器,指向字符串的末尾(最后一个字符的下一个位置)

四个函数的指示位置说明

这里涉及到迭代器的概念: 

迭代器

迭代器是一种检查容器内元素并遍历元素的数据类型,通常用于对C++中各种容器内元素的访问,不同的容器有不同的迭代器

第二种解法,哈希表:

建立一个长度为128的哈希表,遍历字符串然后在哈希表上进行加减操作,若最后哈希表中数据有小于0的即为出错

这里涉及到一个C++11标准的技巧:简化for循环

简化for循环for(auto c:s)             

c是变量名称可自定义     s是容器名称,也就是循环的对象   auto会根据容器存放的数据自动推导数据类型

c前还可以加&引用符合:
当for(auto &c:s)中加了引用符号,就可以对容器中的内容进行赋值,即可通过对c赋值替换s中的数据,不加&则无法改变s中的数据

程序员面试金典 01.03 URL化 

今天做了两简单题,这题一开始想直接替换发现行不通,后来用复制的方法在空字符串上操作,利用好数组类型的特性是解题的要点

程序员面试金典 01.04 回文排序

统计数据个数,想到了前几天的哈希表,也用上了C++11的简化for循环

解决问题的关键在于要知道回文是什么,回文的本质是什么,学会观察数据并将其转换为规律(奇数个数不大于1)

程序员面试金典 01.05 一次编辑

一开始进行了分类讨论,长度是否相同,不相同再分差多少字符,相同看字符有多少个相同的,这样我想到了用哈希表去统计字符个数,但是不如直接遍历字符串来的方便,实现难度也高,这是思路上面出现了错误

 官方的题解的思路更加清晰,围绕哪些字符串需要一次编辑分类讨论,需要对数据的敏锐观察

程序员面试金典 01.06 字符串压缩 

这题思路是创建一个空字符串,遍历原来的字符串将需要的数据扒下来,先判断新字符串是否为空,为空则将第一个字符加入到空串,用last记录前一个字符,num记录相同的字符数量,当c和last不同时将num和c加入字符串中,如此往复直到原来的字符串完全遍历

字符串拼接整型数据

1.使用+=运算符和string类的to_string函数 

例:S+=to_string(number)

2.使用std::stringstream(字符串流),stringstream是sstream中的一个类,其可以将多种输入类型并以字符串格式存储,其对象可以将基本数据类型转换(流插入运算符<<)成字符串,也可以将字符串转换(流提取运算符>>)成其他数据类型

例:将app_str和number放入tmp_stream中

stringstream类对象的清空操作:c++ stringstream 的clear()清空误区-菜鸟笔记 (coonote.com) 

3.使用append()函数

例:S.append(to_string(number))

程序员面试金典 01.07 旋转矩阵(leetcode 48)   

题目要求的不占用额外空间实在做不到,借助辅助数组可以通过,主要还是需要对位置的观察,发现行列之间的转换关系

用翻转的方法也挺有意思的,相当于先转一个90度再转一个180度

程序员面试金典 01.08 零矩阵

要避免在第一次循环里进行操作,否则会出现内存溢出的情况,借助标志数组将目标进行标记再循环赋值 

leetcode 283 移动零 

经典的快慢指针,想清楚快指针慢指针怎么样动,什么时候两者交换

慢指针是遇到0则停准备比较,快指针则是不停(否则容易进入快指针内容为0的时候才前进,整个循环体用while的误区,因为如果0较少快指针将永远无法到达数组末尾,造成超时)

还有一种思路是遇到非零则交换,后面再补零,类似于创建新数组

同样用到快慢指针的还有leetcode 27 移除元素

 这题的思路是和上面的第二种方法是差不多的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值