首页 > 其他分享 >手撕代码之数组

手撕代码之数组

时间:2023-08-29 11:41:49浏览次数:32  
标签:return matrix nums int 代码 vector 数组 size



文章目录

  • 一、二维数组中的查找(leetcode 240)
  • 二、旋转数组的最小数字(leetcode 153)
  • 三、旋转数组中的查找(leetcode 33)
  • 四、数组中出现次数超过一半的数字(leetcode 169)
  • 五、把数组排成最大的数(leetcode 179)
  • 六、数组中只出现一次的数字(leetcode 136)
  • 七、排序数组中查找某一个数第一次和最后一次出现的位置(leetcode 34)
  • 八、寻找数组中的重复元素(leetcode 287)
  • 九、顺时针打印矩阵(leetcode 54)
  • 十、数组中第K大的数(leetcode 215)
  • 十一、数据流中的中位数(leetcode 295)
  • 十二、接雨水问题(leetcode 42)
  • 十三、求两个数组的交集(leetcode 350)
  • 十四、二维数组的最长递增序列(leetcode 329)
  • 十五、两个排序数组找第K大的数(leetcode 4)
  • 十六、调整数组使所有奇数位于偶数前面(剑指offer)
  • 十七、二维矩阵顺时针旋转90度(leetcode 48)
  • 十八、包含重复元素的数组全排列


一、二维数组中的查找(leetcode 240)

手撕代码之数组_最小堆


  思路:如果当前元素大于目标值,则向左移动列,如果当前元素小于目标值,则向下移动行

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if (matrix.empty())
            return false;
        int col_len = matrix[0].size(), row_len = matrix.size();
        int row = 0, col = col_len - 1;
        // 循环条件为行和列的作用范围
        while (row < row_len && col >= 0){
            if (matrix[row][col] == target)
                return true;
            // 如果当前元素大于目标值,则向左移动列
            else if (matrix[row][col] > target)
                col--;
            // 如果当前元素小于目标值,则向下移动行
            else
                row++;
        }
        return false;
    }
};

二、旋转数组的最小数字(leetcode 153)

手撕代码之数组_数组_02


  思路:二分查找,如果中间位置的值比左边元素大,则更新左边元素位置为中间位置,否则更新右边元素位置为中间位置

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low = 0, high = nums.size()-1, mid = 0;
        while (nums[low] > nums[high]){
            if (high - low == 1){
                mid = high;
                break;
            }
            mid = low + (high - low) / 2;
            // 二分查找,如果中间位置的值比左边元素大,则更新左边元素位置为中间位置
            // 否则更新右边元素位置为中间位置
            if (nums[mid] > nums[low])
                low = mid;
            else
                high = mid;
        }
        return nums[mid];
    }
};

三、旋转数组中的查找(leetcode 33)

手撕代码之数组_二分查找_03


  思路:

手撕代码之数组_二分查找_04


对于[0,1,2,3,4,5,6,7]而言,如红色标出所示,如果nums[mid]<nums[right],即中间的数小于最右边的数,那么mid(包括mid)右边的数是有序的,如果中间的数大于最右边的数,那么mid(包括mid)左边的数是有序的。那我们每次都可以折半查找。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size()-1;
        while (left <= right){
            int mid = left + (right - left) / 2;
            if (nums[mid] == target)
                return mid;
            // 如果中间数比右边数小,则右半边是有序的
            if (nums[mid] < nums[right]){
            	// 如果target在右半边范围内,则进行二分查找
                if (nums[mid] < target && target <= nums[right])
                    left = mid + 1;
                // 如果target不在右边范围,则缩小查找区间
                else
                    right = mid - 1;
            }
            // 否则左半边是有序的
            else{
            	// 如果target在左半边范围内,则进行二分查找
                if (nums[left] <= target && target < nums[mid])
                    right = mid - 1;
                // 如果target不在左边范围,则缩小查找区间
                else
                    left = mid + 1;
            }
        }
        return -1;
    }
};

四、数组中出现次数超过一半的数字(leetcode 169)

