1.常用输入
Scanner sc = new Scanner(System.in);
//单行输入
// int n=sc.nextInt();//键盘实际输入是“数字+回车”,nextInt读出了“数字”,并留下了“回车”,
// // 接着nextLine读到了一个“回车”,这是字符串的结束判定符,所以读到的字符串是空字符串
// sc.nextLine();//去除回车
// String s = sc.nextLine();
//多行输入
// while (sc.hasNext()){
// String s1 = sc.nextLine();
// }
// //多行输入的数组接收(已知行数、列数)
// int n=sc.nextInt();
// sc.nextLine();
// int[][] a=new int[n][3]; //已知列数为3
// for (int i=0;i<n;i++){
// String s0 = sc.nextLine();
// String s = s0.substring(1, s0.length() - 1); //带符号输入字符串的切割:先取子串
// String[] c=s.split(" ");
//
// for (int j=0;j<c.length;j++){
// a[i][j]=Integer.parseInt(c[j]);
// }
// }
// //常见自定义输出
// for (int i=0;i<n;i++){
// System.out.print("{");
// System.out.print(a[i][j]+" ");
// }
// System.out.println(a[i][2]+"}");
// }
//多行输入的数组接收(未知行数、已知列数,但有结束提示)
ArrayList<String> arr = new ArrayList<>();
while (sc.hasNext()){
String s = sc.nextLine();
String[] s1 = s.split(" ");
if (Integer.parseInt(s1[0])==0&&Integer.parseInt(s1[1])==0){ //if(结束条件)
break;
}
arr.add(s);
}
int[][] a = new int[arr.size()][2];
for (int i=0;i<arr.size();i++){
String s = arr.get(i);
String[] s1 = s.split(" ");
for (int j = 0; j<s1.length;j++) {
a[i][j] = Integer.parseInt(s1[j]);
System.out.println(a[i][j]);
}
}
2.HashMap两种排序方法
Map phone=new HashMap();
phone.put("Apple",7299);
phone.put("SAMSUNG",6000);
phone.put("Meizu",2698);
phone.put("Xiaomi",2400);
// 1.按key排序(从小到大)
对名称进行排序,首先要得到HashMap中键的集合(keySet),并转换为数组,这样才能用Arrays.sort()进行排序
Set set=phone.keySet();
Object[] arr=set.toArray();
Arrays.sort(arr);
for(Object key:arr){
System.out.println(key+": "+phone.get(key));
}
// 2.按value排序(从小到大、从大到小)
对value进行排序,首先需要得到HashMap中的包含映射关系的视图(entrySet),
如图:
[Meizu=2698;Xiaomi=2400;SAMSUNG=6000;Apple=7299]
// 将entrySet转换为List,然后重写比较器比较即可.这里可以使用List.sort(comparator),也可以使用Collections.sort(list,comparator)转换为list
// 2.1 HashMap转List进行排序
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(phone.entrySet()); //转换为list
//使用list.sort()排序
list.sort(new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o2.getValue().compareTo(o1.getValue()); //从大到小
// return o1.getValue().compareTo(o2.getValue()); //从小到大
}
});
//使用Collections.sort()排序
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o2.getValue().compareTo(o1.getValue()); //从大到小
// return o1.getValue().compareTo(o2.getValue()); //从小到大
}
});
//for循环输出
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getKey() + " " + list.get(i).getValue());
}
student(id,score)按value排序
//输入学生人数,ID 和成绩,按成绩从大到小进行排序,成绩相同则按 ID 从小到大排序
//例如:
//输入
3
1 98
2 94
3 100
//输出
3 100
1 98
2 94
import java.util.*;
public class yuanjingAI {
public static void main(String[] args) {
HashMap hm = new HashMap();
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); //键盘实际输入是“数字+回车”,nextInt读出了“数字”,并留下了“回车”,
// 接着netxLine读到了一个“回车”,这是字符串的结束判定符啊,所以读到的字符串 就是空字符串“”。
sc.nextLine(); //使用sc.nextLine()吃掉多余的回车
// 多行输入
for (int i = 0;i < n;i++){
String str = sc.nextLine();
String str1[] = str.split(" ");
hm.put(str1[0],str1[1]);
}
// System.out.println(hm.values());
//HashMap转List进行排序
List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(hm.entrySet()); //转换为list
//使用list.sort()排序
list.sort(new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
//for循环输出
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getKey() + " " + list.get(i).getValue());
}
}
}
标题十万个数据,找出重复次数最多的十个数据并打印
public class NumberRepetMost {
public static void main(String[] args) {
final int NUM = 100000;//十万个
int t = 0;
Random random = new Random( );
HashMap<Integer,Integer> hashMap = new HashMap<Integer, Integer>(NUM);//key:数据 value:出现次数
//模拟十万个数据
while (t < NUM){
Integer i = random.nextInt(1000);
if(hashMap.containsKey( i )){ //如果存在,出现次数+1,不存在出现次数为1
hashMap.put( i,hashMap.get( i ) +1);
}else{
hashMap.put( i,1 );
}
t++;
}
//用来存储最终的十个结果,优先级队列 小根堆形式,最小的在堆顶.
PriorityQueue<Map.Entry<Integer,Integer>> priorityQueue =
new PriorityQueue<Map.Entry<Integer,Integer>>( 10, new Comparator<Map.Entry<Integer,Integer>>() {
@Override
public int compare(Map.Entry<Integer,Integer> o1, Map.Entry<Integer,Integer> o2) {
return o1.getValue()-o2.getValue();
}//o1-o2 可以这样理解,如果返回正数,说明o1-o2>0.返回负数说明o1-o2<0
} );
//通过hashmap的entrySet进行遍历.
Iterator<Map.Entry<Integer,Integer>> iterator = hashMap.entrySet().iterator();
//先随便放10个,priorityQueue会自动排序
for (int i = 0; i < 10; i++) {
priorityQueue.add( iterator.next());
}
//再放其他的
while (iterator.hasNext()){
Map.Entry<Integer,Integer> curEntry= iterator.next(); //当前遍历的.
Map.Entry<Integer,Integer> topEntry = priorityQueue.peek(); //获得队列中最小的那一个.
if(curEntry.getValue() > topEntry.getValue()){
priorityQueue.remove( topEntry );
priorityQueue.add( curEntry );
}
}
//打印结果
Iterator<Map.Entry<Integer,Integer>> iterator2 = priorityQueue.iterator();
while (iterator2.hasNext()){
Map.Entry<Integer,Integer> entry = iterator2.next();
System.out.println(entry.getKey()+":出现了"+entry.getValue()+"次");
}
}
3.根据二叉树前序和中序获得后续遍历
3.1重建二叉树
3.2获得后续遍历
3.3二叉树遍历口诀
前序遍历:根——左——右
中序遍历:左——根——右
后续遍历:左——右——根
链接:https://www.nowcoder.com/questionTerminal/8a19cbe657394eeaac2f6ea9b0f6fcf6?answerType=1&f=discussion
来源:牛客网
1. 分析
根据中序遍历和前序遍历可以确定二叉树,具体过程为:
1.根据前序序列第一个结点确定根结点
2.根据根结点在中序序列中的位置分割出左右两个子序列
3.对左子树和右子树分别递归使用同样的方法继续分解
例如:
前序序列{1,2,4,7,3,5,6,8} = pre
中序序列{4,7,2,1,5,3,8,6} = in
根据当前前序序列的第一个结点确定根结点,为 1
找到 1 在中序遍历序列中的位置,为 in[3]
切割左右子树,则 in[3] 前面的为左子树, in[3] 后面的为右子树
则切割后的左子树前序序列为:{2,4,7},切割后的左子树中序序列为:{4,7,2};切割后的右子树前序序列为:{3,5,6,8},切割后的右子树中序序列为:{5,3,8,6}
对子树分别使用同样的方法分解。
2.代码
import java.util.Arrays;
public class BinaryTree {
public static void main(String[] args) {
int[] pre = {1, 2, 3, 4, 5, 6, 7};
int[] in = {2, 3, 1, 5, 6, 4, 7};
postOrder(reConstructBinaryTree(pre,in));
}
//二叉树定义
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
//根据重建后的root获得后续遍历结果
private static void postOrder(TreeNode localRoot) {
if (localRoot != null) {
postOrder(localRoot.left);
postOrder(localRoot.right);
System.out.print(localRoot.val + " ");
}
}
//根据前序和中序遍历获得重建二叉树
public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if (pre.length == 0 || in.length == 0) {
return null;
}
TreeNode root = new TreeNode(pre[0]);
// 在中序中找到前序的根
for (int i = 0; i < in.length; i++) {
if (in[i] == pre[0]) {
// 左子树,注意 copyOfRange 函数,左闭右开
root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
// 右子树,注意 copyOfRange 函数,左闭右开
root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(in, i + 1, in.length));
break;
}
}
return root;
}
}
3.4二叉树的深度
// 实现二叉树的深度方式有两种,递归以及非递归。
// ①递归实现:(掌握)
为了求树的深度,可以先求其左子树的深度和右子树的深度,可以用递归实现,递归的出口就是节点为空。返回值为0;
// ②非递归实现:
利用层次遍历的算法,设置变量level记录当前节点所在的层数,设置变量last指向当前层的最后一个节点,当处理完当前层的最后一个节点,让level指向+1操作。设置变量cur记录当前层已经访问的节点的个数,当cur等于last时,表示该层访问结束。
层次遍历在求树的宽度、输出某一层节点,某一层节点个数,每一层节点个数都可以采取类似的算法。
树的宽度:在树的深度算法基础上,加一个记录访问过的层节点个数最多的变量max,在访问每层前max与last比较,如果max比较大,max不变,如果max小于last,把last赋值给max;
// 代码实现:
import java.util.LinkedList;
public class Deep{
//递归实现1
public int findDeep(BiTree root){
int deep = 0;
if(root != null)
{
int lchilddeep = findDeep(root.left);
int rchilddeep = findDeep(root.right);
deep = lchilddeep > rchilddeep ? lchilddeep + 1 : rchilddeep + 1;
}
return deep;
}
//递归实现2
public int findDeep1(BiTree root){
if(root == null){
return 0;
}
else{
int lchilddeep = findDeep1(root.left);//求左子树的深度
int rchilddeep = findDeep1(root.left);//求右子树的深度
return lchilddeep > rchilddeep ? lchilddeep + 1 : rchilddeep + 1;//左子树和右子树深度较大的那个加一等于整个树的深度
}
}
//非递归实现
public int findDeep2(BiTree root){
if(root == null)
return 0;
BiTree current = null;
LinkedList<BiTree> queue = new LinkedList<BiTree>();
queue.offer(root);
int cur,last;
int level = 0;
while(!queue.isEmpty())
{
cur = 0;//记录本层已经遍历的节点个数
last = queue.size();//当遍历完当前层以后,队列里元素全是下一层的元素,队列的长度是这一层的节点的个数
while(cur < last)//当还没有遍历到本层最后一个节点时循环
{
current = queue.poll();//出队一个元素
cur++;
//把当前节点的左右节点入队(如果存在的话)
if(current.left != null)
{
queue.offer(current.left);
}
if(current.right != null)
{
queue.offer(current.right);
}
}
level++;//每遍历完一层level+1
}
return level;
}
public static void main(String[] args)
{
BiTree root = BiTree.buildTree();
Deep deep = new Deep();
System.out.println(deep.findDeep(root));
System.out.println(deep.findDeep1(root));
System.out.println(deep.findDeep2(root));
}
}
3.5二叉树任意两个节点之间路径的最大长度
int maxDist(Tree
root) {
//如果树是空的,则返回0
if(root == NULL)
return 0;
if(root->left != NULL) {
root->lm = maxDist(root->left) +1;
}
if(root->right != NULL)
root->rm = maxDist(root->right) +1;
//如果以该节点为根的子树中有最大的距离,那就更新最大距离
int sum = root->rm + root->lm;
if(sum > max) {
max = sum;
}
return root->rm > root->lm ?root->rm : root->lm;
}
4.用两个栈实现队列
链接:https://www.nowcoder.com/questionTerminal/54275ddae22f475981afa2244dd448c6?answerType=1&f=discussion
来源:牛客网
1. 分析
队列的特性是:“先入先出”,栈的特性是:“先入后出”
当我们向模拟的队列插入数 a,b,c 时,假设插入的是 stack1,此时的栈情况为:
栈 stack1:{a,b,c}
栈 stack2:{}
当需要弹出一个数,根据队列的"先进先出"原则,a 先进入,则 a 应该先弹出。但是此时 a 在 stack1 的最下面,将 stack1 中全部元素逐个弹出压入 stack2,现在可以正确的从 stack2 中弹出 a,此时的栈情况为:
栈 stack1:{}
栈 stack2:{c,b}
继续弹出一个数,b 比 c 先进入"队列",b 弹出,注意此时 b 在 stack2 的栈顶,可直接弹出,此时的栈情况为:
栈 stack1:{}
栈 stack2:{c}
此时向模拟队列插入一个数 d,还是插入 stack1,此时的栈情况为:
栈 stack1:{d}
栈 stack2:{c}
弹出一个数,c 比 d 先进入,c 弹出,注意此时 c 在 stack2 的栈顶,可直接弹出,此时的栈情况为:
栈 stack1:{d}
栈 stack2:{}
根据上述栗子可得出结论:
当插入时,直接插入 stack1
当弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将 stack1 中的全部数逐个出栈入栈 stack2,再弹出 stack2 栈顶元素
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.size()<=0){
while(stack1.size()!=0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
5.青蛙跳台阶——矩形覆盖问题(递归/动态规划)
链接:https://www.nowcoder.com/questionTerminal/8c82a5b80378478f9484d87d1c5f12a4?answerType=1&f=discussion
来源:牛客网
// 1.一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
// 2.我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?比如n=3时,2*3的矩形块有3种覆盖方法:
本质上还是斐波那契数列,所以迭代也可以求
当成 dp 问题来想的话:首先分析问题,它最终解是由前面的解累积起来的解,如何缩小问题的规模?
首先可知,第一阶有只能一步,一种;,第二阶可以两次一步、一次两步两种
若楼梯阶级 n = 3
跳 2 步到 3:剩下的是第一步没跳,起始跳到第一步只有一种
跳 1 步到 3:剩下的是第二步没跳,起始跳到第二步有两种
通过分类讨论,问题规模就减少了:
若楼梯阶级 n = n
跳 2 步到 n:剩下的是第 n - 2 步没跳,起始跳到第 n - 2 步设它为 pre2 种
跳 1 步到 n:剩下的是第 n - 1 步没跳,起始跳到第 n - 1 步设它为 pre1 种
同时可以发现第 n 阶的解法,只要用到 n - 1 和 n - 2 阶是多少,其他的不用考虑,因此用两个变量临时存下来即可
dp(i) = dp(i-2) + dp(i-1)
// 解法1:递归
public class Solution {
public int JumpFloor(int target) {
if(target<=2) return target;
return JumpFloor(target-1)+JumpFloor(target-2);
}
}
// 解法2:动态规划
public class Solution {
public int JumpFloor(int target) {
if(target<=2) return target;
int pre1=2;
int pre2=1;
for(int i =3;i<=target;i++){
int cur = pre1+pre2;
pre2=pre1;
pre1=cur;
}
return pre1;
}
}
变态青蛙跳
//一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
题目分析:
解法一: 动态规划。斐波那契数列的变种。由于青蛙每次可以跳n级台阶,因此青蛙跳上n级台阶的跳法为f(n)=f(n-1)+f(n-2)+……f(1)+f(0)。经过分析,发现结果呈规律性,f(n)以2的n次幂形式增长,f(0)=1,f(1)=1,f(2)=2…从而可以归纳得到计算表达式:f(n)=2^(n-1)。
public class Solution {
public int JumpFloorII(int target) {
return target==0?1:(int)Math.pow(2,target-1);
}
}
6.二进制中1的个数
链接:https://www.nowcoder.com/questionTerminal/8ee967e43c2c4ec193b040ea7fbb10b8?answerType=1&f=discussion
来源:牛客网
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
(搬运评论区大佬的解释)
如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
JAVA解:
public class Solution {
public int NumberOf1(int n) {
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
}
7.调整数组使奇数在偶数之前
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
public class Solution {
public void reOrderArray(int [] array) {
int p=0;
int m=0;
int[] array1=new int[array.length];
for(int i=0;i<array.length;i++){
if(array[i]%2!=0){
p++; //查出奇数个数
}
}
for(int i=0;i<array.length;i++){
if(array[i]%2!=0){
array1[m]=array[i];
m++;
}
if(array[i]%2==0){
array1[p]=array[i];
p++;
}
}
for(int i=0;i<array.length;i++){
array[i]=array1[i];
}
}
}
8.链表中倒数第K个结点
//输入一个链表,输出该链表中倒数第k个结点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
ListNode fast=head;
ListNode slow=head;
int i=0; // i 必须为全局变量
for(;fast!=null;i++){
if(i>=k){
slow=slow.next;
}
fast=fast.next;
}
return i < k ? null:slow;
}
}
从尾到头打印链表
//输入一个链表,按链表从尾到头的顺序返回一个ArrayList
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
ArrayList<Integer> list = new ArrayList();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if (listNode != null) {
printListFromTailToHead(listNode.next);
list.add(listNode.val);
}
return list;
}
}
反转链表,输出新表头
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
//初始化pre指针,用于记录当前结点的前一个结点地址
ListNode pre = null;
//初始化p指针,用于记录当前结点的下一个结点地址
ListNode p = null;
//head指向null时,循环终止。
while(head != null){
//先用p指针记录当前结点的下一个结点地址。
p = head.next;
//让被当前结点与链表断开并指向前一个结点pre。
head.next = pre;
//pre指针指向当前结点
pre = head;
//head指向p(保存着原链表中head的下一个结点地址)
head = p;
}
return pre;//当循环结束时,pre所指的就是反转链表的头结点
}
}
9.排序算法比较
[外链图片转存中…(img-GeZ431BF-1586338963636)]