• 力扣leetcode-算法基础21天刷题记录①



    力扣【leetcode】 算法基础21天刷题 记录篇一


    菜鸡算法刷题打卡!!


    ⭐二分查找


    34. 在排序数组中查找元素的第一个和最后一个位置


    给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

    如果数组中不存在目标值 target,返回 [-1, -1]。

    进阶:

    你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?


    二分查找


    两次二分查找应用


    class Solution {
    public:
        vector<int> searchRange(vector<int>& nums, int target) {
            int n = nums.size();
            int l = 0, r = n - 1;
            int index1 = n, index2 = n;
            // 获取第一个跟目标数相等的数的下标
            while(l <= r){
                int mid = l + (r - l) / 2;
                if(nums[mid] >= target) {
                    r = mid - 1;
                    index1 = mid;
                }
                else{
                    l = mid + 1;
                }
            }
    
            // 获取最后一个跟目标数相等的数的下标
            l = 0, r = n - 1;
            while(l <= r){
                int mid = l + (r - l) / 2;
                if(nums[mid] > target) {
                    r = mid - 1;
                    index2 = mid;
                }
                else{
                    l = mid + 1;
                }
            }
            index2--;
            if(index1 <= index2 && index2 < n && nums[index1] == target && nums[index2] == target){
                return vector<int>{index1, index2};
            }
            else {
                return vector<int>{-1, -1};
            }
        }
    };
    

    33. 搜索旋转排序数组


    整数数组 nums 按升序排列,数组中的值 互不相同 。

    在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

    给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。


    // 旋转排序数组本质上就是两段升序数组,实际上,抽象讲就是经历一次的二分查找
    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int n = nums.size();
            int l = 0, r = n - 1;
            while(l < r) {
                // r获取两段升序的衔接
                if (nums[r] - nums[l] < 0){
                    r--;
                }
                else {
                    break;
                }
            }
    
            // 分情况二分查找
            int i, j;
            if (target == nums[r]){
                return r;
            }
            // 注意等号!若无,则会忽略第一个元素是寻找数的情况 
            else if(target >= nums[0] && target < nums[r]){
                // target落在 0~r 间的升序数组
                i = 0;
                j = r - 1;
            }
             // target落在 r~n-1 间的升序数组
            else if (target < nums[0]){
                i = r + 1;
                j = n - 1;
            }
            else {
                return -1;
            }
        //    二分查找
           while(i <= j) {
                int mid = i + (j - i) / 2;
                if (nums[mid] == target) {
                    return mid;
                }
                else if ( nums[mid] < target) {
                    i = mid + 1;
                }
                else{
                    j = mid - 1;
                }
            }
            return -1;
        }
    };
    

    74. 搜索二维矩阵


    编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

    每行中的整数从左到右按升序排列。
    每行的第一个整数大于前一行的最后一个整数。


    class Solution {
    public:
        bool searchMatrix(vector<vector<int>>& matrix, int target) {
            int n = matrix[0].size(), m = matrix.size();
            int l = 0, r = m * n -1;
            while(l <= r){
                int mid = l + (r - l) / 2;
                if (matrix[mid / n][mid % n] == target) {
                    return true;
                }
                else if (matrix[mid / n][mid % n] < target) {
                    l = mid + 1;
                }
                else {
                    r = mid - 1;
                }
            }
            return false;
        }
    };
    

    153. 寻找旋转排序数组中的最小值


    已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
    若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
    若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
    注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

    给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。


    // 利用旋转,产生两段升序的数组,则找到分界点,利用升序特点进行求解最小值
    class Solution {
    public:
        int findMin(vector<int>& nums) {
            int  n = nums.size();
            int l = 0, r = n - 1;
            while(l <= r) {
                if (nums[r] - nums[l] < 0) {
                    r--;
                }
                else{
                    break;
                }
            }
            // 若直接是升序数组,则两段升序数组并不存在,可直接返回
            if (r != n-1) {
                return min(nums[0],nums[r + 1]);
            }
            else {
                return nums[0];
            }
        }
    };
    

    162. 寻找峰值


    峰值元素是指其值大于左右相邻值的元素。

    给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

    你可以假设 nums[-1] = nums[n] = -∞ 。


    // 需要再考虑一种情况,即升序,无降,此时返回数组最后一个位置
    class Solution {
    public:
        int findPeakElement(vector<int>& nums) {
            int n = nums.size();
            int i = 0;
            int index = -1;
            while(i < n-1){
                if (nums[i] < nums[i+1]) {
                    i++;
                }
                else {
                    index = i;
                    break;
                }
            }
            if (i == n-1){
                index = n-1;
            }
            return index;
        }
    };
    

    ⭐双指针


    82. 删除排序链表中的重复元素 II


    存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。

    返回同样按升序排列的结果链表。


    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
    public:
        ListNode* deleteDuplicates(ListNode* head) {
            if(!head){
                return head;
            }
            ListNode* dumpy = new ListNode(0, head);
            ListNode* cur = dumpy;
            // 遍历整个链表
            while(cur->next && cur->next->next) {
                // 找到重复的链表,进行循环替代
                if(cur->next->val == cur->next->next->val) {
                    int x = cur->next->val;
                    while(cur->next && cur->next->val == x){
                        cur->next = cur->next->next;
                    }
                }
                else{
                    cur = cur->next;
                }
            }
            return dumpy->next;
        }
    };
    

    15. 三数之和


    给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

    注意:答案中不可以包含重复的三元组。


    // 对数组进行升序排序,k指针从头遍历.利用i j双指针进行移动
    class Solution {
    public:
        vector<vector<int>> threeSum(vector<int>& nums) {
            int n = nums.size();
            vector<vector<int>> sum;
            // 进行排序
            sort(nums.begin(), nums.end());
            for(int k = 0; k < nums.size(); ++k){
                // 正数,相加也不会等于0
                if(nums[k] > 0) {
                    break;
                }
                // 相等
                if(k > 0 && nums[k] == nums[k - 1]) {
                    continue;
                }
                int i = k + 1, j = n - 1;
                while(i < j) {
                    int ans = nums[k] + nums[i] + nums[j];
                    // <0 说明负数这边更大,因此移动左指针,使得和接近0
                    if( ans < 0) {
                        while(i < j && nums[i] == nums[++i]);
                    }
                    else if (ans > 0) {
                        while(i < j && nums[j] == nums[--j]);
                    }
                    else{
                        sum.push_back(vector<int> {nums[k], nums[i], nums[j]});
                        while(i < j && nums[i] == nums[++i]);
                        while(i < j && nums[j] == nums[--j]);
                    }
                }  
            }
            return sum;
        }
    };
    

    844. 比较含退格的字符串


    给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。

    注意:如果对空文本输入退格字符,文本继续为空。


    class Solution {
    public:
        bool backspaceCompare(string s, string t) {
            return backTo(s) == backTo(t);
        }
    
        string backTo(string str) {
            string s;
            for (char ch : str) {
                if(ch != '#') {
                    // 把非#的字符插入字符串
                    s.push_back(ch);
                }
                else if (! s.empty()) {
                    // 遇到#,将此时字符串中的最后一个字符切除
                    s.pop_back();
                }
            }
            return s;
        }
    };
    

    986. 区间列表的交集


    给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序 。

    返回这 两个区间列表的交集 。

    形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b 。

    两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3] 。


    class Solution {
    public:
        vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList, vector<vector<int>>& secondList) {
            vector<vector<int>> ans;
            int i = 0, j = 0;
            while (i < firstList.size() && j < secondList.size()) {
                int low = max(firstList[i][0], secondList[j][0]);
                int high = min(firstList[i][1], secondList[j][1]);
                if (low <= high) {
                    ans.push_back(vector<int> {low, high});
                }
                if (firstList[i][1] < secondList[j][1]) {
                    i++;
                }
                else{
                    j++;
                }
            }
            return ans;
        }
    };
    

    11. 盛最多水的容器


    给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

    说明:你不能倾斜容器。


    class Solution {
    public:
        int maxArea(vector<int>& height) {
            int l = 0, r = height.size() - 1;
            int ans = 0;
            while (l < r) {
                int area = min(height[l], height[r]) * (r - l);
                ans = max(ans, area);
                if (height[l] <= height[r]) {
                    ++l;
                }
                else {
                    --r;
                }
            }
            return ans;
        }
    };
    

    ⭐滑动窗口

    438. 找到字符串中所有字母异位词

    给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

    异位词 指字母相同,但排列不同的字符串。


    // 类似567. 字符串的排列。也就是说只需考虑字母出现的次数以及总长度即可
    class Solution {
    public:
        vector<int> findAnagrams(string s, string p) {
            int n = p.length();
            vector<int> a(26,0);
            for(auto c : p){
                a[c-'a']++;
            }
            int l = 0, r = 0;
            vector<int> a1(a);
            vector<int> index;
            while(r < s.size()){
                a1[s[r] - 'a']--;
                while(a1[s[r] - 'a'] < 0){
                    a1[s[l] - 'a']++;
                    l++;
                }
                if(n == r - l + 1) {
                    index.push_back(l);
                }
                r++;
            }
            return index;
        }
    };
    

    713. 乘积小于K的子数组


    给定一个正整数数组 nums和整数 k

    请找出该数组内乘积小于 k 的连续的子数组的个数。


    简单思路,就是从第一个数开始往后迭乘,超过k则跳出循环、

    class Solution {
    public:
        int numSubarrayProductLessThanK(vector<int>& nums, int k) {
            int n = nums.size();
            int l = 0, r = 0, cnt;
            int sum = 0;
            while(l < n) {
                r = l;
                cnt = 1;
                while(r < n) {
                    cnt *= nums[r++];
                    if(cnt >= k){
                        break;
                    }
                    sum++;         
                }
                l++;
            }
            return sum;
        }
    };
    
    // 但是很遗憾,超出时间限制。于是我们换个思路来解决该难题。
    // 之前我们一直是遍历数组进行迭乘,那么我们是否可以只迭乘一次?
    // 将右指针固定移动遍历。利用左指针缩短左右指针间隔? 如下:
    
    

    通过


    class Solution {
    public:
        int numSubarrayProductLessThanK(vector<int>& nums, int k) {
            // 正整数乘积小于1,不存在直接返回。
            if(k <= 1)
                return 0;
            int l = 0, r = 0, n = nums.size();
            int sum = 0, cnt = 1;
            while(r < n) {
                // 右指针进行移动,一直迭乘
                cnt *= nums[r];
                while(cnt >= k) {
                    // 左指针进行移动条件为 迭乘超过k
                    cnt /= nums[l++];
                }
                // 左右指针间隔差即为小于k的连续子数组的个数
                sum += r - l + 1;
                r++;
            }
            return sum;
        }
    };
    

    209. 长度最小的子数组


    给定一个含有 n 个正整数的数组和一个正整数 target 。

    找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。


    跟上一题差不多


    class Solution {
    public:
        int minSubArrayLen(int target, vector<int>& nums) {
            int n = nums.size();
            if(n == 0){
                return 0;
            }
            int mins = n;
            int l = 0, r = 0, len = 0;
            int sum = 0;
            bool flag = false;
            while(r < n){
                sum += nums[r];
                while(sum >= target) {
                    len = r - l + 1;
                    mins = min(mins, len);
                    sum -= nums[l];
                    l++;
                    // 标记存在符合条件的连续子数组
                    flag = true;
                }
                r++;
            }
            if(!flag)
                mins = 0;
            return mins;
        }
    };
    

  • 相关阅读:
    对List进行子查询及分组
    网格数据库架构设计构想
    推荐一款简单实用的漏洞测试工具:Paros
    用友U9产品SOA设计架构遭技术质疑
    超简单使用MemCached
    不按常规出招
    如何学好AJax、求高手指点
    Begin
    SpringBoot基础
    SpringBoot基础学习(番外9.1)Spring MVC或Spring Boot配置默认访问页面不生效?
  • 原文地址:https://www.cnblogs.com/Jlay/p/Leetcode_wp1.html
Copyright © 2020-2023  润新知