LeetCode算法日记:数组和字符串

这篇博客详细介绍了LeetCode中关于数组和字符串的算法题目,包括寻找数组中心索引、搜索插入位置、合并区间等数组问题,以及翻转字符串里的单词、实现strStr()等字符串操作。此外,还探讨了二维数组的旋转和对角线遍历,以及使用双指针技巧解决问题的方法。
摘要由CSDN通过智能技术生成

数组和字符串

日期:2022/7/10

数组简介

1.寻找数组的中心索引

题目描述:给你一个整数数组 nums ,请计算数组的 中心下标 。

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1

示例:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。

思路:

sum = nums[0]+...+nums[nums.size()-1]
if(left == (sum-nums[i])/2.0) return i;

代码+解析:

class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        if(nums.size() == 1) return 0;

        int sum = 0;
        for(int i=0; i<nums.size(); i++){
            sum += nums[i];
        }

        int i;
        float left = 0;
        for(i=0; i<nums.size(); i++){
            if(left == (sum-nums[i])/2.0) return i;
            left += nums[i];
        }

        return -1;
    }
};

2.搜索插入位置

题目描述:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例:

输入: nums = [1,3,5,6], target = 5
输出: 2

输入: nums = [1,3,5,6], target = 2
输出: 1

输入: nums = [1,3,5,6], target = 7
输出: 4

思路:

二分查找

代码+解析:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0,right = nums.size()-1;

        while(left<=right){
            int mid = (left + right)/2; 
            if(nums[mid] == target) return mid;
            else if(nums[mid] < target) left = mid + 1;
            else if(nums[mid] > target) right = mid -1; 
        }
        return left;
    }
};

3.合并区间

题目描述:以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

思路:

无思路,有手就行

代码+解析:

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        int n = intervals.size();
        //if(n == 1) return intervals;
        sort(intervals.begin(),intervals.end());  //排序,虽然例子给的都是排好序的,但实际上没排好序
        vector<vector<int>> res;
        res.push_back(intervals[0]);
        int j=0;
        for(int i=1; i<n; i++){
            vector<int> temp = intervals[i]; //res是前,temp是后
            if(res[j][1]>=temp[0]){  //如(x,3),(2,y)
                 if(res[j][1]<temp[1]) //如(x,3),(2,4)
                 {
                     res[j][1] = temp[1]; //(x,4)
                 }
            }
            else{  //如(x,3),(4,5)
                res.push_back(temp);
                j++;
            }
        }
        return res;
    }
};

二维数组简介

1.旋转矩阵

题目描述:给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。

不占用额外内存空间能否做到?

示例:

给定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

原地旋转输入矩阵,使其变为:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

给定 matrix =
[
  [ 5, 1, 9,11],
  [ 2, 4, 8,10],
  [13, 3, 6, 7],
  [15,14,12,16]
], 

原地旋转输入矩阵,使其变为:
[
  [15,13, 2, 5],
  [14, 3, 4, 1],
  [12, 6, 8, 9],
  [16, 7,10,11]
]

思路:

先上下翻转 matrix[i][j]和matrix[n-i-1][j]交换
再对角线翻转 matrix[i][j]和matrix[j][i]交换

代码+解析:

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        if(n<=1) return;

        for(int i=0; i<n/2; i++){    //遍历到matrix一半的行,不然就恢复原样了
            for(int j=0; j<n; j++){  //上下翻转
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n-i-1][j];
                matrix[n-i-1][j] = temp;
            }
        }

        for(int i=0; i<n; i++){      //对角线翻转
            for(int j=0; j<=i; j++){   
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }

    }
};

2.零矩阵

题目描述:编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。

示例:

输入:
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]
输出:
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]

输入:
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]
输出:
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]

思路:

用数组rows和columns存储matrix中为0的那个元素的行列,之后分别遍历rows和columns,把所在行和列都清零

代码+解析:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int n = matrix.size();
        int m = matrix[0].size();
        vector<int> rows;
        vector<int> columns;
        
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++)
                if(matrix[i][j] == 0){
                    rows.push_back(i);
                    columns.push_back(j);
                }
        }

        for(int i=0; i<rows.size(); i++){
            int row = rows[i];
            for(int j=0; j<matrix[row].size(); j++)
                matrix[row][j] = 0;
        }

        for(int j=0; j<columns.size(); j++){
            int column = columns[j];
            for(int i=0; i<matrix.size(); i++)
                matrix[i][column] = 0;
        }
    }
};

