数组题目总结 -- 花式遍历

一. 反转字符串中的单词

思路和代码:

I. 博主的做法

  • 先用trim()方法把字符串前后的多余空格全部去掉。
  • 再用replaceAll(“\\s+”, " ");把多个空格替换成一个空格,\s代表空格,+代表多个。
  • 用split以空格为标志,切分字符串,放到字符串数组中。
  • 之后使用StringBuffer反向存储。
class Solution {
    public String reverseWords(String s) {
        s = s.trim().replaceAll("\\s+", " ");
        String[] temp = s.split(" ");
        StringBuffer stb = new StringBuffer();

        for(int i = temp.length-1; i > 0; i--){
            stb.append(temp[i] + " ");
        }
        stb.append(temp[0]);
        
        return stb.toString();
    }
}
  • 申请了额外的空间,原地反转,博主不会,,,
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

II. 东哥的做法

  • 先反转整个字符串。
  • 然后反转每一个单词。
    在这里插入图片描述
class Solution {
    public StringBuilder trimSpace(String s){
        int left = 0, right = s.length()-1;
		//去除开头和结尾的空格
        while(left <= right && s.charAt(left) == ' ')
            left++;
        while(left <= right && s.charAt(right) == ' ')
            right--;
            
		//去除字符串中间的空格
        StringBuilder stb1 = new StringBuilder();
        while(left <= right){

            if(s.charAt(left) != ' ')
                stb1.append(s.charAt(left));
            else if(stb1.charAt(stb1.length()-1) != ' ')
                stb1.append(s.charAt(left));
            
            left++;
        }
        return stb1;
    }
    
    //写的挺巧妙的反转函数,可以积累
    public void reverse(StringBuilder stb, int left, int right){
        while (left < right) {
            char temp = stb.charAt(left);
            stb.setCharAt(left++, stb.charAt(right));   
            stb.setCharAt(right--, temp);
        }

    }
    public void reverseWord(StringBuilder stb){
        int start = 0, end = 0;

        while(start < stb.length()){
            while(end < stb.length() && stb.charAt(end) != ' ')
                end++;
            
            reverse(stb, start, end-1);
            //寻找下一个单词
            start = end + 1;
            end++;
        } 

    }
    
    //总函数
    public String reverseWords(String s) {

        StringBuilder stb = trimSpace(s);
        
		//StringBuilder 不用再加返回值,直接就在原地操作了
        reverse(stb, 0, stb.length()-1);

        reverseWord(stb);

        return stb.toString();
    }
}
  • StringBuilder一定要定义在循环外面,循环里面的属于临时变量,外面的函数是调用不了的。
  • 如果是StringBuilder,就不用再有返回值,因为StringBuilder是可变的,而java中String是不可变的,需要额外申请空间进行操作。C++中,String是可变的,所以空间复杂度能降到O(1),不需要额外申请空间。
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

III. 其他做法1

  • 去除开头和末尾的空格。
  • 运用正则表达式将字符串分成一个一个的单词(用一个或者多个空格当做分隔符),返回的数组转换成List。
  • 反转List,相当于将单词的顺序做一个反转。
  • 将空格加入到List当中。
