首页 > 编程语言 >代码随想录算法训练营第二天| LeetCode 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

代码随想录算法训练营第二天| LeetCode 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

时间:2023-07-27 23:22:14浏览次数:65  
标签:977 cout int 位置 随想录 ++ result 数组

977.有序数组的平方

          题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/

        文章讲解:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html

        视频讲解: https://www.bilibili.com/video/BV1QB4y1D7ep

       卡哥的题目建议建议大家先独立做题,然后看视频讲解,然后看文章讲解,然后在重新做一遍题,把题目AC,最后整理成今日当天的博客,拓展题目可以先不做。本题关键在于理解双指针思想。 

         做题思路:

         有两种方法,暴力解法和双指针法。非递减顺序就是递增顺序,从小到大排。

       暴力解法思路:就是将数组中的每个数平方后,再按从小到大的顺序排序。C++的sort函数是基于快速排序实现的,函数原型 void sort(first,last),first是要排序的数组的起始地址,last是结束的地址(最后一个数据的后一个数据的地址)。如果利用迭代器,就可以用sort(a.begin(),a.end()),

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 const int N = 10;
 5 int a[N]; //声明全局数组
 6 int main()
 7 {
 8     int n; //数组的元素个数为 n 
 9     cout << "请输入数组的元素个数:" << endl;
10     cin >> n;
11     cout << "请输入数组:" << endl;
12     for (int i = 0; i < n; i++)
13     {
14         cin >> a[i]; //循环输入数组中的元素
15     }
16     for (int i = 0; i < n; i++)
17     {
18         a[i] *= a[i];
19     }
20     sort(a,a+n); //注意:这里没有利用迭代器
21     for (int i = 0; i < n; i++)
22     {
23         cout << a[i] << " "; //循环输入数组中的元素
24     }
25 
26     return 0;
27 }

       运行结果:

 

        双指针法思路:

       先看给的数组,nums = [-4,-1,0,3,10],有负数;再平方后,nums = [16,1,0,9,100],观察到平方后的数组两边的数比中间的大,所以我们可以考虑从第一个位置向后开始,最后一个位置向前开始,比较谁大,如果最后一个位置的数大的话,那就把这个数存放到新数组中,随着第一个位置和最后一个位置的移动,就能实现排序,有移动的动图,在文章链接里。这个两个位置就可以用双指针解决,其实就是两个变量。

 1 #include <iostream>
 2 using namespace std;
 3 const int N = 10;
 4 int a[N]; //声明全局数组
 5 int main()
 6 {
 7     int n; //数组的元素个数为 n 
 8     cout << "请输入数组的元素个数:" << endl;
 9     cin >> n;
10     cout << "请输入数组:" << endl;
11     for (int i = 0; i < n; i++)
12     {
13         cin >> a[i]; //循环输入数组中的元素
14     }
15     for (int i = 0; i < n; i++)
16     {
17         a[i] *= a[i];
18     }
19     int result[N];//放排好序的新数组
20     int k = n - 1; //新数组的下标为k,因为我们是先把大的数放进新数组,所以k值从n - 1开始
21     int first = 0;
22     int last = n - 1;
23     while (first <= last) //第一个位置的下标肯定比最后一个位置下标小或等于,才合法
24     {
25         if (a[first] < a[last])
26         {
27             result[k] = a[last];//最后一个位置比第一个位置大,就把最后一个位置的数放到新数组
28             k--; //比较完后,新数组的实际大小减一,更新下标
29             last--;//最后一个位置的数放到新数组,最后一个位置向前移动
30         }
31         else
32         {
33             result[k] = a[first];//第一个位置比最后一个位置大,
34             k--;//比较完后,新数组的实际大小减一,更新下标
35             first++;//第一个位置的数放到新数组,第一个位置向后移动
36         }
37     }
38     for (int i = 0; i < n; i++)
39     {
40         cout << result[i] << " "; //循环输入数组中的元素
41     }
42 
43     return 0;
44 }

 

       运行结果:

 

 209.长度最小的子数组

          题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/

       文章讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html

       视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE

         卡哥的题目建议本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。  拓展题目可以先不做。 

         做题思路:

         有两种方法,暴力解法和双指针法。

       暴力解法思路:这个题是为了找到和大于某个值的长度最小的连续子数组,所以先找到和大于某个值的子数组集合,再找长度最小的。可以用两层for循环,外层循环先定好从哪个地方开始找子数组,内层循环求和,从下标为0的地方开始找和大于某个值的位置,如果和大于某个值,就先记录下这个位置到外层循环定好的位置之间的长度,后面的位置没有必要寻找了。然后开始下一轮外层循环,同理.......再记录,更新长度;直到外层循环完后,才能把最短长度求出来。所以暴力解法会超时。

 1 #include <iostream>
 2 using namespace std;
 3 const int N = 10;
 4 int a[N]; //声明全局数组
 5 int minSubArrayLen(int a[], int s)
 6 {
 7     int n = sizeof(a);
 8     int result = INT32_MAX; // 最终的结果,INT32_MAX是int的最大值,2的16次方 - 1
 9     int sum = 0; // 子数组的数值之和
10     int subLength = 0; // 子数组的长度
11     for (int i = 0; i < n; i++) //外层循环
12     {
13         sum = 0; //每一轮外层循环的时候,重置sum值,为了记录不同i值下的长度
14         for (int j = i; j < n; j++) //内层循环
15         {
16             sum += a[j]; 
17             if (sum >= s)//求和,从下标为0的地方开始找和大于某个值的位置
18             {
19                 subLength = j - i + 1; //记录下这个位置到外层循环定好的位置之间的长度
20                 result = result < subLength ? result : subLength;//如果小于最终长度,那最终长度不变;如果大于最终长度,那更新最终长度
21                 break;
22             }
23         }
24     }
25     return result == INT32_MAX ? 0 : result;//如果result没有被赋值的话,就返回0,说明没有符合条件的子数组
26 }
27 int main()
28 {
29     int n; //数组的元素个数为 n 
30     cout << "请输入数组的元素个数:" << endl;
31     cin >> n;
32     cout << "请输入数组:" << endl;
33     for (int i = 0; i < n; i++)
34     {
35         cin >> a[i]; //循环输入数组中的元素
36     }
37     int s;
38     cout << "请输入正整数 s :" << endl;
39     cin >> s;
40     cout << minSubArrayLen(a, s) << endl;
41     return 0;
42 }

     

       运行结果:

 

        双指针法思路:我们为了找到和大于某个值的长度最小的连续子数组,想像在数组上移动着找,刚开始会像暴力解法那样,定好一个起始位置,再移动另一个位置,但是如果两个位置都移动,效率就会提升很多,所以就引出了滑动窗口这个方法。在滑动窗口中,分起始位置和终止位置,终止位置用来找到和大于某个值的连续子数组集合;起始位置用来找最短长度,和暴力解法不同的是,当找到了和大于某个值的连续子数组集合后,就是终止位置暂时不移动了,同时记录下长度,起始位置此时从下标0开始移动,每移动一个位置,就用刚求好的和减去上一个位置的数,为了看还能不能找到更短的长度;如果不能的话,那只能移动终止位置,去重新找和大于某个值的连续子数组集合。同理,直到终止位置移动到数组尾部,且找不到最短长度了,就停止。

 1 #include <iostream>
 2 using namespace std;
 3 const int N = 10;
 4 int a[N]; //声明全局数组
 5 int minSubArrayLen(int a[], int s)
 6 {
 7     int n = sizeof(a);
 8     int result = INT32_MAX; // 最终的结果,INT32_MAX是int的最大值,2的16次方 - 1
 9     int sum = 0; //滑动窗口的数值之和
10     int subLength = 0; //滑动窗口的长度
11     int i = 0; //滑动窗口起始位置
12     for (int j = 0; j < n; j++) 
13     {
14         sum += a[j];
15         // 注意这里使用while,每次更新 i(起始位置),并不断比较子数组是否符合条件
16         while (sum >= s) {
17             subLength = (j - i + 1); // 取子数组的长度
18             result = result < subLength ? result : subLength;
19             sum -= a[i]; // 这里体现出滑动窗口的精髓之处,不断变更i(子数组的起始位置)
20             i++; //起始位置向后移动
21         }
22     }
23     return result == INT32_MAX ? 0 : result;//如果result没有被赋值的话,就返回0,说明没有符合条件的子数组
24 }
25 int main()
26 {
27     int n; //数组的元素个数为 n 
28     cout << "请输入数组的元素个数:" << endl;
29     cin >> n;
30     cout << "请输入数组:" << endl;
31     for (int i = 0; i < n; i++)
32     {
33         cin >> a[i]; //循环输入数组中的元素
34     }
35     int s;
36     cout << "请输入正整数 s :" << endl;
37     cin >> s;
38     cout << minSubArrayLen(a, s) << endl;
39     return 0;
40 }

 

       运行结果:

 