手撕代码之数组_二分查找_05


  思路:题目中要找的数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现的次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数

  第一个数字作为士兵,防守阵地(cnt = 1);

  遇到相同的元素,cnt++

  如果cnt == 0时,选取新的下标值对应的元素作为防守阵地的士兵

  遇到不同的元素(即为敌人),同归于尽,cnt–

  最后留在阵地上的士兵,可能是主元素。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int result = nums[0], count = 1, len = nums.size();
        for(int i = 1; i < len; ++i)
        {
            if(nums[i] == result)
                count++;
            else if(count == 0)
            {
                result = nums[i];
                count = 1;
            }
            else
                count --;
        }
        return result;
    }
};

五、把数组排成最大的数(leetcode 179)

手撕代码之数组_二分查找_06


  思路:重新定义排序规则,两个数字m和n能够拼接成mn和nm,如果mn大于nm,则m应该排在n前面;如果mn小于nm,则m应该排在n后面

class Solution {
public:
    string largestNumber(vector<int>& nums) {
        vector<string> vec_str;
        for (auto num : nums)
            vec_str.push_back(to_string(num));
        sort(vec_str.begin(), vec_str.end(), [](const string & str1, const string & str2)->bool{
            return str1 + str2 > str2 + str1;
        });
        string res;
        for (auto str : vec_str)
            res.append(str);
        if (res[0] == '0')
            return "0";
        return res;
    }
};

六、数组中只出现一次的数字(leetcode 136)

手撕代码之数组_数组_07


  思路:利用异或的基本性质(相同为0,相异为1,与0异或等于本身),以及交换律和结合律

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = nums[0], sz = nums.size();
        for (int i = 1; i < sz; ++i){
            res ^= nums[i];
        }
        return res;
    }
};

七、排序数组中查找某一个数第一次和最后一次出现的位置(leetcode 34)

手撕代码之数组_最小堆_08


  思路:二分查找的变式,注意边界条件

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int low = lower_bound(nums, target);
        int high = upper_bound(nums, target);
        if (nums.empty() || nums[low] != target)
            return vector<int>{-1, -1};
        else
            return vector<int>{low, high};
    }
    // 查找第一次出现的位置
    int lower_bound(vector<int>& nums, int target){
        int low = 0, high = nums.size() - 1;
        while (low < high){
            int mid = low + (high - low) / 2;
            if (nums[mid] >= target)
                high = mid;
            else
                low = mid + 1;
        }
        return low;
    }
    // 查找最后一次出现的位置
    int upper_bound(vector<int>& nums, int target){
        int low = 0, high = nums.size() - 1;
        while (low <= high){
            int mid = low + (high - low) / 2;
            if (nums[mid] <= target)
                low = mid + 1;
            else
                high = mid - 1;
        }
        return high;
    }
};

八、寻找数组中的重复元素(leetcode 287)

手撕代码之数组_数组_09


  思路:用哈希表存储每一个元素出现的次数。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int res;
        unordered_map<int, int> ump;
        for (auto num : nums){
            ump[num]++;
            if (ump[num] > 1){
                res = num;
                break;
            }
        }
        return res;
    }
};

九、顺时针打印矩阵(leetcode 54)

手撕代码之数组_最小堆_10


  思路:用四个变量记录下边界,然后分析打印每一步的前提条件

手撕代码之数组_二分查找_11

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> res;
        if (matrix.empty())
            return res;
        // 用四个变量记录下边界
        int top = 0, btm = matrix.size()-1, left = 0, right = matrix[0].size()-1;
        while (left <= right && top <= btm){
            // 打印第一步不需要条件
            for (int j = left; j <= right; ++j){
                res.push_back(matrix[top][j]);
            }
            // 打印第二步的前提条件是(top<btm)
            if (top < btm){
                for (int i = top+1; i <= btm; ++i){
                    res.push_back(matrix[i][right]);
                }    
            }
            // 打印第三步的前提条件是(top<btm && left<right)
            if (top < btm && left < right){
                for (int j = right-1; j >= left; --j){
                    res.push_back(matrix[btm][j]);
                }                    
            }
            // 打印第四步的前提条件是(top+1<btm&&left<right)
            if (left < right && top+1 < btm){
                for (int i = btm-1; i >= top+1; --i){
                    res.push_back(matrix[i][left]);
                }
            }
            ++top, ++left, --btm, --right;
        }
        return res;
    }
};

十、数组中第K大的数(leetcode 215)

手撕代码之数组_数组_12


  思路:基于快排的思想,比较k和mid的位置关系,每次只需要利用一个区间即可。