class Solution {
    public String reverseWords(String s) {
        // 除去开头和末尾的空白字符
        s = s.trim();
        // 正则匹配连续的空白字符作为分隔符分割
        List<String> wordList = Arrays.asList(s.split("\\s+"));
        Collections.reverse(wordList);
        return String.join(" ", wordList);
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
补充知识点:
  • 在Java 8及以上版本中,String.join()方法最后一个参数可以传入任何对象类型的可迭代集合.
  • 可迭代集合就是实现Iterable接口的集合,看下图:
    在这里插入图片描述
  • 需要注意的是:Java中数组也实现了Iterable接口,也就是,数组也可以通过join方法,返回字符串

IV. 其他做法2

  • 去掉头尾两头的空格
  • 将字符串加入双端队列的头部或者直接加入栈中,如下图:
    在这里插入图片描述
class Solution {
    public String reverseWords(String s) {
        int left = 0, right = s.length()-1;
		//去掉前后两头的空格
        while(left <= right && s.charAt(left) == ' ')
            left++;
        while(left <= right && s.charAt(right) == ' ')
            right--;
        //使用双端队列存字符串,使用StringBuilder来存单词
        Deque<String> deque = new ArrayDeque<>();
        StringBuilder stb = new StringBuilder();

        while(left <= right){
        	//如果StringBuilder中不为空(单词存在),并且遇到下一个空格了,就从头部加入双端队列并清空StringBuilder
            if(stb.length() != 0 && s.charAt(left) == ' '){
                deque.offerFirst(stb.toString());
                stb.setLength(0);
            }
            else if(s.charAt(left) != ' ')
                stb.append(s.charAt(left));

            left++;
        }
        //记得加入最后一个单词,因为遇不到下一个空格了!!!   
        deque.offerFirst(stb.toString());
		
        return String.join(" ", deque);
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

二. 旋转图像

思路和代码:

I. 博主的做法

  • 博主只能想到一圈一圈的进行迭代数组。。。太复杂了。

II. 东哥的做法

  • 先将矩阵沿对角线,做对称矩阵操作

在这里插入图片描述

  • 对矩阵的每一行进行反转
    在这里插入图片描述
    在这里插入图片描述
class Solution {
	//以对角线对称交换矩阵
    public void symmetry(int[][] matrix){
        for(int i = 0; i < matrix.length; i++)
            for(int j = 0; j < i; j++){
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
    }
    
    //将每一行进行反转
    public void reverse(int[] num){
        int left = 0, right = num.length-1;
        while(left <= right){
            int temp = num[left];
            num[left++] = num[right];
            num[right--] = temp;
        }

    }
    public void rotate(int[][] matrix) {
        symmetry(matrix);

        for(int[] n : matrix)
            reverse(n);
             
    }
}
  • 常规的思路就是去寻找原始坐标和旋转后坐标的映射规律,但我们是否可以让思维跳跃跳跃,尝试把矩阵进行反转、镜像对称等操作,可能会出现新的突破口。
  • 仔细想想,旋转二维矩阵的难点在于将「行」变成「列」,将「列」变成「行」,而只有按照对角线的对称操作是可以轻松完成这一点的,对称操作之后就很容易发现规律了。

三. 旋转图像(逆时针旋转90°)

  • 题目链接:无。
  • 函数名: public void rotate(int[][] matrix){ }

思路和代码:

I. 博主和东哥的做法

  • 和上一道题真的很像,就是沿着另一条对角线旋转,然后反转每一行。
    在这里插入图片描述
package 洛谷;

import java.util.Scanner;

public class Test {
	//沿逆对角线进行对称
    public static void romate(int[][] matrix){
        for(int i = 0; i < matrix.length; i++)
            for(int j = 0; j < matrix.length-i; j++){
                int temp = matrix[i][j];
                matrix[i][j] = matrix[matrix.length - 1 - j][matrix.length - 1 - i];
                matrix[matrix.length - 1 - j][matrix.length - 1 - i] = temp;
            }
    }

	//每行进行反转
    public static void reverse(int[] matrix){
        int left = 0, right = matrix.length-1;

        while(left <= right){
            int temp = matrix[left];
            matrix[left++] = matrix[right];
            matrix[right--] = temp;
        }
    }
    
    //主函数
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int[][] a = {{1,2,3,4}, {5,6,7,8}, {7,6,5,4},{4,1,2,3}};

        romate(a);

        for(int[] num : a)
            reverse(num);

        for(int i = 0; i < a.length; i++){
            for(int j = 0; j < a.length; j++)
                System.out.print(a[i][j] + " ");
            System.out.println();
        }

    }

}

  • 顺逆对角线对称的逻辑还是不太会,仍需加强

四. 矩阵的螺旋遍历

思路和代码:

I. 博主的做法

  • 设置4个边界,然后模拟顺序输出的样子,进行遍历
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        int top = 0, left = 0, right = matrix[0].length-1, bottom = matrix.length-1;
        
        List<Integer> list = new ArrayList<>();

        while(left <= right && top <= bottom){
            for(int j = left; top <= bottom && j <= right; j++)
                list.add(matrix[top][j]);
            top++;

            for(int i = top; left <= right && i <= bottom; i++)
                list.add(matrix[i][right]);
            right--;

            for(int j = right; top <= bottom && j >= left; j--)
                list.add(matrix[bottom][j]);
            bottom--;

            for(int i = bottom; left <= right && i >= top; i--)
                list.add(matrix[i][left]);
            left++;
        }

        return list;
    }
}
  • 这一行写成:for(int j = left; left <= right && j <= right; j++),这显然是错的,j <= right已经判断过了;其次,如果上下都是负的空间,左右又有什么意义呢??
  • 还需要注意,螺旋输出,没拐个弯,对应的边界就要多走一格子。
  • 列的个数一定是matrix[0].length - 1,行的个数是matrix.length

II. 东哥的做法

  • 和博主想的一样,设置四个边界
List<Integer> spiralOrder(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length;
    int upper_bound = 0, lower_bound = m - 1;
    int left_bound = 0, right_bound = n - 1;
    List<Integer> res = new LinkedList<>();
    // res.size() == m * n 则遍历完整个数组
    while (res.size() < m * n) {
        if (upper_bound <= lower_bound) {
            // 在顶部从左向右遍历
            for (int j = left_bound; j <= right_bound; j++) {
                res.add(matrix[upper_bound][j]);
            }
            // 上边界下移
            upper_bound++;
        }
        
        if (left_bound <= right_bound) {
            // 在右侧从上向下遍历
            for (int i = upper_bound; i <= lower_bound; i++) {
                res.add(matrix[i][right_bound]);
            }
            // 右边界左移
            right_bound--;
        }
        
        if (upper_bound <= lower_bound) {
            // 在底部从右向左遍历
            for (int j = right_bound; j >= left_bound; j--) {
                res.add(matrix[lower_bound][j]);
            }
            // 下边界上移
            lower_bound--;
        }
        
        if (left_bound <= right_bound) {
            // 在左侧从下向上遍历
            for (int i = lower_bound; i >= upper_bound; i--) {
                res.add(matrix[i][left_bound]);
            }
            // 左边界右移
            left_bound++;
        }
    }
    return res;
}

  • 思路是一样的,大家看着哪个顺眼参考哪个

五. 构建螺旋矩阵

思路和代码:

I. 博主的做法

  • 跟上个题几乎是一模一样,只是在每次循环当中进行的操作不同而已。
class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];