3.对角线遍历

题目描述:给你一个大小为 m x n 的矩阵 mat ,请以对角线遍历的顺序,用一个数组返回这个矩阵中的所有元素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EIVeJBSc-1657611659845)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20220711151309513.png)]

示例:

输入:mat = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,4,7,5,3,6,8,9]

输入:mat = [[1,2],[3,4]]
输出:[1,2,3,4]

思路:

将一个斜方向的一行看成一个单位,当i为奇数时,遍历方向向下;i为偶数时,遍历方向向上。

代码+解析:

class Solution {
public:
    vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
        int n = mat.size();
        int m = mat[0].size();
        vector<int> res;

        int row=0,col=0;
        for(int i=0; i<n+m-1; i++){
            if(i%2){   //奇数,方向向下
                col = (i<m) ? i : m-1;
                row = i-col;
                while(col >= 0 && row < n){
                    res.push_back(mat[row++][col--]);
                }
            }else{  //偶数,方向向上
                row = (i<n) ? i : n-1;
                col = i - row;
                while(col < m && row >= 0){
                    res.push_back(mat[row--][col++]);
                }
            }
        }

        return res;
    }
};

字符串简介

日期:2022/7/11

前两个做过了

3.翻转字符串里的单词

题目描述:给你一个字符串 s ,颠倒字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

**进阶:**如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

示例:

输入:s = "the sky is blue"
输出:"blue is sky the"

输入:s = "  hello world  "
输出:"world hello"
解释:颠倒后的字符串中不能存在前导空格和尾随空格。

输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,颠倒后的字符串需要将单词间的空格减少到仅有一个。

思路:

先翻转整个字符串s,然后一个一个单词的翻转回来。

代码+解析:

class Solution {
public:
    string reverseWords(string s) {
        reverse(s.begin(),s.end());  //翻转整个字符串

        int index = 0;   //对应结果的s的下标索引
        for(int i=0; i<s.size(); i++){
            if(s[i] != ' '){    //遇到字母的时候,也就是遇到空格直接跳过 
                if(index != 0) s[index++] = ' ';  //除了s[0],其他时候都手动添加一个空格

                int end = i;
                while(end < s.size() && s[end] != ' ')s[index++] = s[end++]; //将单词放到index对应的位置上

                reverse(s.begin()+index-(end-i),s.begin()+index);  //翻转刚刚移动的单词

                i = end;   //i移动到刚刚翻转的单词的最后一位
            }
        }

        s.erase(s.begin()+index,s.end()); //消除后面的字符串
        return s;
    }
};

学到的总结:

  1. reverse(s.begin(),s.end()),翻转字符串
  2. erase(),删除子串

4.实现strStr()

*字符串匹配算法:KMP

题目描述:实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

示例:

输入:haystack = "hello", needle = "ll"
输出:2

输入:haystack = "aaaaa", needle = "bba"
输出:-1

思路:

用KMP算法,其实我没看懂,直接抄的题解,希望以后能看懂

代码+解析:

class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.size(), m = needle.size();
        if(!m) return 0;
        
        vector<int> next(m,0);
        next[0] = -1;
        int j = -1;
        for(int i=0; i<m-1; i++){    //这个大概就是构造next
            while(j>=0 && needle[j] != needle[i]) j = next[j];
            j++;
            next[i + 1] = j;
        }
        j=0;
        for(int i=0; i<n; i++){
            while(j>=0 && haystack[i] != needle[j]) j = next[j];
            j++;
            if(j == m) return i-m+1;
        }
        return -1;
    }
};

双指针技巧

情景一:对撞指针

1.反转字符串

题目描述:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

思路:

双指针

代码+解析:

class Solution {
public:
    void reverseString(vector<char>& s) {
        int i=0,j=s.size()-1;

        while(i<j){
            char temp = s[i];
            s[i] = s[j];
            s[j] = temp;
            j--;
            i++;
        }
    }
};

2.数组拆分Ⅰ

题目描述:给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。
返回该 最大总和 。

示例:

输入:nums = [1,4,3,2]
输出:4
解释:所有可能的分法(忽略元素顺序)为:
1. (1, 4), (2, 3) -> min(1, 4) + min(2, 3) = 1 + 2 = 3
2. (1, 3), (2, 4) -> min(1, 3) + min(2, 4) = 1 + 2 = 3
3. (1, 2), (3, 4) -> min(1, 2) + min(3, 4) = 1 + 3 = 4
所以最大总和为 4