手撕代码之数组_数组_13

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        quick_sort(nums, 0, nums.size()-1, k);
        return nums[nums.size() - k];
    }
    void quick_sort(vector<int>& nums, int low, int high, int k){
        if (low < high){
            int mid = partition(nums, low, high);
            // 比较k和mid的位置关系,每次只需要利用一个区间即可
            if (mid > nums.size() - k)
                quick_sort(nums, 0, mid - 1, k);
            else if (mid < nums.size() - k)
                quick_sort(nums, mid + 1, high, k);
            else
                return;
        }
    }
    // 根据枢纽元划分左右区间
    int partition(vector<int>& nums, int low, int high){
        int pivot = nums[low], mid = low;
        for (int i = low + 1; i <= high; ++i)
            if (nums[i] <= pivot)
                swap(nums[i], nums[++mid]);
        swap(nums[low], nums[mid]);
        return mid;
    }
};

十一、数据流中的中位数(leetcode 295)

手撕代码之数组_最小堆_14


  思路:基于最大堆和最小堆来实现,新元素先加入到最大堆,然后将最大堆的堆顶元素加入最小堆中并弹出,这样保证了最小堆的堆顶元素永远大于最大堆的堆顶元素。如果最小堆的元素个数大于最大堆的元素个数,则继续弹出和加入操作,保证最大堆的元素个数始终与最小堆的元素个数相差不超过1

手撕代码之数组_最小堆_15

class MedianFinder {
public:
    priority_queue<int, vector<int>, less<int>> large; // 定义最大堆
    priority_queue<int, vector<int>, greater<int>> small; // 定义最小堆
    MedianFinder() { }
    
    void addNum(int num) {
        // 保证最小堆的堆顶元素永远大于最大堆的堆顶元素
        large.push(num);
        small.push(large.top());
        large.pop();
        if (small.size() > large.size()){
            // 保证最大堆的堆顶元素永远小于最小堆的堆顶元素
            large.push(small.top());
            small.pop(); 
        }
    }
    
    double findMedian() {
        return large.size() > small.size() ? 
        		large.top() : 0.5 * (small.top() + large.top());
    }
};

十二、接雨水问题(leetcode 42)

手撕代码之数组_数组_16


  思路:保存一个左指针left,右指针right,以及左指针遍历的最大值maxleft以及右指针遍历的最大值maxright。如果当前高度小于maxleft,说明有坑,需要补全,对于right也是一样,但是要保持height[left] < height[right],目的是找到最高点。

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() == 0) return 0;
        int left = 0, right = height.size() - 1, maxleft = 0, maxright = 0, res = 0;
        while (left < right) {
            if (height[left] < height[right]){
                maxleft = max(maxleft, height[left]);
                res += maxleft - height[left];
                left++;
            } else {
                maxright = max(maxright, height[right]);
                res += maxright - height[right];
                right--;                
            }
        }
        return res;
    }
};

十三、求两个数组的交集(leetcode 350)

手撕代码之数组_最小堆_17


  思路:用哈希表记录下较长的数组中的每个元素及其出现的次数,遍历较短长度的数组,如果能找到则递减计数

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res;
        if (nums1.size() < nums2.size()) 
            swap(nums1, nums2);
        unordered_map<int, int> mp;
        // 用哈希表记录下较长的数组中的每个元素及其出现的次数
        for (int i = 0; i < nums1.size(); ++i)
            mp[nums1[i]]++;
        for (auto num : nums2){ 
            if (mp.find(num) != mp.end() && mp[num] > 0){
                res.push_back(num);
                mp[num]--;
            }
        }
        return res;
    }
};

十四、二维数组的最长递增序列(leetcode 329)

手撕代码之数组_最小堆_18


  思路:深度优先搜索+记忆化搜索,具体方法如下:从起点开始遍历矩阵,递归寻找其最长递增路径。定义辅助数组record,用于记录已经搜索过的矩阵元素的数据,如record[x][y]存储了从坐标(x, y)出发的最长递增路径长度。之后,进行深度优先搜索。逐一检查某个元素的四个方向,并继续从所有可能出现最长路径的方向上进行搜索。 当遇到record[x][y]已算出的情况,直接返回record[x][y],减少运算量。

