相关图文参考链接:代码随想录
常用到StringBuilder类,常用方法有 append、chatAt
操作字符串,本质是操作StringBuilder
数组是存放在连续内存空间上的相同类型数据的集合。
在删除或者增添元素的时候,就难免要移动其他元素的地址
定义数组
// 一维数组
int[] arr = {1, 2, 3};
// 二维数组
int[][] arr = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {9,9,9}};
双指针,fast记录循环次数;slow记录要保留的元素,每记录一个就slow++,fast循环结束,slow就是想要的答案
链表是一种通过指针串联在一起的线性结构,
两部分组成:
一个是数据域data(值)
一个是指针域next(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链接的入口节点称为链表的头结点也就是head。
节点的单个next操作并不会改变链表的结构
删除头节点的时候,只需要按照原来的方式删除节点就可以了(操作上一个节点)
ListNode dummy = new ListNode(0, head);
// 双指针
ListNode pre = dummy; // 上一个节点,pre节点始终代表的是被删除节点的上一个节点
ListNode cur = head; // 当前节点
int lenA = 0;
while (curA != null) { // 求链表A的长度
lenA++;
curA = curA.next;
}
只需要把原链表复制出来到新的参数就行了,然后操作新的参数
public static ListNode method(ListNode head) {
// 不改变原来链表
ListNode cur = head;
// 后续的一切操作都根据cur来
双链表 既可以向前查询也可以向后查询。
就是链表首尾相连。
在单链表的基础上修改,最后一个节点的指针不是null,而是指向了第一个节点head(也可能指向其他节点)
用双指针,slow每次走1步,fast每次走2步,那么fast和slow指针的距离每次多1步,如果有环,fast肯定会突然跑到slow后面,每次多走一步,总有一次会追上slow的
slow1步,fast2步,假设fast指针只在环中转了一圈,2*(x+y) = x+y+z+y得出 x = z(这个结论很关键!)
index1从head开始走,index2从相遇点开始走,直到他们相遇,相遇点就是环形的入口
在内存中不连续,通过指针串联在一起
public class ListNode {
int val; // 值
ListNode next; // 指针
// 三个构造函数
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
// Test
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1, null);
ListNode listNode2 = new ListNode(2, listNode1);
ListNode listNode3 = new ListNode(3, listNode2);
ListNode listNode4 = new ListNode(4, listNode3); //这是head
System.out.println(listNode4);
}
将被删除节点的上一个节点的next指针指向被删除节点的下一个节点就行了,JVM会自动回收这个被删除的节点
白话文:如果删除的节点是N,则把N-1的这个节点的next指针指向N+1的这个节点
但是要删之前需要查询吧,查询的时间复杂度O(n)
把要添加位置的上个节点的next指针的值取出来,存放到自己的next指针里
然后把要添加位置的上个节点的next指针指向自己,然后把自己的next指针指向
白话文:把你的小弟给我,当我的小弟,然后让我来当你的小弟
tips:对于单链表来说。要添加位置的下个节点是不需要任何修改的
要查最后一个节点,需要从第一个节点通过next指针一直一直查下去,直到最后一个节点
一般哈希表都是用来快速判断一个元素是否出现集合里,时间复杂度O(1)
但牺牲了空间换取了时间,我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找
使用hashcode,把其他数据格式转化为数值
hashFunction = hashcode % tableSize
拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间
一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
白话文:如果Hash碰撞了,就向下找一个空位放置,本质还是在一个数组中的(问题来了,查询怎么办?)
数组在某种意义上就是一张哈希表(key是数组下标,value是数组的值)
栈是先进后出,栈没有迭代器iterator
栈其实就是递归的一种是实现结构,常用方法
参考:栈的几个常用方法
队列是先进先出,队列也没有迭代器iterator
如RocketMq消息队列
常用方法:
如果深度为k,有2^k-1个节点的二叉树 (等比数列求和公式)
深度为k,对应的那层的节点数为2^(k-1)
二叉搜索树是有数值的树,是一个有序的树
在二叉搜索树的基础上,多了个条件:左右子树的高度差不能超过1
链式存储则是通过指针把分布在散落在各个地址的节点串联一起,内存不连续
public class Tree {
int val; // 值
Tree left; // 左子树
Tree right; // 右子树
// 构造函数
public Tree() {
}
public Tree(int val, Tree left, Tree right) {
this.val = val;
this.left = left;
this.right = right;
}
}
顺序存储的元素在内存是连续分布的
如果父节点的数组下标是 i,那么
我们把好好的数据变成二叉树的形式,肯定有其中的道理,奥秘所在
根据遍历的侧重点不一样,分为:
先往深走,遇到叶子节点再往回走
下面的前中后,其实指的就是中间节点的遍历顺序,只要大家记住 前中后序指的就是中间节点的位置就可以了。
可以借助栈使用非递归的方式来实现的
一层一层的去遍历
一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
我就用左闭右闭这种方法好了√
主要思路:将数组分一半,定义左闭区间left,右闭区间right,中间下标middle,根据某种条件(视情况而定),判断答案在哪个区间,如果在左边,则把right = middle -1;如果在右边,则left = middle + 1;
二分法的难点就在于如何确定这个某种条件
双指针法(快慢指针法): 通过一个快指针fast和慢指针slow
在一个for循环下完成两个for循环的工作,降低时间复杂度
白话文:把2个for循环变成一个for循环
递归算法的时间复杂度:递归的次数 * 每次递归中的操作次数。
不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
三个问题确定好就行
例如:
左右窗口的起始点都是数组的第一位,这时候窗口的大小是0
关键字:连续
一般用int数组,其中key是数组的下标,value是数组中元素的值
主要方法:
hashSet.contains(Object o);
HashSet没有get方法(当然没有了。数组可以通过下标获取,你Set能通过什么获取哇?),遍历取Set的值的时候用增强for循环,坑的
int index = 0;
for (Integer i : resSet) {
res[index++] = i;
}
当需要存放两个元素的时候用到
hashMap.containsKey(Object key);
关键字:穷举、for循环横向遍历、递归纵向遍历、叶子节点是答案
当for循环写不出代码的时候,可以考虑回溯
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
通过局部最优,达到全局最优,如果找不到反例,那么就可以用贪心算法!
能找到,局部最优,没有反例
确定dp数组以及下标的含义:
递推公式:
dp数组初始化值:
确定遍历顺序:
举例推导dp数组
某一问题有很多重叠子问题
每一个状态一定是由上一个状态推导出来的
这道题目我举例推导状态转移公式了么?
我打印dp数组的日志了么?
打印出来了dp数组和我想的一样么?
数组模型:dp
常规操作:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String s = scanner.nextLine();
List<String> strings = Arrays.asList(s.split(","));
System.out.println(strings);
}
}
将输入信息标记为XXX类型,常用nextLine、nextInt、nextBigdecimal
scanner.nextLine()方法,读取输入,包括空格和除回车以外的所有符号
如scanner.hasNextLine()方法,判断当前是否有输入,当键盘有输入后返回true,否则会一直等待键盘输入
参考链接:Java Scanner类的常用方法及用法(很详细)
public char[] toCharArray(){}
将字符串转化为char数组
public char charAt(int index){}
读取String中的第i个char字符
public int compareTo(String anotherString) {}
若s1 < s2,返回-1,小于0
若s1 = s2,返回 0,等于0
若s1 > s2,返回 1,大于0
例子:
a.compareTo(A) = 32
A.compareTo(a) = -32
0.compareTo(A) = 17
0对应48
A对应65
a对应97
ASCII表:链接
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextLine()) {
String s = scanner.nextLine();
char[] chars = s.toCharArray();
for (char aChar : chars) {
int i = aChar - '0';
System.out.println(i);
}
}
while (n > 0) {
int i = n % 10; // 取个位数
System.out.print(i);
n = n / 10; // 如把原来的十位数放到个位数
}
public synchronized StringBuffer reverse() {}
将里面的字符串翻过来,如123变成321
例如
StringBuffer stringBuffer = new StringBuffer(str).reverse();
default void sort(Comparator<? super E> c) {}
默认是升序的
list.sort((s1, s2) ->
// 这里返回的数据 若小于0的在前,所以是升序
s1.compareTo(s2)
// 下面是降序的操作 相当于强行转了
//s1.compareTo(s2) < 0 ? 1 : -1
);
这个结果默认是升序的
默认升序
如按照绝对值大小排序
IntStream.of(nums)
// 装箱
.boxed()
.sorted((o1,o2)->Math.abs(o1) - Math.abs(o2))
// 拆箱
.mapToInt(Integer::intValue)
.toArray();
public static int max(int a, int b) {}
取两个int中最大的,除了int 还有float、long、double类型
public static int max(int a, int b) {}
取两个int中最小的,除了int 还有float、long、double类型
public static int abs(int a) {}
取int值的绝对值,除了int 还有float、long、double类型
Math.pow(2, 3) = 8
判断集合中是否还有下一个元素
指针指向下一个元素,并返回这个元素
while (iterator.hasNext()) {
Object o = iterator.next();
System.out.println(o);
}
TreeMap自身实现了排序,根据AscII表升序排列(只根据第一个字母,然后再第二个字母排序。。)
public static void main(String[] args) {
TreeMap<String, String> tmp = new TreeMap<String, String>();
// 随便put操作
tmp.put("a", "aaa");
tmp.put("c", "ccc");
tmp.put("A", "AAA");
tmp.put("B", "BBB");
tmp.put("12", "111");
tmp.put("11", "111");
tmp.put("3", "333");
tmp.put("2", "222");
tmp.put("4", "444");
// 遍历操作
Iterator<String> iterator = tmp.keySet().iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key +":"+ tmp.get(key));
}
}
控制台
Connected to the target VM, address: '127.0.0.1:50642', transport: 'socket'
11:111
12:111
2:222
3:333
4:444
A:AAA
B:BBB
a:aaa
c:ccc
TreeSet自身也实现了排序,根据AscII表升序排列,且会根据字段进行去重
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("1");
treeSet.add("3");
treeSet.add("5");
treeSet.add("a");
treeSet.add("a");
treeSet.add("A");
treeSet.add("b");
Iterator<String> iterator = treeSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
控制台
Connected to the target VM, address: '127.0.0.1:54116', transport: 'socket'
1
3
5
A
a
b