数组知识点以及leetcode刷题

数组


二分查找

题目

力扣题目链接

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

适用范围

数组有序,数组中无重复元素

思路

每次找数组的中点,然后与目标值进行对比。

  • if(nums[mid] > target ) high = mid - 1
  • else if((nums[mid] < target )) low = mid + 1
  • else 找到,返回对应下标

循环终止条件判断,while(low <= high) 此时low == high有意义,所以要加等号。

代码

class Solution { 
    public:     
    int search(vector& nums, int target) {         
        //二分查找         
        int n = nums.size();         
        int left = 0, right = n - 1;                    //定义初始查找边界为左闭右闭
        while(left <= right){                           //等号要加上             
            int mid = left + (right - left)/2;          //等同于 (left+right)/2,防止数组溢出
            if(nums[mid] > target) right = mid - 1;     //在左半部分查找             
            else if(nums[mid] < target) left = mid + 1; //在右半部分查找             
            else return mid;                            //找到,返回对应下标         
        }         
        return -1;                                      //未找到,返回-1      
    } 
};

//在c++的语法当中,存在内置函数进行二分的查找,在编程的时候可以采用内置函数的方法
//lower_bound(val),在数组中找到大于等于val值的第一个元素下标位置
//upper_bound(val),在数组中找到大于val值的第一个元素下标位置
//时间复杂度为O(logn)
//空间复杂度为O(1)

移除元素

题目

力扣题目链接

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

思路

数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。

所以采用双指针的写法对原先的内存进行覆盖

代码

写法一
  • 定义快慢指针
    • left指针作为慢指针,用来更新元素下标,存储不等于val值的元素
    • right指针作为快指针,用来遍历数组

一次循环,只有在right指针不等于值val的情况下,left才进行移动。

class Solution { 
    public:     
    int removeElement(vector& nums, int val) {         
        //双指针,当数组下表为val的时候,left不移动,只有当数组值不为val的时候,left才进行移动
        int n = nums.size();         
        int left=0;         //left作为数组的存储下标,存储不等于val值的元素                  
        int right = 0;      //right作为遍历数组的下标          
        for(right = 0; right < n; right++){             
            if(nums[right] != val){                 
                nums[left++] = nums[right];             
            }         
        }         
        return left;     
    } 
};
// 时间复杂度:O(n) 
// 空间复杂度:O(1)
写法二

类似于快速排序的思想,左指针用来寻找等于val值的元素,右指针来寻找不等于val值的元素,然后找到之后,进行元素之间的交换,这个写法改变了元素之间的相对位置。

    class Solution { 
    public:     
    int removeElement(vector& nums, int val) {     
		/*写法二
        也是双指针写法,但是与第一种有些区别。左右指针,一个从头开始找元素等于val,一个从末尾开始找不是val的,然后元素之间的值进行交换
        */
        int n = nums.size();
        int left = 0, right = n - 1;
        while(left <=  right){
            while(left <= right && nums[left] != val) left++;
            while(left <= right && nums[right] == val) right--;
            if(left <= right){
                nums[left++] = nums[right--];
            }
        }
        return left;
    }
};

有序数组的平方

题目

力扣题目链接

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

思路

双指针解法

一个指针从头开始比较,

一个指针从尾开始比较,

大的数的话,放在最后面,然后对应下标减去1,

一个指针在每次比较的时候往后移,用于计数。

代码

  • 定义左右指针
    • i指针作为左指针,比较负数的最大元素
    • j指针作为右指针,用来比较正数的最大元素
    • pos指针用来模拟下标的移动
class Solution {
public:
    vector sortedSquares(vector& nums) {
        //双指针解法
        int n = nums.size();
        vector ans(n,0);
        int i = 0, j = n - 1, pos = n - 1;
        while(pos >= 0){
            if(nums[i] * nums[i] > nums[j] * nums[j]){
                ans[pos--] = nums[i]*nums[i];
                i++;
            }
            else{
                ans[pos--] = nums[j]*nums[j];
                j--;
            }
        }
        return ans;
    }
};
// 时间复杂度:O(n) 
// 空间复杂度:O(n)

长度最小的子数组

题目

力扣题目链接

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

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

思路

经典滑动窗口思路题

也是类似于双指针的解法,定义两个指针leftright,分别代表滑动窗口的左右边界,同时维护一个变量sum存储子数组,也就是滑动窗口的元素之和。

  • 刚开始,leftright都指向0
  • 然后右指针不断往前走,然后num[end]加到sum
  • 如果说sum大于s,说明此时可以对左边界指针也就是left进行更新,此时获取当前最短的滑动窗口值,然后左指针不断进行移动。此时需要将已经不在滑动窗口里面的值减去
  • 不断地进行迭代,直到右指针走到了数组的末尾,就可以结束了。