class Solution {
public:
    // record[x][y]存储了从坐标(x, y)出发的最长递增路径长度。
    int dfs(vector<vector<int>>& matrix, vector<vector<int>>& record, int x, int y, int lastval) {
        if (x < 0 || y < 0 || x >= matrix.size() || y >= matrix[0].size()) return 0;
        if (matrix[x][y] > lastval) {
            // 路线已算出,直接返回结果
            if (record[x][y] != 0) return record[x][y];
            // 向四周搜索
            int left = dfs(matrix, record, x + 1, y, matrix[x][y]) + 1;
            int right = dfs(matrix, record, x - 1, y, matrix[x][y]) + 1;
            int top = dfs(matrix, record, x, y + 1, matrix[x][y]) + 1;
            int bottom = dfs(matrix, record, x, y - 1, matrix[x][y]) + 1;
            record[x][y] = max(left, max(right, max(top, bottom)));
            return record[x][y];
        }
        return 0;
    }
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        if (matrix.size() == 0) return 0;
        int res = 0;
        vector<vector<int>> record(matrix.size(), vector<int>(matrix[0].size(), 0));
        for (int i = 0; i < matrix.size(); ++i) {
            for (int j = 0; j < matrix[0].size(); ++j) {
                res = max(res, dfs(matrix, record, i, j, -1));
            }
        }
        return res;
    }
};

十五、两个排序数组找第K大的数(leetcode 4)

手撕代码之数组_数组_19


  思路:比较这两个数组的第 K/2 小的数字的大小,如果第一个数组的第 K/2 个数字较小的话,那么说明我们要找的数字肯定不在 nums1 中的前 K/2 个数字,所以我们可以将其淘汰,将 nums1 的起始位置向后移动 K/2 个,并且此时的K也自减去 K/2,调用递归。

手撕代码之数组_数组_20

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        return (findKth(nums1, nums2, (m + n + 1) / 2) + findKth(nums1, nums2, (m + n + 2) / 2)) / 2.0;
    }
    int findKth(vector<int> nums1, vector<int> nums2, int k) {
        if (nums1.empty()) return nums2[k - 1];
        if (nums2.empty()) return nums1[k - 1];
        if (k == 1) return min(nums1[0], nums2[0]);
        int i = min((int)nums1.size(), k / 2), j = min((int)nums2.size(), k / 2);
        if (nums1[i - 1] > nums2[j - 1]) {
            return findKth(nums1, vector<int>(nums2.begin() + j, nums2.end()), k - j);
        } else {
            return findKth(vector<int>(nums1.begin() + i, nums1.end()), nums2, k - i);
        }
        return 0;
    }
};

十六、调整数组使所有奇数位于偶数前面(剑指offer)

手撕代码之数组_最小堆_21


  思路:解法一是利用冒泡排序的方法,遇到前偶后奇就交换;解法二是空间换时间的做法,先摘取再合并

// 解法一
class Solution {
public:
    void reOrderArray(vector<int> &array) {
        // 类似冒泡算法,前偶后奇数就交换
        for (int i = 0; i < array.size(); ++i) {
            for (int j = array.size()-1; j > i; --j) {
                if (array[j] % 2 == 1 && array[j-1] % 2 == 0)
                    swap(array[j], array[j-1]);
            }
        }
    }
};

// 解法二
class Solution {
public:
    void reOrderArray(vector<int> &array) {
        vector<int> res;
        for(int i = 0; i < array.size(); i++)
        {
            if(array[i] % 2 == 1)
                res.push_back(array[i]);
        }
        for(int i = 0; i < array.size(); i++)
        {
            if(array[i] % 2 == 0)
                res.push_back(array[i]);
        }
        array.swap(res);
    }
};

十七、二维矩阵顺时针旋转90度(leetcode 48)

手撕代码之数组_二分查找_22


  思路:先求转置,再把每一行反转

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        /*
        * 先求转置再反转每一行
        */
        int sz = matrix.size();
        for (int i = 0; i < sz; ++i){
            for (int j = i+1; j < sz; ++j)
                swap(matrix[i][j], matrix[j][i]);
            reverse(matrix[i].begin(), matrix[i].end());
        }
        return;
    }
};

十八、包含重复元素的数组全排列

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;

set<vector<int>> ans; //用set去重

void fun(vector<int> &vec, int start, int len)
{
	// 当start==arr.length-1时,说明子序列的长度为1,就不用再往下划分子序列了
	if (start == len)
		ans.insert(vec);
	for (int i = start; i < len; i++)
	{
		swap(vec[start], vec[i]);
		fun(vec, start + 1, len);
		swap(vec[start], vec[i]);
	}
}

