每日算法练习

Posted 没谱的曲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日算法练习相关的知识,希望对你有一定的参考价值。

1、Z字型变换

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。

请你实现这个将字符串进行指定行数变换的函数:string convert(string s, int numRows);

示例 1:
输入: s = “LEETCODEISHIRING”, numRows = 3
输出: “LCIRETOESIIGEDHN”

示例 2:
输入: s = “LEETCODEISHIRING”, numRows = 4
输出: “LDREOEIIECIHNTSG”
解释:

解析:

该题中只要求输出排列后重新从左到右读取的字符内容,我们可以先通过输入的行数创建一个对应的数组(也可以用List集合)来存放每一行的内容,并将数组中的元素置空

String[] arr = new String[numRows];//创建一个数组存放字符串
for (int i = 0; i < numRows; i++){//分numRows行,就将数组中的元素置空
            arr[i] = "";
        }

创建好了数组之后,最关键的问题就来了——我们要如何才能将每一行对应的内容存入到对应的行呢?
当我们读取输入的字符串的时候,我们可以每读取一个字符就将其拼接到当前的行,然后读取下一个字符的时候我们将其与下一行的数组内容进行拼接,直到存放到最大行数,我们将其进行一个向上的回弹,再重复前面的工作,到第一行的时候我们再将其进行回弹,往下存放,如此往复,直到整个字符串都被存放完毕。
思路清楚以后,那就到了通过代码实现的时候了:

int index = 0;//默认数组下标
        int sign = 1;//设置一个标记为1

		//遍历字符串的每一位,使用toCharArray()方法,
		//将每一个字符对应在哪一行存入到相应的元素中拼接起来
        for (char c : s.toCharArray()) {
            arr[index] += c;//将当前字符与数组中已存放的字符串进行拼接
            index += sign;//数组下标增加或减小
            if (index == numRows - 1 || index == 0)//判断是否到达数组最后一位或第一位
                sign = -sign;//将标记翻转实现回弹操作
        }

最后我们将整个数组的每一位顺序拼接起来:

String result = "";
        for (int i = 0; i < numRows; i++) {//将每一个元素拼接起来,获得最后的字符串
            result += arr[i];
        }

整个题目完成后的代码如下:

public String convert(String s, int numRows) {

        if (s == null || s.length() == 0 || numRows == 1 || numRows>s.length()) {//判断字符串为空,字符串长度为0,给定行数为1,则直接输出
            return s;
        }

        if (numRows < 1) {//判断给定行数小于1,不存在这种情况,返回提示信息
            return "行数不能小于1";
        }

        String[] arr = new String[numRows];//创建一个数组存放字符串
        for (int i = 0; i < numRows; i++){//分numRows行,就将数组中的元素置空
            arr[i] = "";
        }

        int index = 0;//默认数组下标
        int sign = 1;//设置一个标记为1

        for (char c : s.toCharArray()) {//遍历字符串的每一位,使用toCharArray()方法,将每一个字符对应在哪一行存入到相应的元素中拼接起来
            arr[index] += c;//将当前字符与数组中已存放的字符串进行拼接
            index += sign;//数组下标增加或减小
            if (index == numRows - 1 || index == 0)//判断是否到达数组最后一位或第一位
                sign = -sign;//将标记翻转实现回弹操作
        }

        String result = "";
        for (int i = 0; i < numRows; i++) {//将每一个元素拼接起来,获得最后的字符串
            result += arr[i];
        }

        return result;
    }

我们来调用一下这个方法试一下:

public static void main(String[] args) {
        ZArray zArray = new ZArray();
        System.out.println(zArray.convert("弃昨乱日我日我之心多者日去不今烦日可者留之忧", 4));
    }

测试结果:

2、字符流中第一个只出现一次的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

解析:

刚看到这个题的时候我考虑了很多种方法,最后通过提示,使用了map实现了这个算法
map可以存放一个唯一的key和记录key内容的value
我们可以先将每一位读出来的字符存放到一个key中作为键,之后每次出现这个字符我们就将该字符的value加一