输入:nums = [6,2,6,5,1,2]
输出:9
解释:最优的分法为 (2, 1), (2, 5), (6, 6). min(2, 1) + min(2, 5) + min(6, 6) = 1 + 2 + 6 = 9

思路:

先排序,然后下标为偶数的相加。虽然没有用到双指针,不知道为什么说这个是双指针题目

代码+解析:

class Solution {
public:
    int arrayPairSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int sum = 0;
        for(int i=0; i<nums.size(); i++){
            sum += nums[i];
            i++;
        }
        return sum;
    }
};

3.两数之和Ⅱ - 输入有序数组

做过了

情景二:快慢指针:常见于要求使用O(1)的空间复杂度的原地解法

4.移除元素

做过了

5.最大连续1的个数

题目描述:给定一个二进制数组 nums , 计算其中最大连续 1 的个数。

示例:

输入:nums = [1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.

输入:nums = [1,0,1,1,0,1]
输出:2

思路:

有手就行,这题也不知道和快慢指针有什么关系

代码+解析:

class Solution {
public:
    int findMaxConsecutiveOnes(vector<int>& nums) {
        int res = 0;
        int temp = 0;
        for(int i=0; i<nums.size(); i++){
            if(nums[i] == 1){
                temp++;
            }else{
                res = max(temp,res);
                temp = 0;
            }
        }
        res = max(temp,res);
        return res;
    }
};

6.长度最小的子数组

做过了

数组相关技术

  • 类似于数组的数据结构:字符串、哈希表、链表、队列、栈
  • 调用内置函数来对数组进行排序
  • 二分查找
  • 双指针
  • 贪心算法

1.杨辉三角

题目描述:给定一个非负整数 *numRows,*生成「杨辉三角」的前 numRows 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

示例:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

输入: numRows = 1
输出: [[1]]

思路:

如果位于第一列或最后一列,则push_back(1);否则,push_back(上一行前一列+上一行同一列)

代码+解析:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> res(numRows);

        res[0].push_back(1);
        for(int row=1; row<numRows; row++){
            for(int col=0; col<=row; col++){
                if(col == 0 || col == row){
                    res[row].push_back(1);
                }else{
                    res[row].push_back(res[row-1][col-1]+res[row-1][col]);
                }
            }
        }

        return res;
    }
};

日期:2022/7/12

2.杨辉三角Ⅱ

题目描述:给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

示例:

输入: rowIndex = 3
输出: [1,3,3,1]

输入: rowIndex = 0
输出: [1]

输入: rowIndex = 1
输出: [1,1]

思路:

看的题解,看得懂但估计记不住

代码+解析:

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int> res(rowIndex+1);

        for(int i=0; i<=rowIndex; i++)  //先让所有的都为1
            res[i] = 1;  

        if(rowIndex<2) return res;   //[1,1]或者[1]

        for(int i=1; i<rowIndex; i++){  //模拟每一行,直到最后一行,个人感觉有点像动态规划
            for(int j=i; j>0; j--)     //从每一行倒数第二个开始往前遍历,
                res[j] = res[j] + res[j-1];
        }

        return res;
    }
};

3.反转字符串中的单词Ⅲ

题目描述:给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序

示例:

输入:s = "Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"

输入: s = "God Ding"
输出:"doG gniD"

思路:

遇到单词时,设置start和end,i=end,之后用对撞指针反转单词

代码+解析:

class Solution {
public:
    string reverseWords(string s) {
        int start=0,end = 0;
        for(int i=0; i<s.size(); i++){
            if(s[i]!=' '){
                start = i;
                end = i;
                while(end+1<s.size() && s[end+1]!=' ') end++;
                i = end;
                while(start<end){
                    char temp = s[start];
                    s[start] = s[end];
                    s[end] = temp;
                    start++;
                    end--;
                }
            }
        }
        return s;
    }
};

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

题目描述:已知一个长度为 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 ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

思路:

用对撞指针,但是不知道这个算不算O(logn)

代码+解析:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int i=0,j=nums.size()-1;

        while(i<j){
            if(nums[i]>nums[i+1])return nums[i+1];
            if(nums[j]<nums[j-1])return nums[j];
            i++;
            j--;
        }
        return nums[0];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值