vector<vector<int>> Permutation(vector<int> vec) {
	vector<vector<int>> vecout;
	if (!vec.empty())
	{
		fun(vec, 0, vec.size());
		for (auto tmp : ans)
			vecout.push_back(tmp);
	}
	return vecout;
}

int main() {
	vector<vector<int>> res = Permutation({ 1, 2, 3 });
	for (int i = 0; i < res.size(); ++i) {
		for (int j = 0; j < res[0].size(); ++j) {
			cout << res[i][j] << " ";
		}
		cout << endl;
	}
	system("pause");
	return 0;
}


标签:return,matrix,nums,int,代码,vector,数组,size
From: https://blog.51cto.com/u_6526235/7273706

相关文章

  • 手撕代码之其他类型
    文章目录一、根据rand7生成rand10(leetcode470)二、快速幂(leetcode50)三、数字二进制表示后1的个数(leetcode191)四、判断点是否在三角形内五、下一个全排列(leetcode31)六、带精度的开根号(leetcode69)七、实现strcpy和memcpy八、路径简化(leetcode71)九、字母异位词分组(leetcode49)十......
  • 手撕代码之字符串
    文章目录一、反转字符串中的每一个单词(leetcode151、557)二、多个字符串的最长公共前缀(leetcode14)三、字符串转整数(leetcode8)四、N位数字串删除K个数字,使剩下的数字串最小(leetcode402)五、回文子串的个数(Leetcode647)六、最长无重复字符的子串(leetcode3)七、最长回文子串(leetcod......
  • 手撕代码之栈和队列
    文章目录一、括号匹配(leetcode20)二、最小栈(leetcode155)三、两个栈实现一个队列(leetcode232)一、括号匹配(leetcode20)classSolution{public:boolisValid(strings){if(s.empty())returntrue;stack<char>stk;stk.push(s[0]......
  • 手撕代码之二叉树
    文章目录一、根据排序数组构造二叉搜索树(leetcode108)二、根据前序遍历和中序遍历构造二叉树(leetcode105)三、二叉树的非递归遍历(leetcode94、144、145)四、二叉树中和为某一值的路径(leetcode112)五、二叉树的最大深度(leetcode104)六、二叉树的层次遍历(leetcode102)七、判断两个二......
  • 用js reduce 写一个reduce循环遍历数组对象,里面带有if判断
    简单的reduce案例,实际场景中使用不多,这里给到一个常用的遍历数组对象!!varproducts=[{name:"Apple",price:2.5,quantity:3},{name:"Banana",price:1.5,quantity:2},{name:"Orange",price:3,quantity:4},];vartotalPrice=products......
  • Java代码审计之目录穿越
    一、目录穿越漏洞1、什么是目录穿越所谓的目录穿越指利用操作系统中的文件系统对目录的表示。在文件系统路径中,".."表示上一级目录,当你使用"../"时,你正在引用当前目录的上一级目录。如果你使用"../../",你实际上在两次".."的基础上,再次引用上一级目录,从而返回到上两级目录。......
  • Vscode如何如何显示vue代码提示
    Vscode使用版本 下载这个插件 ......
  • 5.1.29 远程代码执行漏洞
    ThinkPHP55.0.22/5.1.29远程代码执行漏洞漏洞描述Thinkphp是一个国内轻量级的开发框架。由于没有正确处理控制器名,导致在网站没有开启强制路由的情况下(即默认情况下)可以执行任意方法,从而导致远程命令执行漏洞。验证方式直接访问http://your-ip:8080/index.php?s=/Index/\thi......
  • 在ardiuno中把String变量#true#2a#3#转化为按照#分隔的数组, 然后再把数组第一个元素
    在Arduino中,你可以使用strtok()函数将一个String变量按照指定的分隔符切割为多个子字符串,并将它们存储到一个数组中。然后,你可以使用strcmp()函数将数组的第一个元素与字符串"true"进行比较。以下是一个示例,演示如何在Arduino中将String变量str按照#分隔符切割......
  • python代码画爱心❤(海龟)
    importturtle#设置标题turtle.title("蜜蜂的程序")turtle.st()#显示海龟print(turtle.position())turtle.color("red","pink")turtle.begin_fill()#填充前turtle.left(90)turtle.penup()turtle.pendown()turtle.circle(60,180)turtle.circle(18......