✨个人主页: 不漫游-CSDN博客
前言
本篇文章是对平常练习遇到的问题总结,多吸取经验教训才能避免未来再犯~
Java语法部分
(一)多态
思考:这道题很明显考察的是多态的知识点,即一个对象可以被赋值给其父类的引用,这样可以通过父类的引用调用子类的方法。
读题就可以知道B是A的子类,C是B的子类,**但同样也还是A的子类,**记住这点答案就显而易见了,选的是D~
(二)继承
思考:这道题是关于继承的知识,在类 Alpha 中,继承了类 Base,在 main 方法中,由于 Alpha 是 Base 的子类,因此在创建 Alpha 对象时,会隐含super关键字
首先创建一个 Alpha 对象 new Alpha(),此时会先执行 Base 类的构造方法,输出"Base",然后执行 Alpha 类的构造方法,但没有输出内容。
接着创建一个 Base 对象 new Base(),此时只会执行 Base 类的构造方法,输出"Base"。
所以最后结果就是输出BaseBase。
(三)字符串的特性
思考: 这道题很容易出错,首先分析代码
首先创建 Example 对象 ex,并初始化了字符串 str 和字符数组 ch。
在 change 方法内部将 str 修改为"test ok",但是这种修改只作用于方法内部,并不会影响到原始对象 ex.str 的值,因为Java中字符串属于不可变对象,所以修改后会在 change 方法结束后被丢弃。
另一方面,修改了字符数组 ch 的第一个元素为 'g',这种修改会影响到原始对象 ex.ch 的内容,因为字符数组是可变对象。所以最终输出是 "good and gbc"。
编程题
(一)字符集合
思考:这道题仔细看题不难发现就是要去重即可,既如此,那便可以记录字母出现的次数,如果出现次数是0次,那就是新字母,那就存起来。
可是这道题很容易忽略字符串String的特性-->不可修改性
因此这里就要创建一个可变字符串类型对象才可以实现,画图理解就更好
代码实现就是这样了: 因为要区分大小写,所以创建的是128长的字符数组,最大的ASCII 值
import java.util.Scanner;
public class Main {
public static String func(String s) {
int ret = 0;
// 创建一个长度为128的字符数组,用于记录每个字符是否出现过
char[] arr = new char[128];
// 创建一个可变字符串对象,用于存储处理后的字符
StringBuilder str = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
// 如果字符在数组中对应位置值为0,表示该字符尚未出现过
if (arr[s.charAt(i)] == 0) {
str.append(s.charAt(i));//追加字符
// 将字符在数组中对应位置标记为已出现
arr[s.charAt(i)]++;
}
}
// 将可变字符串转换为不可变字符串并返回
return str.toString();
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextLine()) {
System.out.println(func(in.nextLine()));
}
}
}
(二)合并两个有序数组
思考:这道题要思考怎么合并,合并后怎么排序。当然这是直观思路,
但仔细观察,num1和num2本身就是有序的,并且num1的数组长度是m+n,
这样一来,可以利用两个指针分别指向两个数组的末尾,然后从后向前遍历两个数组,比较它们的元素大小,并将较大的元素放入合并后的数组的末尾。
搭配动图就更好理解了~
代码实现便是下面:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = m - 1; //指向nums1数组的末尾
int p2 = n - 1; //指向nums2数组的末尾
int p = m + n - 1; //指向合并后数组的末尾
// 从后向前遍历两个数组,将较大的元素放入合并后数组的末尾
while (p2 >= 0) {
//如果nums1数组的元素较大,将其放入合并后数组的末尾
if (p1 >= 0 && nums1[p1] > nums2[p2]) {
nums1[p] = nums1[p1];
p1--;
} else {
//num2数组同理
nums1[p] = nums2[p2];
p2--;
}
p--; //更新合并后数组的指针位置
}
}
}
数据结构部分
(一)链表
思考:选C
这道题没有考虑周到,一般情况下,在不带头结点的单链表存储队列中,进行出队操作时,要移除队头元素,因此需要修改队头指针。
但如果移除后队列为空,则需要同时修改队尾指针为空。因此,在进行出队操作时,队头 、队尾指针都可能需要被修改。
(二)堆排序
思考:要注意是 **排升序要建大堆,排降序建小堆,**别记混了~选C
(三)子序列问题
思考:这道题是匹配类型的问题,用栈是比较适合的,可以先将字符串S入栈,在遍历T的过程中与栈顶元素比较,如果相同则出栈,最后倘若栈空,则就是子序列了
import java.util.*;
public class Solution {
/**
*
* S字符先后顺序和T中一致
*
* @param S string字符串
* @param T string字符串
* @return bool布尔型
*/
public boolean isSubsequence (String S, String T) {
if (S.equals(T)) {
return true;
}
Stack<Character> stack = new Stack<>();
for (int i = S.length() - 1; i >= 0; i--) {
stack.push(S.charAt(i));
}
for (int i = 0; i < T.length(); i++) {
if (stack.isEmpty()) {
return true; //如果栈为空,说明S已经全部匹配完成,即S是T的子序列
}
if (stack.peek() == T.charAt(i)) {
stack.pop();
}
}
return stack.isEmpty(); //判断最后栈是否为空,如果为空则S是T的子序列,否则不是
}
}
(四)点击消除问题
思考:这道题用栈去写会比较适合,思路和判断子序列大差不差,但要注意
最后输出的是正序,但用栈最后输出的是逆序,所以在遍历字符串的时候可以从后遍历更加方便
画图理解
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
String s = in.next(); //获取输入的字符串
Stack<Character> stack = new Stack<>();
// 遍历输入的字符串
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i); //获取当前字符
if (stack.isEmpty()) {
stack.push(c); //入栈
} else if (stack.peek() == c) { //如果当前字符与栈顶字符相同
stack.pop(); //移除栈顶字符
} else {
stack.push(c);
}
}
if (stack.isEmpty()) { //如果栈为空
System.out.print(0); //输出0,表示所有字符都成对出现
} else {
// 遍历栈中剩余的字符并输出
for (int i = 0; i < stack.size(); i++) {
System.out.print(stack.get(i));
}
}
}
}
}
其实这些题目难度不算很大,但错了就是错了,还是要多总结,多复习才是,尤其是字符串相关的知识在OJ题中相当常见,所以还是要熟练掌握才行~
看到最后,如果觉得文章写得还不错,希望可以给我点个小小的赞,您的支持是我更新的最大动力