3.19
链表
相交链表
题目:给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
双指针
假设有两个链表,A 和 B,它们在某个节点相交。
链表A: a1 → a2 ↘ c1 → c2 → c3 ↗ 链表B: b1 → b2 → b3 当curA走到链表A的末尾时,接下来让它从链表B的头节点开始走。 当curB走到链表B的末尾时,接下来让它从链表A的头节点开始走。 curA = curA == null ? headB : curA.next; curB = curB == null ? headA : curB.next; 对于curA: 首先遍历链表A: a1 → a2 → c1 → c2 → c3 然后,当curA到达链表A的末尾,根据代码中的逻辑,它接着会从链表B的头节点开始继续遍历:b1 → b2 → b3 最后,进入共享部分:c1 → c2 → c3 因此,curA的完整路径是:a1 → a2 → c1 → c2 → c3 → b1 → b2 → b3 → c1 对于curB: 首先遍历链表B: b1 → b2 → b3 → c1 → c2 → c3 然后,当curB到达链表B的末尾,它接着会从链表A的头节点开始继续遍历:a1 → a2 最后,进入共享部分:c1 → c2 → c3 因此,curB的完整路径是:b1 → b2 → b3 → c1 → c2 → c3 → a1 → a2 → c1 正确的理解应该是:curA和curB会分别遍历完整个链表A和B(包括各自独有的部分和共享的部分),然后跳到另一个链表的开始继续遍历,直到它们相遇或同时到达末尾(null)。
这意味着:
-
如果
curA
变为null
(意味着已经走到链表A的末尾),那么就让它从链表B的头节点开始走。 -
如果
curB
变为null
(意味着已经走到链表B的末尾),那么就让它从链表A的头节点开始走。
public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { // 如果任一链表为空,则直接返回null,因为空链表不可能与其他链表相交 if(headA == null || headB == null){ return null; } // 初始化两个指针,分别指向两个链表的头节点 ListNode curA = headA, curB = headB; // 继续遍历直到两个指针相遇,即找到相交节点 // 如果两个链表相交,那么curA和curB最终将指向相交节点 // 如果不相交,那么curA和curB最终都会同时为null,因为它们会遍历完两个链表的总长度 while(curA != curB){ // 如果curA到达链表末尾,则跳转到链表B的头部继续遍历 // 这样做是为了消除两个链表长度的差异 curA = curA == null ? headB : curA.next; // 如果curB到达链表末尾,则跳转到链表A的头部继续遍历 // 同上,这也是为了消除两个链表长度的差异 curB = curB == null ? headA : curB.next; } // 当curA == curB时,循环结束 // 如果两个链表相交,curA(或curB)就是相交的起始节点 // 如果不相交,此时curA和curB都为null,函数返回null,表示没有交点 return curA; } }
Hash方法
简单,但是慢
public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { Set<ListNode> visited = new HashSet<>(); ListNode cur = headA; while(cur != null){ visited.add(cur); cur = cur.next; } ListNode temp = headB; while(temp!= null){ if(visited.contains(temp)){ return temp; } temp = temp.next; } return null; } }
链表对齐
public class Solution { public int getLength(ListNode cur){ int len = 0; while(cur != null){ len++; cur = cur.next; } return len; } public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode curA = headA; ListNode curB = headB; int lenA = 0; int lenB = 0; lenA = getLength(curA); lenB = getLength(curB); int move_length = Math.abs(lenA-lenB); ListNode curLong = null; ListNode curShort = null; if(lenA>=lenB){ curLong = headA; curShort = headB; }else if(lenA<lenB){ curLong = headB; curShort = headA; } for(int i=0;i<move_length;i++){ curLong = curLong.next; } while(curLong != null && curShort != null) { if(curLong == curShort) { return curLong; // 当curLong和curShort两个指针在循环中相遇时,表示它们到达了相交的起始节点,这时函数会立即返回当前的节点。 } curLong = curLong.next; curShort = curShort.next; } return null; // 循环结束,未找到相交节点,返回null } }
合并两个有序链表
题目:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
-
// 使用虚拟头节点简化实现 ListNode preHead = new ListNode(-1);
-
// 移动pre到合并链表的最后一个节点 pre = pre.next;
-
// 处理剩余的元素 pre.next = (cur1 == null) ? cur2 : cur1;
-
return preHead.next; // 跳过虚拟头节点返回合并后的链表头节点
迭代
class Solution { public ListNode mergeTwoLists(ListNode list1, ListNode list2) { // 使用虚拟头节点简化实现 ListNode preHead = new ListNode(-1); ListNode pre = preHead; ListNode cur1 = list1; ListNode cur2 = list2; // 遍历两个链表直到其中一个遍历完 while(cur1 != null && cur2 != null) { if(cur1.val <= cur2.val) { pre.next = cur1; cur1 = cur1.next; } else { pre.next = cur2; cur2 = cur2.next; } // 移动pre到合并链表的最后一个节点 pre = pre.next; } // 处理剩余的元素 pre.next = (cur1 == null) ? cur2 : cur1; return preHead.next; // 跳过虚拟头节点返回合并后的链表头节点 } }
递归
递归的“魔法”在于它让我们能够将一个复杂问题分解为更小的相同问题。在这个例子中,不管链表的长度如何,我们都是在做同样的事情:比较两个头节点,选择较小的一个,然后移动到下一个节点。递归让我们能够以一种简洁的方式重复这个过程,直到问题被完全解决。
class Solution { public ListNode mergeTwoLists(ListNode l1, ListNode l2) { // 检查l1是否为空,如果是,剩下的都是l2,直接返回l2 if (l1 == null) { return l2; } // 检查l2是否为空,如果是,剩下的都是l1,直接返回l1 else if (l2 == null) { return l1; } // 如果l1的当前节点值小于l2的当前节点值 else if (l1.val < l2.val) { // 将l1的下一个节点指向递归调用mergeTwoLists的结果 // 此调用是为了将l1的下一个节点与l2当前的状态合并 l1.next = mergeTwoLists(l1.next, l2); // 返回l1,因为它是当前合并后链表的头节点 return l1; } // 如果l1的当前节点值不小于l2的当前节点值 else { // 将l2的下一个节点指向递归调用mergeTwoLists的结果 // 此调用是为了将l2的下一个节点与l1当前的状态合并 l2.next = mergeTwoLists(l1, l2.next); // 返回l2,因为它是当前合并后链表的头节点 return l2; } } }
华为机试【字符串】
HJ17 坐标移动
/** * 开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从输入字符串里面读取一些坐标,并将最终输入结果输出到输出文件里面。 * 输入: * 合法坐标为A(或者D或者W或者S) + 数字(两位以内) * 坐标之间以;分隔。 * 非法坐标点需要进行丢弃。如AA10; A1A; $%$; YAD; 等。 */ // 导入Java的Scanner类和HashSet类,分别用于读取输入和存储唯一元素集合 import java.util.Scanner; import java.util.HashSet; // 定义主类Main public class Main { // 程序的入口点 public static void main(String[] args) { // 创建Scanner对象以从标准输入读取数据 Scanner in = new Scanner(System.in); // 使用while循环来不断读取输入直到没有更多输入 while(in.hasNext()){ // 读取一行输入并以分号(;)分割,得到指令数组 String[] indexs = in.nextLine().split(";"); // 定义一个长度为2的整型数组来存储最终的坐标(x,y) int[] res = new int[2]; // 建立一个包含有效方向字符的HashSet集合 HashSet<Character> set = new HashSet<>(4); set.add('A'); // 向左 set.add('S'); // 向下 set.add('D'); // 向右 set.add('W'); // 向上 // 遍历指令数组,处理每个指令 for(String s: indexs){ // 如果指令长度不符合规范(不是2或3),则忽略这个指令 if(s.length() > 3 || s.length() < 2) continue; // 如果第一个字符不是有效的方向字符,忽略这个指令 if(!set.contains(s.charAt(0))) continue; // 如果第二个字符也是方向字符,则忽略这个指令 if(set.contains(s.charAt(1))) continue; // 如果指令长度为3且第三个字符也是方向字符,则忽略这个指令 if(s.length() == 3 && set.contains(s.charAt(2))) continue; // 初始化移动距离为0 int move = 0; // 根据指令长度来确定移动距离 if(s.length() == 2){ // 如果长度为2,只需处理第二个字符 int char1 = s.charAt(1) - '0'; // 检查字符是否为有效数字,如果不是,忽略这个指令 if(char1 < 0 || char1 >9) continue; move = char1; }else if(s.length() == 3){ // 如果长度为3,需要处理第二和第三个字符 int char1 = s.charAt(1) - '0'; int char2 = s.charAt(2) - '0'; // 同样检查每个字符是否为有效数字 if(char1 < 0 || char1 >9) continue; if(char2 < 0 || char2 >9) continue; move = char1 * 10 + char2; // 计算两位数的移动距离 } // 根据方向字符来更新坐标 Character moveDir = s.charAt(0); if(moveDir == 'A'){ res[0] -= move; // 向左移动 }else if(moveDir == 'D'){ res[0] += move; // 向右移动 }else if(moveDir == 'W'){ res[1] += move; // 向上移动 }else if(moveDir == 'S'){ res[1] -= move; // 向下移动 } } // 循环结束后,打印最终的坐标结果 int count = 0; for(int i:res){ System.out.print(i); // 在x坐标后打印一个逗号 if(count == 0){ System.out.print(","); count++; } } System.out.println(); // 最后换行 } } }
// 导入Java的Scanner和HashMap类,分别用于读取标准输入和存储方向与坐标变化的映射 import java.util.Scanner; import java.util.HashMap; public class Main { public static void main(String[] args) { // 创建一个Scanner对象来读取标准输入 Scanner scanner = new Scanner(System.in); // 创建一个HashMap来存储每个方向字符与其对应的坐标变化 // 键是一个方向字符('A', 'D', 'W', 'S'),值是一个长度为2的整数数组,表示在该方向上的坐标变化 HashMap<Character, int[]> directionMap = new HashMap<>(); directionMap.put('A', new int[]{-1, 0}); // 向左移动,x坐标减少1 directionMap.put('D', new int[]{1, 0}); // 向右移动,x坐标增加1 directionMap.put('W', new int[]{0, 1}); // 向上移动,y坐标增加1 directionMap.put('S', new int[]{0, -1}); // 向下移动,y坐标减少1 // 不断读取输入直到没有更多输入 while (scanner.hasNext()) { // 读取一行输入并以分号(";")分割成多个指令 String[] commands = scanner.nextLine().split(";"); // 初始化x和y坐标 int x = 0, y = 0; // 遍历所有指令 for (String cmd : commands) { // 检查指令是否有效 if (isValidCommand(cmd)) { // 获取方向字符 char direction = cmd.charAt(0); // 获取步数,即从第二个字符开始到字符串末尾的部分,并将其转换为整数 int distance = Integer.parseInt(cmd.substring(1)); // 根据方向更新x和y坐标 // directionMap.get(direction)[0] 获取x方向的变化量 // directionMap.get(direction)[1] 获取y方向的变化量 // 然后乘以步数,加到当前坐标上 x += directionMap.get(direction)[0] * distance; y += directionMap.get(direction)[1] * distance; } } // 所有指令处理完毕,打印最终坐标 System.out.println(x + "," + y); } // 关闭Scanner对象 scanner.close(); } // 一个私有静态方法,用于验证指令是否有效 private static boolean isValidCommand(String cmd) { // 检查指令是否为空,或者长度不符合要求(非2或3) if (cmd == null || cmd.length() < 2 || cmd.length() > 3) { return false; } // 获取指令的第一个字符,检查是否为有效的方向字符 char direction = cmd.charAt(0); if (direction != 'A' && direction != 'D' && direction != 'W' && direction != 'S') { return false; } try { // 尝试将指令的第二个字符到最后一个字符转换为整数 // 如果成功,说明步数是有效的数字,返回true Integer.parseInt(cmd.substring(1)); return true; } catch (NumberFormatException e) { // 如果转换失败,说明步数不是有效的数字,返回false return false; } } }
HJ20 密码验证合格程序
-
count.put(1, count.getOrDefault(1, 0) + 1);
-
// 更新用于检查重复子串的三个变量
prev3 = prev2;
prev2 = prev1;
prev1 = String.valueOf(c);
import java.util.HashMap; import java.util.HashSet; import java.util.Scanner; public class Main { public static void main(String[] args) { // 创建一个Scanner对象用于从标准输入读取数据 Scanner scanner = new Scanner(System.in); // 使用while循环持续读取每一行输入,直到没有更多输入 while (scanner.hasNextLine()) { // 读取一行输入,代表一个待验证的密码 String str = scanner.nextLine(); // 初始化一个标志位,用于记录当前密码是否符合所有验证条件 boolean isValid = true; // 首先检查密码长度是否超过8位,并且检查密码中是否包含空格 if (str.length() <= 8 || str.contains(" ")) { // 如果密码长度不超过8位或包含空格,则标记为不合法 isValid = false; } else { // 如果密码长度合法且不包含空格,继续进行更详细的验证 // 使用HashMap记录各种字符类型的出现次数 HashMap<Integer, Integer> count = new HashMap<>(4); // 使用HashSet记录所有出现的三字符子串,用于检测重复子串 HashSet<String> chars = new HashSet<>(str.length()); // 初始化三个字符串变量用于构造和检查连续的三字符子串 String prev3 = ""; String prev2 = ""; String prev1 = ""; // 遍历密码中的每个字符 for (char c : str.toCharArray()) { // 根据字符类型,将其分类并更新到count映射中 if (c >= 'A' && c <= 'Z') { // 大写字母 count.put(1, count.getOrDefault(1, 0) + 1); } else if (c >= 'a' && c <= 'z') { // 小写字母 count.put(2, count.getOrDefault(2, 0) + 1); } else if (c >= '0' && c <= '9') { // 数字 count.put(3, count.getOrDefault(3, 0) + 1); } else { // 其他符号 count.put(4, count.getOrDefault(4, 0) + 1); } // 更新用于检查重复子串的三个变量 prev3 = prev2; prev2 = prev1; prev1 = String.valueOf(c); // 构造当前的三字符子串 String temp = prev3 + prev2 + prev1; // 检查此子串是否已经出现过 if (chars.contains(temp)) { // 如果已经出现过,标记密码为不合法并跳出循环 isValid = false; break; } else { // 如果未出现过,将此子串加入到集合中 chars.add(temp); } } // 检查是否至少包含三种不同类型的字符 if (count.size() < 3) { isValid = false; } } // 最后,根据isValid变量的值输出密码验证结果 if (isValid) System.out.println("OK"); else System.out.println("NG"); } // 关闭Scanner对象 scanner.close(); } }
HJ23 删除字符串中出现次数最少的字符
所有字符出现次数最少的去掉该种字符
-
for(Map.Entry<Character,Integer> entry : map.entrySet()){
-
for
: 这是Java中的一个循环结构,用于迭代遍历集合中的元素。 -
Map.Entry<Character,Integer>
: 这是一个泛型类型,表示Map中的一个键值对条目。在这里,Character
是键的类型,Integer
是值的类型。 -
entry
: 这是循环过程中的一个临时变量名,用于引用每个键值对条目。 -
map.entrySet()
: 这是一个Map的方法,返回一个Set集合,其中包含了Map中的所有键值对条目。
-
package hw; /** * 实现删除字符串中出现次数最少的字符,若出现次数最少的字符有多个,则把出现次数最少的字符都删除。输出删除这些单词后的字符串,字符串中其它字符保持原来的顺序。 * 数据范围:输入的字符串长度满足 1≤n≤20 ,保证输入的字符串中仅出现小写字母 */ import java.util.*; public class HJ23_删除次数最少字符 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()){ String str = scanner.nextLine(); Map<Character,Integer> map = new HashMap<>(); for(Character c: str.toCharArray()){ map.put(c,map.getOrDefault(c,0) + 1); } int min = 20; for(Map.Entry<Character,Integer> entry : map.entrySet()){ min = Math.min(min, entry.getValue()); } // 也可以用 int min = Collections.min(map.values()); /** * 替换字符串也可以用 s = s.replaceAll(String.valueOf(character), ""); */ StringBuilder stringBuilder = new StringBuilder(); for (Character fin : str.toCharArray()){ if(min != map.get(fin)){ stringBuilder.append(fin); } } String res = stringBuilder.toString(); System.out.println(res); } } }
如果是删除连续字符串
package hw; /** * 实现删除字符串中连续出现次数最少的字符,输出删除这些单词后的字符串,字符串中其它字符保持原来的顺序。 * 数据范围:输入的字符串长度满足 1≤n≤20 ,保证输入的字符串中仅出现小写字母 * 例如。输入 assssaa 输出 a 。 输入 aabbcccccnnnnn,处处cccccnnnnn。 输入 aabbcbcccc,输出aabbcccc */ import java.util.*; public class 删除字符串出现次数最少的字符 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { String str = scanner.nextLine(); List<String> sequences = new ArrayList<>(); // 存储所有连续字符序列 List<Integer> counts = new ArrayList<>(); // 存储每个连续字符序列的长度 // 用于构建当前的连续字符序列 StringBuilder tempSeq = new StringBuilder(); char lastChar = str.charAt(0); tempSeq.append(lastChar); // 遍历字符串识别连续字符序列 for (int i = 1; i <= str.length(); i++) { if (i < str.length() && str.charAt(i) == lastChar) { // 如果当前字符与上一个字符相同,则继续构建连续字符序列 tempSeq.append(lastChar); } else { // 序列结束(当前字符与上一个字符不同或已到字符串末尾) sequences.add(tempSeq.toString()); // 将连续字符序列添加到列表 counts.add(tempSeq.length()); // 记录连续字符序列的长度 if (i < str.length()) { // 重置tempSeq以开始新的序列 tempSeq = new StringBuilder("" + str.charAt(i)); lastChar = str.charAt(i); } } } // 确定最小出现次数的连续序列的长度 int minCount = Collections.min(counts); // 构建并输出结果,排除出现次数最少的连续字符序列 StringBuilder result = new StringBuilder(); for (int i = 0; i < sequences.size(); i++) { if (counts.get(i) != minCount) { // 只添加出现次数不是最少的连续字符序列 result.append(sequences.get(i)); } } System.out.println(result); } scanner.close(); } }
HJ33 整数与IP地址间的转换
注意细节
-
注意使用 long
long ipv4 = Long.parseLong(str);
-
注意 length 防止越界
-
正则表达式中的
.
是一个特殊字符,它匹配任意字符。因此,如果你想根据.
来分割字符串,你需要使用转义字符\\
来对.
进行转义,使其被解释为普通的点字符。-
str.contains(".")
:这行代码检查字符串str
是否包含点号(.
)。在这个上下文中,点号是按字面意义来理解的,即它直接检查字符串中是否存在点号字符。 -
str.split("\\.")
:这行代码基于点号将字符串str
分割成一个字符串数组。然而,因为在正则表达式中点号(.
)是一个特殊字符,它代表任何单个字符,因此不能直接用作分隔符。为了指定点号作为分隔符,你需要对它进行转义,使其被解释为字面上的点号。在Java的字符串中,反斜杠(\
)是转义字符,所以你需要使用两个反斜杠(\\
)来表示一个用于正则表达式的反斜杠。因此,"\\."
实际上在正则表达式中代表一个字面上的点号。
-
-
每个点相当于256进制,直接拿256进制转换更方便 // ip2int String[] fields = str.split("\\."); for (int i = 0; i < N; i++) { result = result * 256 + Integer.parseInt(fields[i]); } //int2ip for (int i = 0; i < N; i++) { result = ipv4 % 256 + "." + result; ipv4 /= 256; }
原理解释
在计算机系统中,数字通常以二进制形式存储。当需要将几个较小的数字组合成一个大的数字时,一个常见的方法是通过将这些小数字的二进制表示按顺序排列来实现。
以给定的四个数字10、0、3、193为例,它们分别对应的二进制数是:
10的二进制表示是
00001010
(8位,即一个字节)0的二进制表示是
00000000
3的二进制表示是
00000011
193的二进制表示是
11000001
将这些二进制数按给定顺序排列组合起来,得到的32位二进制数是:
00001010 00000000 00000011 11000001这个32位的二进制数代表了一个整数。要将其转换为十进制,可以使用二进制到十进制的转换规则,即从右向左,每个位的值乘以2的n次方(n从0开始),然后求和。对于这个特定的二进制数,计算过程如下:
从右向左第一个1是2^0位,值为1
第二个1是2^7位,值为128
第一个0是2^8位,值为0
第二个1是2^16位,值为65536
第三个1是2^24位,值为16777216
将这些值相加,得到的总和是167773121,这就是这个32位二进制数对应的十进制数。
十进制转二进制
StringBuilder binary = new StringBuilder(); while (decimal > 0) { int remainder = decimal % 2; binary.insert(0, remainder); // Insert the remainder at the beginning decimal /= 2; }
ip转换为int
原理
IPv4地址由四个十进制数构成,每个数位于0到255之间,格式通常为
a.b.c.d
。为了将这样的地址转换为一个长整数,我们可以将每个数看作是一个在256进制下的数位。为什么使用256?因为每部分(每个点分隔的数)最大可以达到255,即它们是基于256的数位(从0开始计数,所以是256个可能的值)。
转换过程
转换过程遵循从左到右(从高位到低位)的顺序,每一步都将当前累计的值乘以256(即左移8位,因为每个IP段占8位),然后加上当前段的数值。具体步骤如下:
初始化总和
sum
为0:开始时,没有任何输入被处理,因此初始总和为0。遍历每个IP段:对于IP地址的每个部分(a、b、c、d):
将当前段的数值
num
转换为整数。将当前总和
sum
乘以256,然后加上这个数值。这相当于在256进制下将当前数值添加到总和中。输出总和:所有段处理完毕后,
sum
包含了IP地址转换后的长整数表示。示例
假设有IP地址
10.0.3.193
,转换过程如下:
开始:
sum = 0
处理10:
sum = 0 * 256 + 10 = 10
处理0:
sum = 10 * 256 + 0 = 2560
处理3:
sum = 2560 * 256 + 3 = 655363
处理193:
sum = 655363 * 256 + 193 = 167773121
因此,
10.0.3.193
转换为长整数后是167773121
。总结
这个转换过程有效地将点分十进制表示的IPv4地址转换为一个单一的长整数值,这在网络编程和数据存储中非常有用,因为它允许以整数形式存储和操作IP地址。
import java.util.Scanner; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()){ String in = scanner.nextLine(); if(in.contains(".")){ convert2num(in.split("\\.")); }else convert2ip(in); } } public static void convert2num(String[] in){ String str2 = ""; for(int i = 0; i < 4; i++){ String str = in[i]; int num10 = Integer.parseInt(str); StringBuilder num2 = new StringBuilder(); while(num10 != 0){ int temp = num10 % 2; num2.append(temp); num10 /= 2; } while (num2.length() != 8){ num2.append(0); } num2.reverse(); str2 += num2.toString(); } long sum = 0; //这是错误的,而且不知道为什么 // for(int j = str2.length()-1; j >= 0; j --){ // long cur = (long) Character.getNumericValue(str2.charAt(j)) * (int)(Math.pow(2, str2.length()-j-1)) ; // sum += cur; // } // System.out.println(sum); for (int j = 0; j < str2.length(); j++) { if (str2.charAt(j) == '1') { // 使用long类型进行计算,避免int溢出 sum += (long) Math.pow(2, str2.length() - 1 - j); } } System.out.println(sum); } public static void convert2ip(String in){ /** * 输入 10进制型的IP地址 * 输出 0.0.0.0 */ long in10 = Long.parseLong(in); // StringBuilder in2 = new StringBuilder(); // while(in10 != 0){ // int dec = in10 % 2; // in2.append(dec); // in10 /= 2; // } // in2.reverse(); // while (in2.length() != 32){ // in2.append(0); // } String res = ""; for(int n = 0; n < 4;n ++){ long temp = in10 % 256; res = temp + "." + res; in10 = in10 / 256; } System.out.println(res.substring(0, res.length() - 1)); } }
简洁模式-256进制转换
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNextLine()){
String str = in.nextLine();
if(str.contains(".")){
ip2int(str.split("\\."));
}else int2ip(str);
}
}
private static void ip2int(String[] str){
long sum = 0;
for(int i = 0; i < str.length; i++){
sum = sum * 256 + Long.parseLong(str[i]);
}
System.out.println("" + sum);
}
private static void int2ip(String str){
String res = "";
long num = Long.parseLong(str);
for(int i = 0; i < 4; i ++){
long temp = num % 256;
res = temp + "." + res;
num /= 256;
}
System.out.println(res.toString().substring(0,res.length() - 1));
}
}
HJ101 输入整型数组和排序标识,对其元素按照升序或降序进行排序
也可以排序后,直接一个正序输出,一个逆序输出
import java.util.Scanner; import java.util.Arrays; public class Main { public static void main(String[] args){ Scanner scanner = new Scanner(System.in); while(scanner.hasNextInt()){ int n = scanner.nextInt(); int[] nums = new int[n]; for(int i = 0; i < n; i++){ nums[i] = scanner.nextInt(); } int oder = scanner.nextInt(); Arrays.sort(nums); if(oder == 1){ int left = 0, right = nums.length-1; while(left < right){ int temp = nums[left]; nums[left] = nums[right]; nums[right] = temp; left++; right--; } } for(int i = 0; i < nums.length; i ++){ System.out.print(nums[i] + " "); } System.out.println(); } } }
HJ106 字符逆序
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextLine()) { String str = in.nextLine(); String res = ""; for(int i = 0; i < str.length(); i++){ Character c = str.charAt(i); res = c + res; } System.out.println(res); } } }
3.20
华为机试【排序】
HJ8 合并表记录
-
for(Map.Entry<Integer,Integer> entry: map.entrySet()){
-
nums[j] = entry.getKey();
import java.util.Scanner; import java.util.HashMap; import java.util.Map; import java.util.Arrays; /** 输入描述: 先输入键值对的个数n(1 <= n <= 500) 接下来n行每行输入成对的index和value值,以空格隔开 输出描述: 输出合并后的键值对(多行) */ public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextInt()) { // 注意 while 处理多个 case int n = in.nextInt(); HashMap<Integer,Integer> map = new HashMap<>(); for(int i = 0; i < n; i++){ int a = in.nextInt(); int b = in.nextInt(); map.put(a, map.getOrDefault(a,0) + b); } int[] nums = new int[map.size()]; int j = 0; for(Map.Entry<Integer,Integer> entry: map.entrySet()){ nums[j] = entry.getKey(); j++; } Arrays.sort(nums); for(int i = 0; i < nums.length; i++){ int Key = nums[i]; int KeyValue = map.getOrDefault(Key,0); System.out.print(Key + " " + KeyValue + "\n"); } System.out.println(); } } }
若使用列表排序
-
List<Map.Entry<Integer, Integer>> entries = new ArrayList<>(map.entrySet());
-
entries.sort(Map.Entry.comparingByKey());
import java.util.Scanner; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Comparator; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextInt()) { int n = in.nextInt(); Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < n; i++) { int key = in.nextInt(); int value = in.nextInt(); map.merge(key, value, Integer::sum); } // 将map的entrySet转换成list,然后对list进行排序 List<Map.Entry<Integer, Integer>> entries = new ArrayList<>(map.entrySet()); entries.sort(Map.Entry.comparingByKey()); // 遍历排序后的list,打印键值对 for (Map.Entry<Integer, Integer> entry : entries) { System.out.println(entry.getKey() + " " + entry.getValue()); } System.out.println(); // 分隔符,用于区分不同的case } } }
HJ14 字符串排序
手写排序
-
字符串的
compareTo
方法可以比较字符串的所有元素,确保整个字符串按字典序进行排序。这个方法比较两个字符串的字典顺序。如果第一个字符串字典顺序上小于第二个字符串,则返回一个负数;如果两个字符串相等,则返回0;如果第一个字符串字典顺序上大于第二个字符串,则返回一个正数。它会一直比较到找出第一个不相等的字符或者到达字符串的末尾。
package hw; import java.util.Scanner; /** 输入描述: 输入第一行为一个正整数n(1≤n≤1000),下面n行为n个字符串(字符串长度≤100),字符串中只含有大小写字母。 输出描述: 数据输出n行,输出结果为按照字典序排列的字符串。 */ public class HJ14_字符串排序 { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextLine()) { int n = Integer.parseInt(in.nextLine()); String[] strs = new String[n]; for(int i = 0; i < n; i++) { strs[i] = in.nextLine(); } // 手动实现选择排序 for (int i = 0; i < n - 1; i++) { int minIndex = i; for (int j = i + 1; j < n; j++) { if (strs[j].compareTo(strs[minIndex]) < 0) { minIndex = j; // 更新最小值索引 } } // 交换当前索引i和找到的最小值索引minIndex的元素 if (minIndex != i) { String temp = strs[i]; strs[i] = strs[minIndex]; strs[minIndex] = temp; } } // 打印排序后的字符串 for(String s : strs) { System.out.println(s); } } } }
直接排序
import java.util.Scanner; import java.util.Arrays; /** 输入描述: 输入第一行为一个正整数n(1≤n≤1000),下面n行为n个字符串(字符串长度≤100),字符串中只含有大小写字母。 输出描述: 数据输出n行,输出结果为按照字典序排列的字符串。 */ public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextLine()) { // 注意 while 处理多个 case int n = Integer.parseInt(in.nextLine()); String[] strs = new String[n]; for(int i = 0; i < n; i++){ strs[i] = in.nextLine(); } Arrays.sort(strs); for(String s : strs){ System.out.println(s); } } } }
HJ27 查找兄弟单词
-
在Java中,比较字符串(或任何对象)是否相同时使用
equals
方法而不是==
运算符,原因主要涉及到如何在Java中比较对象的值和引用:-
引用比较 (
==
): 使用==
运算符比较两个对象时,Java实际上比较的是这两个对象的内存地址(即引用)。如果两个变量指向内存中的同一个位置,则==
运算符会认为它们相等。对于基本数据类型(如int
、char
等),==
运算符比较的是它们的值,但对于对象数据类型(包括字符串),==
比较的是引用。 -
值比较 (
equals
): 要比较两个对象的内容是否相同,应该使用它们的equals
方法。equals
方法被设计用来比较两个对象的状态(即它们所包含的数据),而不是它们的引用。对于字符串来说,equals
方法会逐个比较字符串中的字符,以确定两个字符串是否包含完全相同的字符序列。
-
-
数组排序:
Arrays.sort(s);
-
列表排序:
Collections.sort(res);
-
复制map要用
Map<Character,Integer> mapCur = new HashMap<>(map);
通过
Map<Character,Integer> mapCur = map;
的方式并不会复制map
,而是创建了一个新的引用mapCur
指向了相同的map
对象。因此,对mapCur
所做的修改将直接影响到原始的map
对象。这种情况下,你可以通过使用HashMap
的构造函数来创建一个新的HashMap
,并将原始map
的内容复制到新的mapCur
中。 -
输入还可以写成
String[] ss = scanner.nextLine().split(" "); Integer a = Integer.parseInt(ss[0]); String x = ss[ss.length-2]; Integer k = Integer.parseInt(ss[ss.length-1]); List<String> list = new ArrayList<>();
-
判断兄弟元素还可以写成
拿出两个字符串,每个字符放进字符串数组排序,比较是否equals
public static boolean isBrother(String x,String y){ if (x.length()!=y.length()||y.equals(x)){ return false; } char[] s = x.toCharArray(); char[] j= y.toCharArray(); Arrays.sort(s); Arrays.sort(j); return new String(s).equals(new String(j)); }
import java.util.*; /** * 定义一个单词的“兄弟单词”为:交换该单词字母顺序(注:可以交换任意次),而不添加、删除、修改原有的字母就能生成的单词。 * 兄弟单词要求和原来的单词不同。例如: ab 和 ba 是兄弟单词。 ab 和 ab 则不是兄弟单词。 * 输入描述: * 输入只有一行。 先输入字典中单词的个数n,再输入n个单词作为字典单词。 然后输入一个单词x 最后后输入一个整数k * 输出描述: * 第一行输出查找到x的兄弟单词的个数m 第二行输出查找到的按照字典顺序排序后的第k个兄弟单词,没有符合第k个的话则不用输出。 * 输入: * 3 abc bca cab abc 1 * 输出: * 2 * bca */ public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextLine()){ int n = in.nextInt(); String[] strs = new String[n]; for(int i = 0; i < n; i++){ strs[i] = in.next(); } String tarStr = in.next(); int k = in.nextInt(); in.nextLine(); Map<Character,Integer> map = new HashMap<>(); for(Character c:tarStr.toCharArray()){ map.put(c,map.getOrDefault(c,0) + 1); } int countBro = 0; List<String> res = new ArrayList<>(); for(int i = 0; i < n; i++){ String curStr = strs[i]; if(curStr.equals(tarStr) || curStr.length() != tarStr.length()) continue; // 忽略相同单词和长度不同的单词 Boolean isBro = true; Map<Character,Integer> mapCur = new HashMap<>(map); for(Character c : curStr.toCharArray()){ mapCur.put(c,mapCur.getOrDefault(c,0) - 1); } for(Map.Entry<Character,Integer> entry: mapCur.entrySet()){ if(entry.getValue() != 0){ isBro = false; } } if(isBro == true){ countBro++; res.add(curStr); } } System.out.println(countBro); Collections.sort(res); if(res.size() >= k){ System.out.println(res.get(k - 1)); // 索引要向前一个 } } } }
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()){ String[] ss = scanner.nextLine().split(" "); Integer a = Integer.parseInt(ss[0]); String x = ss[ss.length-2]; Integer k = Integer.parseInt(ss[ss.length-1]); List<String> list = new ArrayList<>(); for (int i = 1; i <=a ; i++) { if (isBrother(x,ss[i])){ list.add(ss[i]); } } int size = list.size(); System.out.println(size); if (size>=k){ Collections.sort(list); System.out.println(list.get(k-1)); } } } public static boolean isBrother(String x,String y){ if (x.length()!=y.length()||y.equals(x)){ return false; } char[] s = x.toCharArray(); char[] j= y.toCharArray(); Arrays.sort(s); Arrays.sort(j); return new String(s).equals(new String(j)); } }
NC37 合并区间
贪心算法:排序;找到起点,如果这个起点在上一个终点之后,就计入新起点;如果在上一个终点之前,就合并选二者最大的终点
NC37 题解 | #合并区间#_牛客博客 (nowcoder.net)
-
input = input.replaceAll("[\\[\\]\\s]", "");
在正则表达式中,
\s
表示空白字符,包括空格、制表符和换行符等。在这个特定的情况下,[\\[\\]\\s]
表示一个字符类,它匹配左括号[
、右括号]
和任何空白字符。因此,通过replaceAll("[\\[\\]\\s]", "")
,我们可以将输入字符串中的左括号、右括号和所有空白字符都替换为空字符串,从而将其去除。 -
// 按区间起点进行排序 Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
Lambda表达式
(a, b) -> Integer.compare(a[0], b[0])
:-
a
和b
是参与比较的两个区间,它们都是int[]
类型。 -
a[0]
和b[0]
分别是这两个区间的起点。 -
Integer.compare(a[0], b[0])
是调用 Integer 类的 compare 方法,比较两个区间的起点。这个方法返回:-
负数,如果
a[0] < b[0]
(即第一个区间的起点小于第二个区间的起点); -
零,如果
a[0] == b[0]
; -
正数,如果
a[0] > b[0]
。
-
Arrays.sort 方法:
-
Arrays.sort
是Java标准库中的一个方法,用于对数组进行排序。 -
第一个参数是要排序的数组,这里是
intervals
,包含了多个区间,每个区间由一个包含两个元素的int[]
数组表示。 -
第二个参数是一个实现了
Comparator
接口的对象,它定义了数组元素的比较规则。在Java 8及以上版本,可以直接使用Lambda表达式来简化Comparator
的实现。
排序的效果:
通过这个比较器,
Arrays.sort
方法将按照区间起点的升序排列整个区间数组。这意味着,排序后的数组中,位置较前的区间起点值小于或等于位置较后的区间起点值。 -
-
System.out.print("["); StringJoiner joiner = new StringJoiner(","); for (int[] interval : res) { joiner.add(Arrays.toString(interval)); } System.out.print(joiner.toString()); System.out.print("]");
System.out.print("["); String result = res.stream() .map(interval -> Arrays.toString(interval)) .collect(Collectors.joining(",")); System.out.print(result); System.out.print("]");
package hw; import java.util.*; /** * 给出一组区间,请合并所有重叠的区间。 * 请保证合并后的区间按区间起点升序排列。 * [[10,30],[20,60],[80,100],[150,180]] * [[10,60],[80,100],[150,180]] */ public class NC37_合并区间 { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextLine()){ String str = in.nextLine(); // TEST // String str = "[[10,30],[20,60],[80,100],[150,180]]"; str= str.replaceAll("[\\[\\]\\s]",""); String[] strs = str.split(","); int length = strs.length; int[][] nums = new int[length/2][2]; for(int i = 0; i < length/2; i++){ nums[i][0] = Integer.parseInt(strs[i*2]); nums[i][1] = Integer.parseInt(strs[i*2 + 1]); } Arrays.sort(nums, (a, b) -> Integer.compare(a[0],b[0])); List<int[]> res = new ArrayList<>(); int[] pre = nums[0]; res.add(pre); for(int i = 1; i < nums.length; i++){ int[] cur = nums[i]; if(cur[0] <= pre[1]){ pre[1] = Math.max(pre[1], cur[1]); }else { res.add(cur); } } System.out.print("["); for (int i = 0; i < res.size(); i++) { // 打印区间,但不使用Arrays.toString()以避免空格 int[] interval = res.get(i); System.out.print("[" + interval[0] + "," + interval[1] + "]"); if (i < res.size() - 1) { System.out.print(","); } } System.out.println("]"); System.out.println(); } } }
原题不输入的答案
import java.util.*; public class Solution { public ArrayList<Interval> merge(ArrayList<Interval> intervals) { ArrayList<Interval> res = new ArrayList<>(); //去除特殊情况 if(intervals.size() == 0) return res; //重载比较,按照区间首排序 Collections.sort(intervals, new Comparator<Interval>(){ public int compare(Interval o1, Interval o2){ if(o1.start != o2.start) return o1.start - o2.start; else return o1.end - o2.end; } }); //放入第一个区间 res.add(intervals.get(0)); int count = 0; //遍历后续区间,查看是否与末尾有重叠 for(int i = 1; i < intervals.size(); i++){ Interval o1 = intervals.get(i); Interval origin = res.get(count); if(o1.start > origin.end){ res.add(o1); count++; //区间有重叠,更新结尾 }else{ res.remove(count); Interval s = new Interval(origin.start, o1.end); if(o1.end < origin.end) s.end = origin.end; res.add(s); } } return res; } }
HJ68 成绩排序
-
String[] input = in.nextLine().trim().split("\\s+");
-
in.nextLine()
:这个方法是Scanner
类的一个实例方法,它用于读取一行输入。换句话说,它从当前扫描器指向的位置(通常是用户的输入)读取输入,直到遇到一个换行符(\n
),然后将换行符之前的内容(不包含换行符)作为一个字符串返回。 -
trim()
:这是String
类的一个实例方法,它返回调用字符串的副本,忽略前导空白和尾部空白。这意味着如果输入行的开头或结尾有任何空格,这些都将被去除。 -
split("\\s+")
:这里使用的是split
方法,它的参数是一个正则表达式。正则表达式\\s+
的含义是匹配任何数量的空白字符(\\s
匹配单个空白字符,如空格、制表符、换行符等;+
表示一个或多个前面的字符)。因此,这个方法调用会按照一个或多个空白字符来分割字符串。-
注意,在 Java 正则表达式中,反斜杠
\
是一个转义字符,所以你需要用双反斜杠\\
来表示一个字面上的反斜杠。而\s
本身在正则表达式中就表示空白字符,所以在字符串字面量中写作\\s
。
-
因此,整个表达式
in.nextLine().trim().split("\\s+")
的流程是这样的:-
从用户输入中读取一行。
-
去掉这行字符串两端的空白字符。
-
按照一个或多个空白字符将字符串分割为子字符串。
-
将分割后的子字符串存储到
input
数组中。
-
-
Comparator<Student> comparator = (s1, s2) -> order == 0 ? s2.score - s1.score : s1.score - s2.score;
-
students.sort(comparator);
-
static class Student { String name; int score; Student(String name, int score) { this.name = name; this.score = score; } }
-
static class Student
: 这是一个内部静态类的声明。static
关键字意味着Student
类与其外部类的实例无关,因此它不能访问外部类的非静态成员。静态内部类通常作为一个帮助类,只用于它被定义的外部类内部。 -
String name;
: 这个成员变量用于存储一个Student
对象的姓名。 -
int score;
: 这个成员变量用于存储一个Student
对象的成绩。 -
Student(String name, int score)
: 这是Student
类的构造函数,它接受两个参数:学生的姓名和成绩。构造函数用于在创建Student
对象时初始化这些值。 -
this.name = name;
: 这行代码将构造函数参数name
的值赋给Student
对象的name
成员变量。 -
this.score = score;
: 这行代码将构造函数参数score
的值赋给Student
对象的score
成员变量。
-
注意!!! this.name = name;
不能写成 name = this.name;
构造类
package hw;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.*;
/**
* 给定一些同学的信息(名字,成绩)序列,请你将他们的信息按照成绩从高到低或从低到高的排列,相同成绩
* 都按先录入排列在前的规则处理。注:0代表从高到低,1代表从低到高
* 例示:
* 4
* 0
* jack 70
* peter 96
* Tom 70
* smith 67
*
* 从高到低 成绩
* peter 96
* jack 70
* Tom 70
* smith 67
*/
public class HJ68_成绩排序 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while(in.hasNextLine()){
int n = Integer.parseInt(in.nextLine()); // 学生数量
int order = Integer.parseInt(in.nextLine()); // 排序方式:0降序,1升序
List<Student> students = new ArrayList<>();
for (int i = 0; i < n; i++) {
String[] input = in.nextLine().trim().split("\\s+");
String name = input[0];
int score = Integer.parseInt(input[1]);
students.add(new Student(name, score));
}
// 根据成绩和输入顺序排序
Comparator<Student> comparator = (s1, s2) -> order == 0 ? s2.score - s1.score : s1.score - s2.score;
students.sort(comparator);
// 打印排序后的学生信息
for (Student student : students) {
System.out.println(student.name + " " + student.score);
}
}
}
static class Student {
String name;
int score;
Student(String name, int score) {
this.name = name;
this.score = score;
}
}
}
用数组
-
HashMap<Integer,String> map = new HashMap<>(); int[][] score = new int[n][2];//姓名编号,成绩 for(int i=0;i<n;i++){ String[] nameAndScore = sc.nextLine().split("\\s+"); score[i][0] = i; score[i][1] = Integer.parseInt(nameAndScore[1]); map.put(i,nameAndScore[0]); }
-
Arrays.sort(score,(o1,o2) ->{ if(flag==0){ return o2[1] - o1[1];//按第二列降序排列,如果相等的话,返回0,顺序不变 }else{ return o1[1] - o2[1];//按第二列升序 } });
import java.util.*; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args){ Scanner sc = new Scanner(System.in); HashMap<Integer,String> map = new HashMap<>(); while(sc.hasNextLine()){ int n = Integer.parseInt(sc.nextLine()); int flag = Integer.parseInt(sc.nextLine());//1是升序,0是降序 int[][] score = new int[n][2];//姓名编号,成绩 for(int i=0;i<n;i++){ String[] nameAndScore = sc.nextLine().split("\\s+"); score[i][0] = i; score[i][1] = Integer.parseInt(nameAndScore[1]); map.put(i,nameAndScore[0]); } Arrays.sort(score,(o1,o2) ->{ if(flag==0){ return o2[1] - o1[1];//按第二列降序排列,如果相等的话,返回0,顺序不变 }else{ return o1[1] - o2[1];//按第二列升序 } }); for(int i=0;i<n;i++){ System.out.println(map.get(score[i][0]) + " " + score[i][1]); } } } }