        int top = 0, left = 0, right = n-1, bottom = n-1;
        int num = 1;

        while(top <= bottom && left <= right){
            for(int j = left; top <= bottom && j <= right; j++)
            	//这里不一样,下面同理
                matrix[top][j] = num++;
            top++;

            for(int i = top; left <= right && i <= bottom; i++)
                matrix[i][right] = num++;
            right--;

            for(int j = right; top <= bottom && j >= left; j--)
                matrix[bottom][j] = num++;
            bottom--;

            for(int i = bottom; left <= right && i >= top; i--)
                matrix[i][left] = num++;
            left++;
        }

        return matrix;
    }
}
  • 时间复杂度:O(n^2),其中 n 是给定的正整数。矩阵的大小是 n×n,需要填入矩阵中的每个元素。
  • 空间复杂度:O(1),除了返回的矩阵以外,空间复杂度是常数。

II. 东哥的做法

  • 和博主想的一样,设置四个边界
int[][] generateMatrix(int n) {
    int[][] matrix = new int[n][n];
    int upper_bound = 0, lower_bound = n - 1;
    int left_bound = 0, right_bound = n - 1;
    // 需要填入矩阵的数字
    int num = 1;
    
    while (num <= n * n) {
        if (upper_bound <= lower_bound) {
            // 在顶部从左向右遍历
            for (int j = left_bound; j <= right_bound; j++) {
                matrix[upper_bound][j] = num++;
            }
            // 上边界下移
            upper_bound++;
        }
        
        if (left_bound <= right_bound) {
            // 在右侧从上向下遍历
            for (int i = upper_bound; i <= lower_bound; i++) {
                matrix[i][right_bound] = num++;
            }
            // 右边界左移
            right_bound--;
        }
        
        if (upper_bound <= lower_bound) {
            // 在底部从右向左遍历
            for (int j = right_bound; j >= left_bound; j--) {
                matrix[lower_bound][j] = num++;
            }
            // 下边界上移
            lower_bound--;
        }
        
        if (left_bound <= right_bound) {
            // 在左侧从下向上遍历
            for (int i = lower_bound; i >= upper_bound; i--) {
                matrix[i][left_bound] = num++;
            }
            // 左边界右移
            left_bound++;
        }
    }
    return matrix;
}

参考:
https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/er-wei-shu-150fb/
https://leetcode.cn/problems/reverse-words-in-a-string/solution/fan-zhuan-zi-fu-chuan-li-de-dan-ci-by-leetcode-sol/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值