代码

  • 定义左右指针
    • i指针作为左指针,作为滑动窗口的左边界
    • j指针作为右指针,作为滑动窗口的右边界
    • sum用来计算滑动窗口的总合,方便左右指针进行更新
class Solution {
public:
    int minSubArrayLen(int target, vector& nums) {
        //滑动窗口典型代表题目
        int n = nums.size();
        int minSize = n + 1;            //初始化滑动窗口的长度为n+1
        int flag = 0;
        int i = 0, j = 0;
        int sum = 0;
        while(j < n){
                sum+=nums[j++];         //右指针往后移动
            while(sum >= target){       //滑动窗口总和大于target时,可以求最小滑动窗口的长度,以及更新左指针
                minSize = min(j - i, minSize);
                sum -= nums[i++];       //左指针不断往后移动
            }
        }
        //三目运算符,如果滑动窗口的值没有进行改变的话,说明是不存在
        return minSize == n + 1 ? 0 : minSize;
    }
};

螺旋矩阵II

题目

力扣题目链接

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

思路一

  • 模拟题,这里设置了一个loop代表顺时针转了几圈,然后根据圈的数量进行模拟左右边界。

  • 模拟顺时针画矩阵的过程:

    • 填充上行从左到右
    • 填充右列从上到下
    • 填充下行从右到左
    • 填充左列从下到上
  • 这里有一个比较值得注意的点是,这里需要保持一个代码规划,就是左右边界的问题,每条边都需要遵循左闭右开的原则。

  • 还有一个需要注意的就是这里loop循环一圈是单数,所以如果n为奇数的话,会剩下最后一个中间的数没有填充,所以最后面还需要判断n是否为奇数。

代码

class Solution {
public:
    vector> generateMatrix(int n) {
        vector> matrix(n, vector(n));
        int i = 0, j = 0, loop = 0;
        int num = 1;
        //螺旋矩阵,最重要的几个点,根据圈数来决定是否要结束循环
        //每次循环的左右边界都需要确定好,是左闭右开,还是左闭右闭
        //圈数如果为偶数,直接循环遍历,但是如果圈数为奇数的话,就需要对最里面的一个数单独赋值
        while(loop < n/2){
            //最上面的一行,根据圈数来决定起始位置\终止位置和每圈的个数,列在增大
            for(i = loop; i < n-loop-1; i++)  matrix[loop][i] = num++;

            //最右边的一行,根据圈数来决定起始位置\终止位置和每圈的个数,行在增大
            for(j = loop; j < n-loop-1; j++) matrix[j][n-loop-1] = num++;

            //最下面的一行,根据圈数来决定起始位置\终止位置和每圈的个数,列在减小,初始值可以利用前面的i
            for(;i > loop; i--)  matrix[n-loop-1][i] = num++;

            //最左边的一行,根据圈数来决定起始位置\终止位置和每圈的个数,行在减小,初始值可以利用前面的j
            for(;j > loop; j--) matrix[j][loop] = num++;

            //循环完一圈,继续下一圈
            loop++;
        }

        //如果圈数为奇数,需要对最里面的数单独赋值
        if(n % 2)
            matrix[loop][loop] = num;
        return matrix;
    }
};

思路二

  • 设置上下左右边界,四个值进行模拟。整体理解难度和模拟难度要比思路一要简单很多,代码实现的话同样需要遵守边界一致性原则,这里采用的是左闭右闭。
  • 模拟顺时针画矩阵的过程:
    • 填充上行从左到右
    • 填充右列从上到下
    • 填充下行从右到左
    • 填充左列从下到上

代码

class Solution {
public:
    vector> generateMatrix(int n) {
        vector> matrix(n, vector(n));
        //模拟,设置上下左右边界,每次遍历完某一行或者某一列,边界进行换
        int num = 1;
        //设置上下左右边界
        int left = 0, high = 0, right = n - 1, low = n - 1;
        while(true){
            for(int i = left; i <= right; i++) matrix[high][i] = num++;
            if(++high > low)  break;

            for(int j = high; j <= low; j++) matrix[j][right] = num++;
            if(--right < left) break;

            for(int i = right; i >= left; i--) matrix[low][i] = num++;
            if(--low < high) break;

            for(int j = low; j >= high; j--) matrix[j][left] = num++;;
            if(++left > right) break;
        }
        return matrix;
    }
};

你可能感兴趣的:(数据结构和算法,leetcode,算法,数据结构,柔性数组,c++,java,c语言)