我用来记录自己刷代码随想录(就是Carl)大佬写的那本书的题目,如果有想一起学习的自行百度搜索代码随想录即可。
本篇是数组篇。
其中题目开头的数字为该题在LeetCode中的序号。
因为我一开始学的Java,所以所有题目都是用Java写的,但是后面又转型了C++,所以二刷题目会有C++版本。
这是一道非常经典的二分查找题,比较水的那种,懂二分的基本都能直接写出来,我直接写自己的题解了:
class Solution {
public int search(int[] nums, int target) {
int left = 0; //定义左指针,指向数组的起始点元素
int right = nums.length-1; //定义右指针,指向数组的末尾元素
int mid = (left+right)/2; //中间指针,指向数组最中间的元素
//永真循环一直判断
while(true){
//直到遍历完数组都没有找到,那么返回-1结果表示未找到
//这里有个小技巧,不能直接判断left >= right,
//因为有可能正好下标left==right时的mid值就是我们要查找的值
if(left > right) return -1;
//找到了那么直接返回下标
if(target == nums[mid]) return mid;
//如果目标值小于中间值
if(target < nums[mid]){
//那么让右指针指向的范围变小
right = mid - 1;
}else{
//那么让左指针指向的范围变大
left = mid + 1;
}
//刷新mid值
mid = (right + left) / 2;
}
}
}
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int mid = (left+right) / 2;
while(true){
if(left > right) break;
if(target == nums[mid]) return mid;
if(target < nums[mid]) right = mid - 1;
else left = mid + 1;
mid = (left + right) / 2;
}
//如果没有找到目标值,则按照上述逻辑
//是会跳出循环来执行下述逻辑的
//即未找到目标值,那么返回它将会被按顺序插入的位置
int cnt = 0; //计数器,记录插入位置
for(int i = 0;i < nums.length;i++){
if(target < nums[i]) return cnt;
cnt++;
}
return nums.length;
}
}
思路:
这里我的思路和一位录友的想法很重合,我就直接复制他的思路了,因为我感觉自己讲的没他好。
1、首先,在 nums 数组中二分查找 target;
2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1};
3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
下面给出我的代码实现,注释都写的很清楚了:
class Solution {
public int[] searchRange(int[] nums, int target) {
//这个题因为数组都是升序排列
//所以如果存在目标值,那么这几个目标值绝逼都是挨着排列的
//那问题应该就蛮好解决了
//我们先用二分查找判断是否能够找到目标值
int left = 0;
int right = nums.length - 1;
int mid = (left + right) / 2;
//创建一个结果数组拿来返回结果
int[] arr = {-1,-1};
while(true){
if(left > right) return arr; //未找到,直接返回-1,-1
if(target == nums[mid]){ //找到了,那么我们就要从找到的这个值的坐标开始,向左向右遍历
//这个目标值是存在的
//所以我们现在要做的事情就是
//从此处开始向前向后遍历
//找到这值的起始位置和结束位置
int i = mid;
while(i+1 < nums.length && target == nums[i+1]){ //判断数组越界的逻辑一定要在前面,这里是短路与的写法
//只要前面的i+1 < nums.length不满足条件,则会直接跳出不管后面的判断了
//先向后遍历,当target与nums[i]不相等时,退出循环
i++;
}
//退出循环则这是结束位置
//赋值给我们的结果数组
arr[1] = i;
//向后遍历结束,现在向前遍历
i = mid; //刷新i值
while(i-1 >= 0 && target == nums[i-1]){
//再向前遍历,当target与nums[i]不相等时,退出循环
i--;
}
//退出循环则这是开始位置
//赋值给我们的结果数组
arr[0] = i;
return arr;
}
if(target < nums[mid]) right = mid - 1;
else left = mid + 1;
mid = (left + right) / 2;
}
}
}
这个题有一个很需要注意的地方是,千万注意数组越界的判断!!!
这种错误在很多题里都很容易犯,短路与是一个很好的解决方法。
这个我有了一个更好的解决办法,不过现在我使用C++来做题了:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
//左右分开找
return vector<int> {findLeft(nums,target),findRight(nums,target)};
}
//寻找左边界
int findLeft(auto& nums,auto target){
int l = 0,r = nums.size() -1;
while(l <= r){
int mid = (l + r) >> 1;
if(nums[mid] > target) r = mid -1;
else if(nums[mid] < target) l = mid + 1;
else if((mid == 0 || nums[mid-1] < target) && nums[mid] == target) return mid;
//nums[mid] == target,但该mid所指元素不是最左边的,那么继续向左找
else r = mid - 1;
}
return -1;
}
//寻找左边界
int findRight(auto& nums,auto target){
int l = 0;
int r = nums.size()- 1;
while(l <= r){
int mid = (l+r) >> 1;
if(nums[mid] > target) r = mid -1;
else if(nums[mid] < target) l = mid + 1;
else if((mid == nums.size()-1 || nums[mid + 1] > target) && nums[mid] == target) return mid;
//nums[mid] == target,但该mid所指元素不是最右边的,那么继续向右找
else l = mid + 1;
}
return -1;
}
};
思路:
搬运了LeetCode上一位答友的思路,我觉得写得很好。
整数x的平方根一定小于或等于x;
除0之外的所有整数的平方根都大于或等于1;
整数x的平方根一定是在1到x的范围内,取这个范围内的中间数字mid,并判断mid的平方是否小于或等于x,如果mid的平方小于x;
那么接着判断(mid+1)的平方是否大于x,如果(mid+1)的平方大于x,那么mid就是x的平方根;
如果mid的平方小于x并且(mid+1)的平方小于x,那么x的平方根比mid大,接下来搜索从mid+1到x的范围;
如果mid的平方大于x,则x的平方根小于mid,接下来搜索1到mid-1的范围
然后在相应的范围内重复这个过程,总是取出位于范围中间的mid;
代码如下:
class Solution {
public int mySqrt(int x) {
//因为数据范围很大,为了避免溢出,所以全部采用了long类型
long left = 0;//左指针
long right = x; //右指针
long ans = 0; //记录结果的值
//二分查找,k平方小于等于x嘛,这是数学公式
while(left <= right){
long mid = (right + left) / 2;
if(mid*mid <= x){//满足条件
ans = mid; //赋一个结果值
left = mid + 1;
}else{
right = mid - 1;
}
}
//题目要求返回整形,说明最后的结果肯定是在整形范围内的
//所以我们不用担心大转小精度缺失,直接类型转换即可
return (int)ans;
}
}
这里我写了对应的C++版本:
class Solution {
public:
int mySqrt(int x) {
int l = 0;//这里不能为1是因为传进来的x可能为0
int r = x;
int res = -1;
while(l <= r){
int mid = (l + r) >> 1;
//这里主要是注意一下mid*mid的值可能会非常大,用long long接一下即可
if((long long)mid * mid > x) r = mid - 1;
else {
res = mid;
l = mid + 1;
}
}
return res;
}
};
题目描述:
这个题就是上面那个题的一个变种,没什么说的思路都一样,二分查找找符合条件的值即可:
class Solution {
public boolean isPerfectSquare(int num) {
//上一个题的变种
//思路是就用二分查找找到有没有和这个值相等的即可
long left = 0;
long right = num;
while(left <= right){
long mid = (left+right) / 2;
if(mid * mid == num){
return true;
}else if(mid*mid < num){
left = mid + 1;
}else right = mid - 1;
}
return false;
}
}
更新C++的版本:
class Solution {
public:
bool isPerfectSquare(int num) {
int l = 0;
int r = num;
while(l <= r){
int mid = (l + r) >> 1;
if((long long) mid * mid > num) r = mid - 1;
else if((long long)mid*mid < num) l = mid + 1;
//如果有效的话那么num值对该值取余是肯定为0的,所以是完全平方数
//取余不为0的话则说明有小数
else if(num%mid == 0) return true;
}
return false;
}
};
题目描述:
思路:
双指针法,这个思路我感觉很难说啊,我也是看了题解才做出来的,感觉得靠自己悟性了,看别人大佬的题解应该可以懂的。
反正就是一个快指针一个慢指针嘛,然后快指针是一直在走的,而慢指针只有在特殊情况下才会走,这个特殊情况看题目而定。最后返回只需要返回慢指针的值即可。看代码叭,应该会好一点。
class Solution {
public int removeElement(int[] nums, int val) {
//双指针法
int slowIndex = 0; //慢指针
int fastIndex = 0; //快指针
for(;fastIndex < nums.length;fastIndex++){//快指针一直走
if(nums[fastIndex] != val){//特殊情况发生,本题是符合条件的值放入慢指针所指向的内存空间(数组是下标指向)
nums[slowIndex] = nums[fastIndex];
slowIndex++; //满足条件慢指针开始移动
}
}
//返回慢指针的值即可
return slowIndex;
}
}
这个题后来我用了暴力破解的方式也一样过了,用c++写的:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int cnt = nums.size();
//其实我这种做法也差不多是双指针
for(int i=0;i<cnt;){
//如果数值相等
if(nums[i] == val){
--cnt;
int j=i;
//就把其后的所有元素一个一个移动到前面来即可
for(;j<nums.size()-1;++j){
nums[j] = nums[j+1];
}
//移动完之后把最后一位置成0
nums[j] = 0;
//然后i又从一开始遍历移动
i = 0;
}else ++i;//如果不是我们要删除的值,那我们就再进行i++
}
return cnt;
}
};
然后双指针的C++写法:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//更简便的双指针写法
//这个写法可以模拟一下测试用例:[0,1,2,2,3,0,4,2]
int slow = 0;
int fast = 0;
for(;fast < nums.size();++fast){
if(nums[fast] != val){
nums[slow] = nums[fast];
++slow;
}
}
return slow;
}
};
题目描述:
思路:
首先要记住,线性表的问题一般都可以使用双指针法来解决。
然后做题目之前一定要先看题目给的提示,人家已经说了这个数组是个升序数组,那么相等的值在这个数组中肯定都是挨在一起的。既然如此那我们就只要判断每次快指针所指向的数组元素值是否等于前一个数组元素值即可完成题目,如果相等则不管,相等说明重复我们不能将它放进慢指针所指向的某种意义上的数组(我靠这个要自己去悟才行),如果不相等那说明是不重复的了,我们将其放入即可,然后让慢指针自增以便接收下一个不重复值的到来:
class Solution {
public int removeDuplicates(int[] nums) {
//这种线性问题一般都可以用双指针解决
int slowIndex = 1; //第一个值肯定是必须在的呀,所以我们从第二个值开始操作
int fastIndex;
for(fastIndex = 1;fastIndex < nums.length;fastIndex++){
if(nums[fastIndex] != nums[fastIndex-1]){//判断每一次快指针指向的数组元素值是否等于前一个数组元素值
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
}
其实还是和上面一个题一模一样,一样用双指针,找几个数组模拟一下就能知道该怎么改,这里改用C++写一下:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slow = 0;
int fast = 0;
for(;fast < nums.size();++fast){
if(nums[fast] != nums[slow]){
++slow;
nums[slow] = nums[fast];
}
}
return slow+1;//这里会发现下标少一,注意与本文上面一题辨别
}
};
题目描述:
思路:
都是同类型的题,都差不多,我把思路在代码中写了,看代码就行。
class Solution {
public void moveZeroes(int[] nums) {
//老方法
//如果是线性问题,双指针一般都可以解决
int slowIndex = 0;
int fastIndex = 0;
for(;fastIndex<nums.length;fastIndex++){
if(nums[fastIndex] != 0){ //如果不等于0,放入慢指针所指向的数组中
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
//经过上面处理之后现在slowIndex慢指针所指向的数组肯定是所有值都按原顺序放在前面了
//我们现在要做的事情就是把slowIndex慢指针的后面小于数组长度的那部分元素给置成0即可
while(slowIndex < nums.length){
nums[slowIndex] = 0;
slowIndex++;
}
}
}
二刷使用C++来写了:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0;
int fast = 0;
//这里依然是采用之前题目的双指针模板
//只不过我们先用双指针模板将0值全部清楚
//此时从slow下标之前的所有元素都是非0的
for(;fast < nums.size();++fast){
if(nums[fast] != 0){
nums[slow] = nums[fast];
++slow;
}
}
//这里我们只要从slow开始将数组的后半部分全部都置为0即可
while(slow<nums.size()) {
nums[slow] = 0;
++slow;
}
}
};
思路:
双指针法。
这个可以参考一下LeetCode上的题解,我也是参考了才算差不多想明白的,不过我下面的代码每一步都有清晰注释,搭配上LeetCode上的题解思路应该能看的比较明白的。
只能说很妙!其实另外一个解法要简单点,就是用栈什么的,会比较好明白。
class Solution {
public boolean backspaceCompare(String s, String t) {
int i = s.length()-1,j = t.length()-1;//逆序遍历
int skipS = 0;//skip用来记录#个数
int skipT = 0;
while(i >= 0 || j >= 0){ //只要还有一个字符串没遍历完就一直遍历
//先找s串中逆序的第一个确定的字符(逆序)
while(i >= 0){
if(s.charAt(i) == '#'){ //当前字符为#
skipS++; //#数量加1
i--;//长度-1
}else if(skipS > 0){ //说明当前字符需要被退格
skipS--; //退一次格,减少一个#
i--; //字符被退格,长度-1
}else break; //如果既不等于#又不需要被空格,那么说明已经找到了s串中第一个确定存在的字符了,退出循环即可
//那么现在来找T串中的第一个确定的字符(逆序)
}
while(j >= 0){
if(t.charAt(j) == '#'){
skipT++;
j--;
}else if(skipT > 0){
skipT--;
j--;
}else break;
}
//经过上面处理之后,现在两个串都找到了第一个确定的字符,那么我们现在来进行判断这俩是否相等
//下面为什么要判断j和i是否大于等于0呢?
//因为如果 index = 0 位置上为 '#',则 i, j 会为 -1,为什么?
//因为前面我们的判断条件为如下:
//if(skipS > 0){
// skipS--;
// i--;
//如果index=0位置上为#,那么i和j就为-1了
// 而 index = -1 的情况应当处理。
if (i >= 0 && j >= 0)
{
// 如果待比较字符不同,return false
if (s.charAt(i) != t.charAt(j))
return false;
}
// (i >= 0 && j >= 0) 为 false 情况为
// 1. i < 0 && j >= 0
// 2. j < 0 && i >= 0
// 3. i < 0 && j < 0
// 其中,第 3 种情况为符合题意情况,因为这种情况下 s 和 t 都是 index = 0 的位置为 '#' 而这种情况下
// 退格空字符即为空字符,也符合题意,应当返回 True。
// 但是,情况 1 和 2 不符合题意,因为 s 和 t 其中一个是在 index >= 0 处找到了待比较字符,另一个没有找到
// 这种情况显然不符合题意,应当返回 False,下式便处理这种情况。
else if (i >= 0 || j >= 0)
return false;
i--; //上面的操作结束还未退出程序则说明第一次匹配是相等的,下面循环执行即可
j--;
}
//退出循环都还没有结束程序,说明为true
return true;
}
}
使用栈的思路,C++写法:
class Solution {
public:
bool backspaceCompare(string s, string t) {
return build(s) == build(t);
}
string build(string str){
//模拟栈
string res ;
for(auto ch : str){
if(ch != '#'){
res.push_back(ch);
}else if(!res.empty()){
res.pop_back();
}
}
return res;
}
};
双指针的思路,C++版:
class Solution {
public:
bool backspaceCompare(string s, string t) {
//一个字符是否会被删除,只取决于该字符后面的退格符,而与该字符前面的退格符无关
//所以逆序遍历字符串就可以确定当前字符是否要被删除
int skipS = 0;
int skipT = 0; //分别记录两个字符串的待删除字符的数量
//因为要倒序遍历,所以从后往前遍历
int i = s.size()-1;//i是字符串s下标
int j = t.size()-1;//j是字符串t下标
//只要还有一个没遍历完就继续遍历
while(i>=0 || j>=0){
//先走s的
while(i>=0){
//如果遇到#号,
if(s[i] == '#'){
//那么需要退格的字符数量+1
skipS++;
//遍历一个数下标前移
i--;
}else if(skipS > 0){
//否则说明遇到的字符,判断skipS是否大于0
//大于则说明需要进行退格
//则退格字符数量-1,并且数下标前移
skipS--,i--;
}else {
//上面两个条件都不满足的话就停止循环
//等待t字符串的循环检查
break;
}
}
//现在走t串的
while(j>=0){
if(t[j] == '#'){
skipT++;
j--;
}else if(skipT > 0){
skipT--;
j--;
}else break;
}
//经过上面处理之后,现在两个串都找到了第一个确定的字符,那么我们现在来进行判断这俩是否相等
//下面为什么要判断j和i是否大于等于0呢?
//因为如果 index = 0 位置上为 '#',则 i, j 会为 -1,为什么?
//因为前面我们的判断条件为如下:
//if(skipS > 0){
// skipS--;
// i--;
//如果index=0位置上为#,那么i和j就为-1了
// 而 index = -1 的情况应当处理。
//什么意思?
//举个例子:
//s:ab## t: c#d#
//上面两个串经过处理完后其下标i为-1,j下标也为-1
//但此时也应该返回true,所以这就是为什么我们要判断else if(j>=0 || i>=0) 的原因
if (i >= 0 && j >= 0)
{
// 如果待比较字符不同,return false
if (s[i] != t[j]) return false;
}
// (i >= 0 && j >= 0) 为 false 情况为
// 1. i < 0 && j >= 0
// 2. j < 0 && i >= 0
// 3. i < 0 && j < 0
// 其中,第 3 种情况为符合题意情况,因为这种情况下 s 和 t 都是 index = 0 的位置为 '#' 而这种情况下
// 退格空字符即为空字符,也符合题意,应当返回 True。
// 但是,情况 1 和 2 不符合题意,因为 s 和 t 其中一个是在 index >= 0 处找到了待比较字符,另一个没有找到
// 这种情况显然不符合题意,应当返回 False,下式便处理这种情况。
else if(j>=0 || i>=0) return false;
i--,j--;
}
return true;
}
};
思路:
这个题用暴力做是最好做的,无非就两步,把每个数都拿出来求平方值,然后结果用一个新创建的数组装起来,对这个新数组排序返回即可。
其他的解法后面复盘的时候再分析叭。
class Solution {
public int[] sortedSquares(int[] nums) {
int[] arr = new int[nums.length];
for(int i=0; i<nums.length ;i++){
arr[i] = nums[i] * nums[i];
}
Arrays.sort(arr);
return arr;
}
}
如果是笔试的话其实上面的思路就最简单也最快的,但是如果是面试的时候像上面这样写肯定就显得没有什么水平,所以这里推荐另外一种比较有水平的写法:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
//因为原数组是有序的,又因为负数值平方后可能值会变大
//那么最大值肯定就是在最两侧,那么我们只要每回取两侧绝对值的平方的较大的那个直接逆序存入就可以解决问题
//这样使用一头一尾的双指针法可得到时间复杂度为O(n)的解决问题方法
int i = 0;
int j = nums.size() -1;
int pos = nums.size();
vector<int> res(pos);
while(i <= j ){
pos--;
if((nums[i]*nums[i]) > (nums[j]*nums[j])){
res[pos] = nums[i] * nums[i];
i++;
}else{
res[pos] = nums[j] * nums[j];
j--;
}
}
return res;
}
};
题目描述:
思路:
我直接用的暴力搜索,思路都在代码中了写的很详细的。更好的方法后面复盘再想。
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int sum = 0; //用来求连续子数组的和的
int len = 0; //用来求子数组的长度
int res = Integer.MAX_VALUE;//将结果值一开始设置为无穷大值
//这样下面只要出现了一个满足>=target条件的数组长度
//我们就拿来与res比较
//如果当次子数组长度比res小,那么我们就刷新res的值为档次子数组长度
for(int i = 0; i < nums.length; i++){
sum = 0; //求和,每回求和之前都要刷新sum值
for(int j = i; j < nums.length; j++){//让j从当前的i索引处往后遍历寻找子数组
sum = sum + nums[j];//累加求当前子数组的元素和
if(sum >= s){ //说明当前遍历到的这一段子数组是符合条件的
//那么我们取出这此子数组的长度
len = j - i + 1; //索引相减加上1,因为数组从0开始计数的呀
//比较当前这次子数组的长度和原res的大小
if(len < res){ //如果当前子数组的长度小于原res
//刷新res值为更小
res = len;
}
break; //既然已经在当前这次序列中查找到了符合条件的子数组
//那么处理完就退出循环,继续地毯式搜索下一次符合条件的子数组
}
}
}
//返回res,如果res的值还是预先定义的值
//那么说明并未找到,则返回0即可
//若不是原来的值,那么我们直接返回res
return res == Integer.MAX_VALUE ? 0 : res;
}
}
有一个点是,因为题解当中都用的无穷大值,所以我上面也用的无穷大值,但理论上只要大于等于题目给的nums数组的长度范围都是可以的。比如100000,不一定要用Integer.MAXVALUE,一开始我都看懵了都。
// 本题要求,选择一个最长只有两个元素的子序列
class Solution {
public int totalFruit(int[] fruits) {
if(fruits.length == 1) {
return fruits.length;
}
int basket1 = -1, basket2 = -1; //记录当前篮子里的水果
int sum = 0;
int curFruit = -1, curFruitLoc = 0; //记录当前的水果,和当前水果的起始位置
int subSum = 0;
int j = 0; // 记录篮子起始位置
for (int i = 0; i < fruits.length; ++i) {
if (fruits[i] == basket1 || fruits[i] == basket2)
{
if (fruits[i] != curFruit) {// 记录在篮子里的连续最近,在更换篮子里水果的时候使用
curFruit = fruits[i];
curFruitLoc = i;
}
}
else {
j = curFruitLoc;
curFruitLoc = i;
if (basket1 == curFruit) { // 更新水果篮
basket2 = fruits[i];
curFruit = basket2;
}
else {
basket1 = fruits[i];
curFruit = basket1;
}
}
subSum = (i - j + 1); // 计算最长子序列
sum = sum > subSum ? sum : subSum;
}
return sum;
}
}
思路:
模拟过程,思路都在代码里了,不懂的就看代码随想录中的思路,只能说里面有些地方的想法实在是精妙。
class Solution {
public int[][] generateMatrix(int n) {
//创建一个返回结果的二维数组
int[][] arr = new int[n][n];
int loop = n / 2; //定义循环次数,其实就是定义一共要螺旋多少圈数,这个没考虑到的话真的就很难写了
//定义每一个圈圈的起始位置,因为我们是按一圈一圈来旋转的
int startX = 0;
int startY = 0;
//要用来给二维数组赋值用的
int count = 1;
//每一圈循环,控制每一条边的遍历长度
int offset = 1;
//mid值用来判断这个n是奇数还是偶数
//如果为奇数,则中间会多出一个位置单独成圈(自己可以画一个偶数的看一下)
//如果为偶数那没事儿了,中间正好就可以构成一个回路
int mid = n / 2;
//下面开始循环处理(按圈循环)
while(loop > 0){ //循环圈数
int i = startX;
int j = startY; //startX与startY都要全程记录,所以我们需要用两个临时变量来代替
//先执行从左到右的遍历
for(;j < startY + n - offset;j++){//这里的判断条件是点睛之笔,得地好好品味
arr[startX][j] = count;
count++;
}
//再执行从上到下
for(;i < startX + n - offset;i++){//其实startX完全可以写成startY也行,因为这俩值一直都是一样的
arr[i][j] = count;
count++;
}
//再执行从右到左
for(;j > startY;j--){
arr[i][j] = count;
count++;
}
//最后执行从下到上
for(;i > startX;i--){
arr[i][j] = count;
count++;
}
//每一圈转完,startX和startY都要移动到内圈中进行下一次的转圈
startX++;
startY++;
//转一圈,总的要转的圈数就少一圈
loop--;
//转一圈,边长偏移量也要改变
//每转一圈下一圈就要减少2个数,即偏移量就少2
//而下面我们写成+2是因为我们上面循环中偏移量是作的被减数
offset = offset + 2;
}
//退出循环时,遍历已经结束
//这里还要再判断一个东西就是
//n的奇偶性,如果为偶数那么没有问题,上面的就能全部遍历完
//但如果为奇数,则中间还会剩一个位置
if(n % 2 != 0){
arr[mid][mid] = count;//count经过循环中的处理已经到达最后一个值了
}
return arr;
}
}
思路都在代码里了,这种螺旋矩阵题目都是把矩阵给看成一层一层一圈一圈的来做就好
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
//返回结果集合
List<Integer> list = new ArrayList<>();
int hang = matrix.length;//获得二维数组的行数
int lie = matrix[0].length; //获得二维数组的列数
//将这个矩形按照四个角分出来,按层遍历输出
int top = 0; //(top,left)左上角
int left = 0; //(top,right)右上角
int bottom = hang - 1; //(bottom,left)左下角
int right = lie - 1; //(bottom,right)右下角
//遍历搜索
while(top <= bottom && left <= right){
//从左往右
for(int i=left;i <= right;i++){
list.add(matrix[top][i]);
}
//从上往下
for(int i=top+1;i <= bottom;i++){//top+1是因为要跳到第二行去
list.add(matrix[i][right]);
}
if(left < right && top < bottom){//要排除掉left==right且top==bottom的情况
//因为这种情况出现会造成数据重复记录
//比如输入数据为[[3],[2]]时,不排除相等情况则输出就为[3,2,2]
//从右往左
for(int i=right-1;i > left;i--){
list.add(matrix[bottom][i]);
}
//从下往上
for(int i=bottom;i > top;i--){
list.add(matrix[i][left]);
}
}
//每次经过一圈,范围缩小1
top++;
left++;
bottom--;
right--;
}
return list;
}
}