59.螺旋矩阵II

        题目链接:https://leetcode.cn/problems/spiral-matrix-ii/

      文章讲解:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html

      视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/

     卡哥的题目建议本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。

     做题思路:

        我感觉这里看视频和文章就能理解,就是要想到矩阵可以是一个正方形,从总共能走的圈数,再从正方形4条边入手,这些边可以当做在一个区间上,写一些区间上的边界的限制条件。结合之前做的题,会想到左闭右闭,左闭右开........

 1 #include <iostream>
 2 using namespace std;
 3 const int N  = 10;
 4 int main()
 5 {
 6     int n;
 7     cout << "请输入一个正整数 n:" << endl;
 8     cin >> n;
 9     int res[N][N] = { 0 };
10     int startx = 0, starty = 0; // 定义每循环一个圈的起始位置,相当于一个坐标(startx, starty)
11     int loop = n / 2; // 这个可以画图理解,每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
12     int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
13     int count = 1; // 用来给矩阵中每一个空格赋值
14     int offset = 1; // 每一圈右边界向里收缩一位
15     int i, j;
16     while (loop--)
17     {
18         i = startx;
19         j = starty;
20 
21         // 下面开始的四个for就是模拟转了一圈
22         // 模拟填充上行从左到右(左闭右开)
23         for (j = starty; j < n - offset; j++) //行不变,列变,所以列的初始值一开始为0,右开,右边界值不能取到
24         { 
25             res[startx][j] = count++;
26         }
27         // 模拟填充右列从上到下(左闭右开)
28         for (i = startx; i < n - offset; i++) //列不变,行变,所以行的初始值一开始为0,右开,右边界值不能取到
29         {
30             res[i][j] = count++;
31         }
32         // 模拟填充下行从右到左(左闭右开)
33         for (; j > starty; j--) //走到了i, j 的最大值处,不需要设行列的初始值,行不变,列变,列不能比0小
34         {
35             res[i][j] = count++;
36         }
37         // 模拟填充左列从下到上(左闭右开)
38         for (; i > startx; i--)//走到了 i 的最大值处........
39         {
40             res[i][j] = count++;
41         }
42 
43         // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
44         startx++;
45         starty++;
46 
47         // offset 控制每一圈里每一条边遍历的长度
48         offset += 1;
49     }
50 
51     // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
52     if (n % 2 == 1) 
53     {
54         res[mid][mid] = count;
55     }
56     for (int i = 0; i < n; i++) //矩阵为 n*n
57     {
58         cout << "[";
59         for (int j = 0; j < n; j++)
60         {
61             cout << res[i][j] << " ";
62         }
63         cout << "]";
64         cout << endl;
65     }
66     return 0;
67 }

 

      运行结果:

 

