每日算法练习
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));
}
测试结果:
算法练习极大的锻炼了我的思维能力,也让我复习了一些之前的知识,还需继续努力!
以上是关于每日算法练习的主要内容,如果未能解决你的问题,请参考以下文章