LinkedHashMap<Character, Integer> map = new LinkedHashMap();//存放字符和其出现的次数
for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);//将字符串中的字符按位读取出来

            //判断map中是否已经包含要插入的字符,如果已经包含则将其次数+1
            if (map.containsKey(ch)) {
                map.put(ch, map.get(ch) + 1);//map.get(ch)是根据key得到value的值
            } else {
                map.put(ch, 1);//如果map中还没有包含,则把ch放入map中,并将其值设为1;
            }
        }

最后我们从前往后遍历整个map,第一个value为1的key就是该字符流中第一个只出现了一次的字符

char firstOne = ' ';
        for(char key: map.keySet()){//遍历map的键
            if (map.get(key) == 1) {//如果键对应的值为1将值赋给firstOne
                firstOne = key;
                break;
            }
        }
        return firstOne;

整个算法完成后的代码如下:

public class FirstNotRepeat {
    LinkedHashMap<Character, Integer> map = new LinkedHashMap();//存放字符和其出现的次数

    public char insert(String s) {
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);//将字符串中的字符按位读取出来

            //判断map中是否已经包含要插入的字符,如果已经包含则将其次数+1
            if (map.containsKey(ch)) {
                map.put(ch, map.get(ch) + 1);//map.get(ch)是根据key得到value的值
            } else {
                map.put(ch, 1);//如果map中还没有包含,则把ch放入map中,并将其值设为1;
            }
        }
        char firstOne = ' ';
        for(char key: map.keySet()){//遍历map的键
            if (map.get(key) == 1) {//如果键对应的值为1将值赋给firstOne
                firstOne = key;
                break;
            }
        }
        return firstOne;
    }
}

我们来调用一下这个方法试一下:

public static void main(String[] args) {
        FirstNotRepeat firstNotRepeat = new FirstNotRepeat();
        System.out.println(firstNotRepeat.insert("google"));
    }

测试结果:

3、盛最多水的容器(双指针、数组)

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器。

输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

解析:

该题可以直接通过暴力破解,通过遍历计算每一个数对应所有的容器能装多少水,取其中的最大值,但是这样计算不论是时间复杂度还是空间复杂度都太高了。
我们可以设置两个指针分别指向数组中的第一位和最后一位

		int i = 0;//定义开始位置的数组下标
        int j = height.length - 1;//定义数组最后一位的下标

我们可以得知,可以容纳水的决定因素为两条边中较短的一条边,我们通过对比两个指针的大小和下标的差值,计算其面积,然后将较短的那条边的下标向前或向后移动,再去和新的边进行计算,最后计算出的容积再和之前计算的最大的容积进行比较,保留较大的:

		int res = 0;//最后获得的最大面积
        while (i < j) {
            res=Math.max(res,Math.min(height[i],height[j])*(j-i));//面积等于两条边中短的那条×两条边的数组下标之差,取其中的最大一个为最后获取的最大面积
            if (height[i] < height[j]) {//比较两条边那一条比较短,短的那一条进行移位
                ++i;
            } else {
                --j;
            }
        }

通过上述算法可以节省出大量的时间和空间,将整个算法完成后如下:

public class MostWater {
    public int maxArea(int[] height) {
        if (height.length <= 1) {//判断数组长度小于等于1,则不需要进行运算也能得出面积为0(该句可省略)
            return 0;
        }
        int i = 0;//定义开始位置的数组下标
        int j = height.length - 1;//定义数组最后一位的下标
        int res = 0;//最后获得的最大面积
        while (i < j) {
            res=Math.max(res,Math.min(height[i],height[j])*(j-i));//面积等于两条边中短的那条×两条边的数组下标之差,取其中的最大一个为最后获取的最大面积
            if (height[i] < height[j]) {//比较两条边那一条比较短,短的那一条进行移位
                ++i;
            } else {
                --j;
            }
        }
        return res;
    }
}

我们来调用一下这个方法进行测试:

public static void main(String[] args) {
        MostWater mostWater = new MostWater();
        int[] height = {1,8,6,2,5,4,8,3,7};
        System.out.println(mostWater.maxArea(height));
    }

测试结果:

算法练习极大的锻炼了我的思维能力,也让我复习了一些之前的知识,还需继续努力!

以上是关于每日算法练习的主要内容,如果未能解决你的问题,请参考以下文章

每日算法练习(2020-1-11)

每日算法练习(2020-1-27)

每日算法练习

每日算法练习

每日算法练习

每日算法练习