最后,数组的总结看一下卡哥的吧。

           文章链接:https://programmercarl.com/%E6%95%B0%E7%BB%84%E6%80%BB%E7%BB%93%E7%AF%87.html

 

 

标签:977,cout,int,位置,随想录,++,result,数组
From: https://www.cnblogs.com/romantichuaner/p/17584494.html

相关文章

  • [代码随想录]Day02-数组part02
    题目:977.有序数组的平方思路:一开始的思路是从中间向两边扩:找到第一个大于等于0的位置r;判断nums[r]是否大于等于0,如果不是赋值为len(nums)表示不在范围内了。l的位置在r的左侧因此l=r-1只要l>=0或者r<len(nums)满足一个就可以继续;遍历时要保证数组不能越界说实话维护......
  • 2023-07-27:最长可整合子数组的长度, 数组中的数字排序之后,相邻两数的差值是1, 这种数组
    2023-07-27:最长可整合子数组的长度,数组中的数字排序之后,相邻两数的差值是1,这种数组就叫可整合数组。给定一个数组,求最长可整合子数组的长度。答案2023-07-27:算法maxLen的过程如下:1.检查输入数组是否为空,如果为空,则返回0,表示最长可整合子数组长度为0。2.初始化长度为1的最长......
  • Java 使用数组给对象赋值
    Java使用数组给对象赋值在Java中,我们可以使用数组来给对象赋值。这是一种常见的操作,特别是在处理大量数据时非常有用。在本篇文章中,我将教会你如何使用数组给对象赋值,以及每一步需要做什么。流程概述在开始之前,让我们先来了解一下整个操作的流程。下表展示了用于给对象赋值的步......
  • C#中将字符串分割成字符数组
    在C#中字符串类型String是由一系列的单个字符组合而成,其实可以通过字符串String对象ToCharArray()方法来将字符串中的元素逐一存在数据类型为Char的一维数组中。例如将字符str="ABCDEFG"分割为到一维数组可用下列语句:stringstr="ABCD";char[]strCharArr=str.ToC......
  • java 动态生成int数组
    Java动态生成int数组在Java中,动态生成int数组是一种常见的需求。动态生成数组意味着在程序运行时根据需要创建数组,并根据特定的条件来初始化数组的大小和元素。这种灵活性使得程序能够根据实际情况动态调整数组的大小和内容,提高程序的效率和可扩展性。使用ArrayList动态生成int数......
  • 238. 除自身以外数组的乘积
    给你一个整数数组nums,返回数组answer,其中answer[i]等于nums中除nums[i]之外其余各元素的乘积。题目数据保证数组nums之中任意元素的全部前缀元素和后缀的乘积都在32位整数范围内。请不要使用除法,且在O(n)时间复杂度内完成此题。示例1:输入:nums=[1,......
  • 带你详细刨析JavaScript 对象数组的深浅拷贝
    深浅拷贝●深浅拷贝指的是一种复制对象或者数组的行为●也就是把一个对象或者数组中的数据完完整整的复制一份放到另一个数组或者对象中●并且相互之间没有联系●说道深浅拷贝这个我们不考虑基本数据类型●因为基本数据类型没有引用地址一说●说到复制这个事儿有三个级别○赋值......
  • MFC-realloc修改数组容量
     TCHAR*p,*q;//分配初始内存空间p=(TCHAR*)malloc(10*sizeof(TCHAR));//初始化p中的数据for(inti=0;i<9;i++){*(p+i)=_T('a');}*(p+9)=_T('\0');//扩容pq=(TCHAR*)realloc......
  • 2023.7.25 将数组和减半的最少操作次数
    贪心,显然每次都削减最大数的一半,可以更快的接近至少削减一半的目标。可以证明,削减任何不是最大数的一半,都不会优于削减最大数的一半,因为题目要求不是正好减少一半,而是至少减少一半。所以可以开一个大根堆存储所有的数,每次取出堆顶的数进行减半,然后将减半后的数插入堆中,并且更新......
  • Go语言初始化数组的方式
    在Go语言中,数组的初始化有多种方法,我会一一为你列举如下:基本初始化:可以在声明数组时直接指定元素的初始值,由编译器自动推断数组的长度。//方法1:使用数组字面值初始化arr1:=[3]int{1,2,3}//方法2:使用自动推断数组长度arr2:=[...]int{4,5,6}指定索引初始化:可......